static Image getIconFromIcnsFile (const File& icnsFile, const int size)
{
    FileInputStream stream (icnsFile);

    if (! stream.openedOk())
        return {};

    const int numHeaderSectionBytes = 4;
    char headerSection [numHeaderSectionBytes];

    if (stream.read (headerSection, numHeaderSectionBytes) != numHeaderSectionBytes
          || headerSection[0] != 'i'
          || headerSection[1] != 'c'
          || headerSection[2] != 'n'
          || headerSection[3] != 's')
        return {};

    if (stream.read (headerSection, numHeaderSectionBytes) != numHeaderSectionBytes)
        return {};

    const auto dataSize = juce::ByteOrder::bigEndianInt (headerSection);

    if (dataSize <= 0)
        return {};

    OwnedArray<juce::ImageFileFormat> internalFormats;
    internalFormats.add (new  PNGImageFormat());
    internalFormats.add (new JPEGImageFormat());

    Array<Image> images;
    auto maxWidth = 0;
    auto maxWidthIndex = -1;

    while (stream.getPosition() < dataSize)
    {
        const auto sectionStart = stream.getPosition();

        if (! stream.setPosition (sectionStart + 4))
            break;

        if (stream.read (headerSection, numHeaderSectionBytes) != numHeaderSectionBytes)
            break;

        const auto sectionSize = ByteOrder::bigEndianInt (headerSection);

        if (sectionSize <= 0)
            break;

        const auto sectionDataStart = stream.getPosition();

        for (auto* fmt : internalFormats)
        {
            if (fmt->canUnderstand (stream))
            {
                stream.setPosition (sectionDataStart);

                images.add (fmt->decodeImage (stream));

                const auto lastImageIndex = images.size() - 1;
                const auto lastWidth = images.getReference (lastImageIndex).getWidth();

                if (lastWidth > maxWidth)
                {
                    maxWidthIndex = lastImageIndex;
                    maxWidth = lastWidth;
                }
            }

            stream.setPosition (sectionDataStart);
        }

        stream.setPosition (sectionStart + sectionSize);
    }

    return maxWidthIndex == -1 ? juce::Image()
                               : images.getReference (maxWidthIndex).rescaled (size, size, Graphics::ResamplingQuality::highResamplingQuality);
}
//==============================================================================
void AudioFileConverter::run()
{
	
	while ( getQueueSize() > 0 )
	{	
		{   // lock jobQueue before retrieving a task
			const ScopedLock lock (queueLock);
			task  = jobQueue[0];
		}

		/* try opening the file */
		File	inputDataFile( task->getFileName() );
		String  inputFileName( inputDataFile.getFullPathName() );

		if ( !inputDataFile.existsAsFile() || (inputDataFile.getSize() == 0) )
		{
			dbgOut(L"** AudioFileConverter ** Invalid or corrupted temporary file:\t" + inputFileName);
			removeFromQueue();
			continue;
		}
	
		/* try creating the input stream */
		FileInputStream*	fileInputStream	=	inputDataFile.createInputStream();
		if (fileInputStream == NULL)
		{
			dbgOut(L"** AudioFileConverter ** Unable to create input stream for file:\t" + inputFileName);
			removeFromQueue();
			continue;
		}
		
		dbgOut(L"");
		dbgOut(L" ***  AudioFileConverter ***");
		dbgOut(L"** AudioFileConverter ** Converting file:\t" + inputFileName 
               + L" (" + String( inputDataFile.getSize() ) + L" b)");

		int		processorOutputs = task->getChannelNumber();
		const int bytesPerSample = processorOutputs * sizeof(float);
		int		bufferSize		= task->getBufferSize();
		double	samplingRate	= task->getSamplingRate();
		int		bitDepth		= task->getBitDepth();
		String	audioFormatName = task->getFormat();

		AudioSampleBuffer tempBuffer(1, bufferSize);

		// declare classes needed to save the format
		OwnedArray<AudioFormat>			someAudioFormats;
		OwnedArray<AudioFormatWriter>	audioFormatWriters;
		OwnedArray<File>				audioFiles;
		Array<FileOutputStream*>		outStreams;
		String							audioFileName;

		AudioFormatWriter*	tmpWriter;
		FileOutputStream*	tmpStream;
		File*				tmpAudioFile;

		String outputDir = inputDataFile.getParentDirectory().getFullPathName();
	
		for (int i=0; i < processorOutputs ; i++)
		{
			// Delete temporary files
			File tmpDataFile(outputDir + File::separatorString + L"channel" + String::formatted("%.2d", i ) + ".dat");

			if ( tmpDataFile != File::nonexistent)
			{
				dbgOut( L"** AudioFileConverter ** \tDeleting temporary file:\t" + tmpDataFile.getFullPathName() );
				tmpDataFile.deleteFile();
			}
			else
			{
				dbgOut( "** AudioFileConverter ** Unable to delete temporary file:\t\t" + tmpDataFile.getFullPathName() );
			}

		
			// Define the format (wav is default)
			if (audioFormatName == "wav")
				someAudioFormats.add( new WavAudioFormat() );			
		
			else if (audioFormatName == "aiff")
				someAudioFormats.add( new AiffAudioFormat() );
		
			else if (audioFormatName == "flac")
				someAudioFormats.add( new FlacAudioFormat() );

//			else if (audioFormatName == "ogg")
//				someAudioFormats.add( new OggVorbisAudioFormat() );

			else
				someAudioFormats.add( new WavAudioFormat() );	
		
			audioFileName = outputDir + File::separatorString + "channel" + String::formatted("%.2d",i) + someAudioFormats[i]->getFileExtensions()[0];
		
			tmpAudioFile = new File (audioFileName);
			if (*tmpAudioFile == File::nonexistent)
			{
				dbgOut( L"** AudioFileConverter ** Unable to create file:\t" + audioFileName );
				audioFormatWriters.clear(true);
				someAudioFormats.clear(true);
				audioFiles.clear(true);
				outStreams.clear();

				delete fileInputStream;
				
				removeFromQueue();

				continue;
			}
		
			audioFiles.add( tmpAudioFile );

			// Delete existing files
			if (audioFiles[i]->existsAsFile())
			{
				dbgOut( "** AudioFileConverter ** \tDeleting existing audio file:\t\t" + audioFileName );			
				if	(!audioFiles[i]->deleteFile())
				{
					dbgOut( L"** AudioFileConverter ** Unable to delete existing file:\t" + audioFileName );
					audioFormatWriters.clear(true);
					someAudioFormats.clear(true);
					audioFiles.clear(true);
					outStreams.clear();
					delete fileInputStream;

					removeFromQueue();

					continue;
				}
			}

			dbgOut( "** AudioFileConverter ** \tSaving audio file:\t\t" + audioFileName );

			/* Create output stream for this file */
			tmpStream = audioFiles[i]->createOutputStream();
			if (tmpStream == NULL)
				{
					dbgOut( L"** AudioFileConverter ** Unable to create output stream for file:\t" + audioFileName );
					delete tmpAudioFile;
					audioFormatWriters.clear(true);
					someAudioFormats.clear(true);
					audioFiles.clear(true);
					outStreams.clear();
					delete fileInputStream;

					removeFromQueue();

					continue;
				}

			outStreams.add( tmpStream );
		

			/* Create Audio Format Writer */
			tmpWriter = someAudioFormats[i]->createWriterFor(	outStreams[i],		// streamToWriteTo,
																			samplingRate,		// sampleRateToUse,  
																			1,					// numberOfChannels,  
																			someAudioFormats[i]->getPossibleBitDepths().getLast(),	// bitsPerSample - Get the maximum possible bit depth for this format
																			NULL,				//  metadataValues,  
																			0 );


			if (tmpWriter == NULL)
			{
					dbgOut( L"** AudioFileConverter ** Unable to create audio format writer for:\t" + audioFileName );
					delete tmpAudioFile;
					audioFormatWriters.clear(true);
					someAudioFormats.clear(true);
					audioFiles.clear(true);
					outStreams.clear();
					delete fileInputStream;

					removeFromQueue();
		
					continue;
			}
			audioFormatWriters.add( tmpWriter );
		}

		// Write data to wav file
		int dataBlockSize = processorOutputs * bufferSize * bitDepth/8 ;
		MemoryBlock*	buffer = new MemoryBlock( dataBlockSize, true);
	
		int64 bytesSaved = inputDataFile.getSize();

		while ( !fileInputStream->isExhausted() && (fileInputStream->getPosition() <  bytesSaved) )
		{
			float* x = (float *) buffer->getData() ;

			int bytesRead = fileInputStream->read( (void *)x, dataBlockSize );
			int numSamples = (int)( bytesRead / bytesPerSample );

			for (int ch=0; ch < processorOutputs; ch++)
			{
//				const int numBytes = (int) (bytesRead/processorOutputs);

				tempBuffer.copyFrom(	0,					//  const int   	 destChannel,
										0,					//	const int  	destStartSample,
										x+ch*numSamples,	//	const float *  	source,
										numSamples			//	int  	numSamples	 
									);

				audioFormatWriters[ch]->write(	(const int**)(tempBuffer.getArrayOfChannels()),	//AudioFormatWriter *  writer,  
												numSamples				//const int  numSamples   
											  );
			}
		}

		// clean up
		delete	buffer;

		//	this should delete 'owned' objects 
		audioFormatWriters.clear(true);
		someAudioFormats.clear(true);
		audioFiles.clear(true);
		// clear the outStreams without deleting objects (already deleted)
		outStreams.clear();
	
		// Delete and close the stream
		delete fileInputStream;	

		// Delete the data.dat file
		dbgOut( L"** AudioFileConverter ** \tDeleting temporary file:\t" + inputFileName );
		inputDataFile.deleteFile();

		// Delete the task
		removeFromQueue();		
		
		dbgOut( "** AudioFileConverter ** Files saved." );

	}

	dbgOut( "** AudioFileConverter ** Thread terminates." );

}