| @@ -36,7 +36,9 @@ | |||||
| */ | */ | ||||
| #include "JucePluginCharacteristics.h" | #include "JucePluginCharacteristics.h" | ||||
| #define JUCE_SUPPORT_CARBON 1 | |||||
| #if ! defined (__LP64__) | |||||
| #define JUCE_SUPPORT_CARBON 1 | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| // The following stuff is just to cause a compile error if you've forgotten to | // The following stuff is just to cause a compile error if you've forgotten to | ||||
| @@ -27,7 +27,8 @@ | |||||
| //[MiscUserDefs] You can add your own user definitions and misc code here... | //[MiscUserDefs] You can add your own user definitions and misc code here... | ||||
| class DemoThumbnailComp : public Component, | class DemoThumbnailComp : public Component, | ||||
| public ChangeListener | |||||
| public ChangeListener, | |||||
| public FileDragAndDropTarget | |||||
| { | { | ||||
| public: | public: | ||||
| DemoThumbnailComp() | DemoThumbnailComp() | ||||
| @@ -104,6 +105,19 @@ public: | |||||
| repaint(); | repaint(); | ||||
| } | } | ||||
| bool isInterestedInFileDrag (const StringArray& files) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void filesDropped (const StringArray& files, int x, int y) | |||||
| { | |||||
| AudioDemoPlaybackPage* demoPage = findParentComponentOfClass ((AudioDemoPlaybackPage*) 0); | |||||
| if (demoPage != 0) | |||||
| demoPage->showFile (File (files[0])); | |||||
| } | |||||
| AudioFormatManager formatManager; | AudioFormatManager formatManager; | ||||
| AudioThumbnailCache thumbnailCache; | AudioThumbnailCache thumbnailCache; | ||||
| AudioThumbnail thumbnail; | AudioThumbnail thumbnail; | ||||
| @@ -266,6 +280,14 @@ void AudioDemoPlaybackPage::sliderValueChanged (Slider* sliderThatWasMoved) | |||||
| //[MiscUserCode] You can add your own definitions of your custom methods or any other code here... | //[MiscUserCode] You can add your own definitions of your custom methods or any other code here... | ||||
| void AudioDemoPlaybackPage::showFile (const File& file) | |||||
| { | |||||
| loadFileIntoTransport (file); | |||||
| zoomSlider->setValue (0, false, false); | |||||
| thumbnail->setFile (file); | |||||
| } | |||||
| void AudioDemoPlaybackPage::loadFileIntoTransport (const File& audioFile) | void AudioDemoPlaybackPage::loadFileIntoTransport (const File& audioFile) | ||||
| { | { | ||||
| // unload the previous file source and delete it.. | // unload the previous file source and delete it.. | ||||
| @@ -292,10 +314,7 @@ void AudioDemoPlaybackPage::loadFileIntoTransport (const File& audioFile) | |||||
| void AudioDemoPlaybackPage::selectionChanged() | void AudioDemoPlaybackPage::selectionChanged() | ||||
| { | { | ||||
| loadFileIntoTransport (fileTreeComp->getSelectedFile()); | |||||
| zoomSlider->setValue (0, false, false); | |||||
| thumbnail->setFile (fileTreeComp->getSelectedFile()); | |||||
| showFile (fileTreeComp->getSelectedFile()); | |||||
| } | } | ||||
| void AudioDemoPlaybackPage::fileClicked (const File&, const MouseEvent&) | void AudioDemoPlaybackPage::fileClicked (const File&, const MouseEvent&) | ||||
| @@ -54,6 +54,7 @@ public: | |||||
| void selectionChanged(); | void selectionChanged(); | ||||
| void fileClicked (const File& file, const MouseEvent& e); | void fileClicked (const File& file, const MouseEvent& e); | ||||
| void fileDoubleClicked (const File& file); | void fileDoubleClicked (const File& file); | ||||
| void showFile (const File& file); | |||||
| //[/UserMethods] | //[/UserMethods] | ||||
| void paint (Graphics& g); | void paint (Graphics& g); | ||||
| @@ -147,7 +147,7 @@ | |||||
| reduce code size. | reduce code size. | ||||
| */ | */ | ||||
| #if (! defined (JUCE_USE_CDBURNER)) && ! (JUCE_WINDOWS && ! JUCE_MSVC) | #if (! defined (JUCE_USE_CDBURNER)) && ! (JUCE_WINDOWS && ! JUCE_MSVC) | ||||
| #define JUCE_USE_CDBURNER 0 | |||||
| #define JUCE_USE_CDBURNER 1 | |||||
| #endif | #endif | ||||
| /** JUCE_USE_CDREADER: Enables the audio CD reader code (Mac and Windows only). | /** JUCE_USE_CDREADER: Enables the audio CD reader code (Mac and Windows only). | ||||
| @@ -155,7 +155,7 @@ | |||||
| reduce code size. | reduce code size. | ||||
| */ | */ | ||||
| #ifndef JUCE_USE_CDREADER | #ifndef JUCE_USE_CDREADER | ||||
| #define JUCE_USE_CDREADER 0 | |||||
| #define JUCE_USE_CDREADER 1 | |||||
| #endif | #endif | ||||
| //============================================================================= | //============================================================================= | ||||
| @@ -244,7 +244,7 @@ | |||||
| Carbon isn't required for a normal app, but may be needed by specialised classes like | Carbon isn't required for a normal app, but may be needed by specialised classes like | ||||
| plugin-hosts, which support older APIs. | plugin-hosts, which support older APIs. | ||||
| */ | */ | ||||
| #ifndef JUCE_SUPPORT_CARBON | |||||
| #if ! (defined (JUCE_SUPPORT_CARBON) || defined (__LP64__)) | |||||
| #define JUCE_SUPPORT_CARBON 1 | #define JUCE_SUPPORT_CARBON 1 | ||||
| #endif | #endif | ||||
| @@ -32,6 +32,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "../../core/juce_PlatformUtilities.h" | #include "../../core/juce_PlatformUtilities.h" | ||||
| #include "../../text/juce_LocalisedStrings.h" | #include "../../text/juce_LocalisedStrings.h" | ||||
| //============================================================================== | //============================================================================== | ||||
| static const char* const aiffFormatName = "AIFF file"; | static const char* const aiffFormatName = "AIFF file"; | ||||
| static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; | static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; | ||||
| @@ -72,14 +73,14 @@ public: | |||||
| hasGotVer = true; | hasGotVer = true; | ||||
| const int ver = input->readIntBigEndian(); | const int ver = input->readIntBigEndian(); | ||||
| if (ver != 0 && ver != (int)0xa2805140) | |||||
| if (ver != 0 && ver != (int) 0xa2805140) | |||||
| break; | break; | ||||
| } | } | ||||
| else if (type == chunkName ("COMM")) | else if (type == chunkName ("COMM")) | ||||
| { | { | ||||
| hasGotType = true; | hasGotType = true; | ||||
| numChannels = (unsigned int)input->readShortBigEndian(); | |||||
| numChannels = (unsigned int) input->readShortBigEndian(); | |||||
| lengthInSamples = input->readIntBigEndian(); | lengthInSamples = input->readIntBigEndian(); | ||||
| bitsPerSample = input->readShortBigEndian(); | bitsPerSample = input->readShortBigEndian(); | ||||
| bytesPerFrame = (numChannels * bitsPerSample) >> 3; | bytesPerFrame = (numChannels * bitsPerSample) >> 3; | ||||
| @@ -167,318 +168,42 @@ public: | |||||
| input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | ||||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||||
| char tempBuffer [tempBufSize]; | |||||
| while (numSamples > 0) | while (numSamples > 0) | ||||
| { | { | ||||
| int* left = destSamples[0]; | |||||
| if (left != 0) | |||||
| left += startOffsetInDestBuffer; | |||||
| int* right = numDestChannels > 1 ? destSamples[1] : 0; | |||||
| if (right != 0) | |||||
| right += startOffsetInDestBuffer; | |||||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||||
| char tempBuffer [tempBufSize]; | |||||
| const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); | const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); | ||||
| const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); | const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); | ||||
| if (bytesRead < numThisTime * bytesPerFrame) | if (bytesRead < numThisTime * bytesPerFrame) | ||||
| zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); | |||||
| if (bitsPerSample == 16) | |||||
| { | { | ||||
| if (littleEndian) | |||||
| { | |||||
| const short* src = reinterpret_cast <const short*> (tempBuffer); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *right++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| *right++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| const char* src = tempBuffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *right++ = ByteOrder::bigEndianShort (src) << 16; | |||||
| src += 4; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| src += 2; | |||||
| *left++ = ByteOrder::bigEndianShort (src) << 16; | |||||
| src += 2; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::bigEndianShort (src) << 16; | |||||
| src += 2; | |||||
| *right++ = ByteOrder::bigEndianShort (src) << 16; | |||||
| src += 2; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::bigEndianShort (src) << 16; | |||||
| src += 2; | |||||
| } | |||||
| } | |||||
| } | |||||
| jassert (bytesRead >= 0); | |||||
| zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); | |||||
| } | } | ||||
| else if (bitsPerSample == 24) | |||||
| { | |||||
| const char* src = tempBuffer; | |||||
| if (littleEndian) | |||||
| { | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *right++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 6; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| src += 3; | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| *right++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *right++ = ByteOrder::bigEndian24Bit (src) << 8; | |||||
| src += 6; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| src += 3; | |||||
| *left++ = ByteOrder::bigEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::bigEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| *right++ = ByteOrder::bigEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::bigEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 32) | |||||
| { | |||||
| const unsigned int* src = reinterpret_cast <const unsigned int*> (tempBuffer); | |||||
| unsigned int* l = reinterpret_cast <unsigned int*> (left); | |||||
| unsigned int* r = reinterpret_cast <unsigned int*> (right); | |||||
| jassert (! usesFloatingPointData); // (would need to add support for this if it's possible) | |||||
| if (littleEndian) | |||||
| if (littleEndian) | |||||
| { | |||||
| switch (bitsPerSample) | |||||
| { | { | ||||
| if (numChannels > 1) | |||||
| { | |||||
| if (l == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *r++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| else if (r == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| *r++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| default: jassertfalse; break; | |||||
| } | } | ||||
| else | |||||
| { | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (l == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *r++ = ByteOrder::swapIfLittleEndian (*src++); | |||||
| } | |||||
| } | |||||
| else if (r == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfLittleEndian (*src++); | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfLittleEndian (*src++); | |||||
| *r++ = ByteOrder::swapIfLittleEndian (*src++); | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfLittleEndian (*src++); | |||||
| } | |||||
| } | |||||
| } | |||||
| left = reinterpret_cast <int*> (l); | |||||
| right = reinterpret_cast <int*> (r); | |||||
| } | } | ||||
| else if (bitsPerSample == 8) | |||||
| else | |||||
| { | { | ||||
| const char* src = tempBuffer; | |||||
| if (numChannels > 1) | |||||
| switch (bitsPerSample) | |||||
| { | { | ||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *right++ = ((int) *src++) << 24; | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *left++ = ((int) *src++) << 24; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ((int) *src++) << 24; | |||||
| *right++ = ((int) *src++) << 24; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ((int) *src++) << 24; | |||||
| } | |||||
| case 8: ReadHelper<AudioData::Int32, AudioData::Int8, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 32: ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| default: jassertfalse; break; | |||||
| } | } | ||||
| } | } | ||||
| @@ -486,14 +211,6 @@ public: | |||||
| numSamples -= numThisTime; | numSamples -= numThisTime; | ||||
| } | } | ||||
| if (numSamples > 0) | |||||
| { | |||||
| for (int i = numDestChannels; --i >= 0;) | |||||
| if (destSamples[i] != 0) | |||||
| zeromem (destSamples[i] + startOffsetInDestBuffer, | |||||
| sizeof (int) * numSamples); | |||||
| } | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -509,6 +226,68 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class AiffAudioFormatWriter : public AudioFormatWriter | class AiffAudioFormatWriter : public AudioFormatWriter | ||||
| { | { | ||||
| public: | |||||
| //============================================================================== | |||||
| AiffAudioFormatWriter (OutputStream* out, double sampleRate_, unsigned int numChans, int bits) | |||||
| : AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits), | |||||
| lengthInSamples (0), | |||||
| bytesWritten (0), | |||||
| writeFailed (false) | |||||
| { | |||||
| headerPosition = out->getPosition(); | |||||
| writeHeader(); | |||||
| } | |||||
| ~AiffAudioFormatWriter() | |||||
| { | |||||
| if ((bytesWritten & 1) != 0) | |||||
| output->writeByte (0); | |||||
| writeHeader(); | |||||
| } | |||||
| //============================================================================== | |||||
| bool write (const int** data, int numSamples) | |||||
| { | |||||
| jassert (data != 0 && *data != 0); // the input must contain at least one channel! | |||||
| if (writeFailed) | |||||
| return false; | |||||
| const int bytes = numChannels * numSamples * bitsPerSample / 8; | |||||
| tempBlock.ensureSize (bytes, false); | |||||
| switch (bitsPerSample) | |||||
| { | |||||
| case 8: WriteHelper<AudioData::Int8, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| if (bytesWritten + bytes >= (uint32) 0xfff00000 | |||||
| || ! output->write (tempBlock.getData(), bytes)) | |||||
| { | |||||
| // failed to write to disk, so let's try writing the header. | |||||
| // If it's just run out of disk space, then if it does manage | |||||
| // to write the header, we'll still have a useable file.. | |||||
| writeHeader(); | |||||
| writeFailed = true; | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| bytesWritten += bytes; | |||||
| lengthInSamples += numSamples; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| juce_UseDebuggingNewOperator | |||||
| private: | |||||
| MemoryBlock tempBlock; | MemoryBlock tempBlock; | ||||
| uint32 lengthInSamples, bytesWritten; | uint32 lengthInSamples, bytesWritten; | ||||
| int64 headerPosition; | int64 headerPosition; | ||||
| @@ -592,153 +371,6 @@ class AiffAudioFormatWriter : public AudioFormatWriter | |||||
| jassert (output->getPosition() == headerLen); | jassert (output->getPosition() == headerLen); | ||||
| } | } | ||||
| public: | |||||
| //============================================================================== | |||||
| AiffAudioFormatWriter (OutputStream* out, | |||||
| const double sampleRate_, | |||||
| const unsigned int chans, | |||||
| const int bits) | |||||
| : AudioFormatWriter (out, | |||||
| TRANS (aiffFormatName), | |||||
| sampleRate_, | |||||
| chans, | |||||
| bits), | |||||
| lengthInSamples (0), | |||||
| bytesWritten (0), | |||||
| writeFailed (false) | |||||
| { | |||||
| headerPosition = out->getPosition(); | |||||
| writeHeader(); | |||||
| } | |||||
| ~AiffAudioFormatWriter() | |||||
| { | |||||
| if ((bytesWritten & 1) != 0) | |||||
| output->writeByte (0); | |||||
| writeHeader(); | |||||
| } | |||||
| //============================================================================== | |||||
| bool write (const int** data, int numSamples) | |||||
| { | |||||
| if (writeFailed) | |||||
| return false; | |||||
| const int bytes = numChannels * numSamples * bitsPerSample / 8; | |||||
| tempBlock.ensureSize (bytes, false); | |||||
| char* buffer = static_cast <char*> (tempBlock.getData()); | |||||
| const int* left = data[0]; | |||||
| const int* right = data[1]; | |||||
| if (right == 0) | |||||
| right = left; | |||||
| if (bitsPerSample == 16) | |||||
| { | |||||
| short* b = reinterpret_cast <short*> (buffer); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (short) ByteOrder::swapIfLittleEndian ((uint16) (*left++ >> 16)); | |||||
| *b++ = (short) ByteOrder::swapIfLittleEndian ((uint16) (*right++ >> 16)); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (short) ByteOrder::swapIfLittleEndian ((uint16) (*left++ >> 16)); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 24) | |||||
| { | |||||
| char* b = buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| ByteOrder::bigEndian24BitToChars (*left++ >> 8, b); | |||||
| b += 3; | |||||
| ByteOrder::bigEndian24BitToChars (*right++ >> 8, b); | |||||
| b += 3; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| ByteOrder::bigEndian24BitToChars (*left++ >> 8, b); | |||||
| b += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 32) | |||||
| { | |||||
| uint32* b = reinterpret_cast <uint32*> (buffer); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = ByteOrder::swapIfLittleEndian ((uint32) *left++); | |||||
| *b++ = ByteOrder::swapIfLittleEndian ((uint32) *right++); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = ByteOrder::swapIfLittleEndian ((uint32) *left++); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 8) | |||||
| { | |||||
| char* b = buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (char) (*left++ >> 24); | |||||
| *b++ = (char) (*right++ >> 24); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (char) (*left++ >> 24); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (bytesWritten + bytes >= (uint32) 0xfff00000 | |||||
| || ! output->write (buffer, bytes)) | |||||
| { | |||||
| // failed to write to disk, so let's try writing the header. | |||||
| // If it's just run out of disk space, then if it does manage | |||||
| // to write the header, we'll still have a useable file.. | |||||
| writeHeader(); | |||||
| writeFailed = true; | |||||
| return false; | |||||
| } | |||||
| else | |||||
| { | |||||
| bytesWritten += bytes; | |||||
| lengthInSamples += numSamples; | |||||
| return true; | |||||
| } | |||||
| } | |||||
| juce_UseDebuggingNewOperator | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -763,15 +395,8 @@ const Array <int> AiffAudioFormat::getPossibleBitDepths() | |||||
| return Array <int> (depths); | return Array <int> (depths); | ||||
| } | } | ||||
| bool AiffAudioFormat::canDoStereo() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| bool AiffAudioFormat::canDoMono() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| bool AiffAudioFormat::canDoStereo() { return true; } | |||||
| bool AiffAudioFormat::canDoMono() { return true; } | |||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| bool AiffAudioFormat::canHandleFile (const File& f) | bool AiffAudioFormat::canHandleFile (const File& f) | ||||
| @@ -785,8 +410,7 @@ bool AiffAudioFormat::canHandleFile (const File& f) | |||||
| } | } | ||||
| #endif | #endif | ||||
| AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, | |||||
| const bool deleteStreamIfOpeningFails) | |||||
| AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) | |||||
| { | { | ||||
| ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream)); | ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream)); | ||||
| @@ -801,18 +425,13 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, | |||||
| AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, | AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, | ||||
| double sampleRate, | double sampleRate, | ||||
| unsigned int chans, | |||||
| unsigned int numberOfChannels, | |||||
| int bitsPerSample, | int bitsPerSample, | ||||
| const StringPairArray& /*metadataValues*/, | const StringPairArray& /*metadataValues*/, | ||||
| int /*qualityOptionIndex*/) | int /*qualityOptionIndex*/) | ||||
| { | { | ||||
| if (getPossibleBitDepths().contains (bitsPerSample)) | if (getPossibleBitDepths().contains (bitsPerSample)) | ||||
| { | |||||
| return new AiffAudioFormatWriter (out, | |||||
| sampleRate, | |||||
| chans, | |||||
| bitsPerSample); | |||||
| } | |||||
| return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -28,6 +28,7 @@ | |||||
| #include "../../io/streams/juce_InputStream.h" | #include "../../io/streams/juce_InputStream.h" | ||||
| #include "../../text/juce_StringPairArray.h" | #include "../../text/juce_StringPairArray.h" | ||||
| #include "../dsp/juce_AudioDataConverters.h" | |||||
| class AudioFormat; | class AudioFormat; | ||||
| @@ -218,6 +219,32 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| protected: | |||||
| /** Used by AudioFormatReader subclasses to copy data to different formats. */ | |||||
| template <class DestSampleType, class SourceSampleType, class SourceEndianness> | |||||
| struct ReadHelper | |||||
| { | |||||
| typedef AudioData::Pointer <DestSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType; | |||||
| typedef AudioData::Pointer <SourceSampleType, SourceEndianness, AudioData::Interleaved, AudioData::Const> SourceType; | |||||
| static void read (int** destData, int destOffset, int numDestChannels, const void* sourceData, int numSourceChannels, int numSamples) throw() | |||||
| { | |||||
| for (int i = 0; i < numDestChannels; ++i) | |||||
| { | |||||
| if (destData[i] != 0) | |||||
| { | |||||
| DestType dest (destData[i]); | |||||
| dest += destOffset; | |||||
| if (i < numSourceChannels) | |||||
| dest.convertSamples (SourceType (addBytesToPointer (sourceData, i * SourceType::getBytesPerSample()), numSourceChannels), numSamples); | |||||
| else | |||||
| dest.clearSamples (numSamples); | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| private: | private: | ||||
| String formatName; | String formatName; | ||||
| @@ -160,6 +160,26 @@ protected: | |||||
| /** The output stream for Use by subclasses. */ | /** The output stream for Use by subclasses. */ | ||||
| OutputStream* output; | OutputStream* output; | ||||
| /** Used by AudioFormatWriter subclasses to copy data to different formats. */ | |||||
| template <class DestSampleType, class SourceSampleType, class DestEndianness> | |||||
| struct WriteHelper | |||||
| { | |||||
| typedef AudioData::Pointer <DestSampleType, DestEndianness, AudioData::Interleaved, AudioData::NonConst> DestType; | |||||
| typedef AudioData::Pointer <SourceSampleType, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceType; | |||||
| static void write (void* destData, int numDestChannels, const int** source, int numSamples) throw() | |||||
| { | |||||
| for (int i = 0; i < numDestChannels; ++i) | |||||
| { | |||||
| const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels); | |||||
| dest.convertSamples (SourceType (*source), numSamples); | |||||
| if (source[1] != 0) | |||||
| ++source; | |||||
| } | |||||
| } | |||||
| }; | |||||
| private: | private: | ||||
| String formatName; | String formatName; | ||||
| @@ -38,7 +38,6 @@ BEGIN_JUCE_NAMESPACE | |||||
| static const char* const wavFormatName = "WAV file"; | static const char* const wavFormatName = "WAV file"; | ||||
| static const char* const wavExtensions[] = { ".wav", ".bwf", 0 }; | static const char* const wavExtensions[] = { ".wav", ".bwf", 0 }; | ||||
| //============================================================================== | //============================================================================== | ||||
| const char* const WavAudioFormat::bwavDescription = "bwav description"; | const char* const WavAudioFormat::bwavDescription = "bwav description"; | ||||
| const char* const WavAudioFormat::bwavOriginator = "bwav originator"; | const char* const WavAudioFormat::bwavOriginator = "bwav originator"; | ||||
| @@ -258,17 +257,7 @@ struct ExtensibleWavSubFormat | |||||
| //============================================================================== | //============================================================================== | ||||
| class WavAudioFormatReader : public AudioFormatReader | class WavAudioFormatReader : public AudioFormatReader | ||||
| { | { | ||||
| int bytesPerFrame; | |||||
| int64 dataChunkStart, dataLength; | |||||
| static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } | |||||
| WavAudioFormatReader (const WavAudioFormatReader&); | |||||
| WavAudioFormatReader& operator= (const WavAudioFormatReader&); | |||||
| public: | public: | ||||
| int64 bwavChunkStart, bwavSize; | |||||
| //============================================================================== | //============================================================================== | ||||
| WavAudioFormatReader (InputStream* const in) | WavAudioFormatReader (InputStream* const in) | ||||
| : AudioFormatReader (in, TRANS (wavFormatName)), | : AudioFormatReader (in, TRANS (wavFormatName)), | ||||
| @@ -389,6 +378,7 @@ public: | |||||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | ||||
| int64 startSampleInFile, int numSamples) | int64 startSampleInFile, int numSamples) | ||||
| { | { | ||||
| jassert (destSamples != 0); | |||||
| const int64 samplesAvailable = lengthInSamples - startSampleInFile; | const int64 samplesAvailable = lengthInSamples - startSampleInFile; | ||||
| if (samplesAvailable < numSamples) | if (samplesAvailable < numSamples) | ||||
| @@ -405,267 +395,55 @@ public: | |||||
| input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | ||||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||||
| char tempBuffer [tempBufSize]; | |||||
| while (numSamples > 0) | while (numSamples > 0) | ||||
| { | { | ||||
| int* left = destSamples[0]; | |||||
| if (left != 0) | |||||
| left += startOffsetInDestBuffer; | |||||
| int* right = numDestChannels > 1 ? destSamples[1] : 0; | |||||
| if (right != 0) | |||||
| right += startOffsetInDestBuffer; | |||||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||||
| char tempBuffer [tempBufSize]; | |||||
| const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); | const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); | ||||
| const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); | const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); | ||||
| if (bytesRead < numThisTime * bytesPerFrame) | if (bytesRead < numThisTime * bytesPerFrame) | ||||
| zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); | |||||
| if (bitsPerSample == 16) | |||||
| { | { | ||||
| const short* src = reinterpret_cast <const short*> (tempBuffer); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *right++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| *right++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = (int) ByteOrder::swapIfBigEndian ((unsigned short) *src++) << 16; | |||||
| } | |||||
| } | |||||
| jassert (bytesRead >= 0); | |||||
| zeromem (tempBuffer + bytesRead, numThisTime * bytesPerFrame - bytesRead); | |||||
| } | } | ||||
| else if (bitsPerSample == 24) | |||||
| { | |||||
| const char* src = tempBuffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| src += 3; | |||||
| *right++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 6; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = 0; i < numThisTime; ++i) | |||||
| { | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| *right++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = 0; i < numThisTime; ++i) | |||||
| { | |||||
| *left++ = ByteOrder::littleEndian24Bit (src) << 8; | |||||
| src += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 32) | |||||
| switch (bitsPerSample) | |||||
| { | { | ||||
| const unsigned int* src = (const unsigned int*) tempBuffer; | |||||
| unsigned int* l = reinterpret_cast<unsigned int*> (left); | |||||
| unsigned int* r = reinterpret_cast<unsigned int*> (right); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (l == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *r++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| else if (r == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| *r++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *l++ = ByteOrder::swapIfBigEndian (*src++); | |||||
| } | |||||
| } | |||||
| left = reinterpret_cast<int*> (l); | |||||
| right = reinterpret_cast<int*> (r); | |||||
| } | |||||
| else if (bitsPerSample == 8) | |||||
| { | |||||
| const unsigned char* src = reinterpret_cast<const unsigned char*> (tempBuffer); | |||||
| if (numChannels > 1) | |||||
| { | |||||
| if (left == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| ++src; | |||||
| *right++ = ((int) *src++ - 128) << 24; | |||||
| } | |||||
| } | |||||
| else if (right == 0) | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ((int) *src++ - 128) << 24; | |||||
| ++src; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ((int) *src++ - 128) << 24; | |||||
| *right++ = ((int) *src++ - 128) << 24; | |||||
| } | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numThisTime; --i >= 0;) | |||||
| { | |||||
| *left++ = ((int)*src++ - 128) << 24; | |||||
| } | |||||
| } | |||||
| case 8: ReadHelper<AudioData::Int32, AudioData::UInt8, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 16: ReadHelper<AudioData::Int32, AudioData::Int16, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 24: ReadHelper<AudioData::Int32, AudioData::Int24, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); | |||||
| else ReadHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; | |||||
| default: jassertfalse; break; | |||||
| } | } | ||||
| startOffsetInDestBuffer += numThisTime; | startOffsetInDestBuffer += numThisTime; | ||||
| numSamples -= numThisTime; | numSamples -= numThisTime; | ||||
| } | } | ||||
| if (numSamples > 0) | |||||
| { | |||||
| for (int i = numDestChannels; --i >= 0;) | |||||
| if (destSamples[i] != 0) | |||||
| zeromem (destSamples[i] + startOffsetInDestBuffer, | |||||
| sizeof (int) * numSamples); | |||||
| } | |||||
| return true; | return true; | ||||
| } | } | ||||
| int64 bwavChunkStart, bwavSize; | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| }; | |||||
| //============================================================================== | |||||
| class WavAudioFormatWriter : public AudioFormatWriter | |||||
| { | |||||
| MemoryBlock tempBlock, bwavChunk, smplChunk; | |||||
| uint32 lengthInSamples, bytesWritten; | |||||
| int64 headerPosition; | |||||
| bool writeFailed; | |||||
| private: | |||||
| ScopedPointer<AudioData::Converter> converter; | |||||
| int bytesPerFrame; | |||||
| int64 dataChunkStart, dataLength; | |||||
| static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } | static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } | ||||
| WavAudioFormatWriter (const WavAudioFormatWriter&); | |||||
| WavAudioFormatWriter& operator= (const WavAudioFormatWriter&); | |||||
| void writeHeader() | |||||
| { | |||||
| const bool seekedOk = output->setPosition (headerPosition); | |||||
| (void) seekedOk; | |||||
| // if this fails, you've given it an output stream that can't seek! It needs | |||||
| // to be able to seek back to write the header | |||||
| jassert (seekedOk); | |||||
| const int bytesPerFrame = numChannels * bitsPerSample / 8; | |||||
| output->writeInt (chunkName ("RIFF")); | |||||
| output->writeInt ((int) (lengthInSamples * bytesPerFrame | |||||
| + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36))); | |||||
| output->writeInt (chunkName ("WAVE")); | |||||
| output->writeInt (chunkName ("fmt ")); | |||||
| output->writeInt (16); | |||||
| output->writeShort ((bitsPerSample < 32) ? (short) 1 /*WAVE_FORMAT_PCM*/ | |||||
| : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); | |||||
| output->writeShort ((short) numChannels); | |||||
| output->writeInt ((int) sampleRate); | |||||
| output->writeInt (bytesPerFrame * (int) sampleRate); | |||||
| output->writeShort ((short) bytesPerFrame); | |||||
| output->writeShort ((short) bitsPerSample); | |||||
| if (bwavChunk.getSize() > 0) | |||||
| { | |||||
| output->writeInt (chunkName ("bext")); | |||||
| output->writeInt ((int) bwavChunk.getSize()); | |||||
| output->write (bwavChunk.getData(), (int) bwavChunk.getSize()); | |||||
| } | |||||
| if (smplChunk.getSize() > 0) | |||||
| { | |||||
| output->writeInt (chunkName ("smpl")); | |||||
| output->writeInt ((int) smplChunk.getSize()); | |||||
| output->write (smplChunk.getData(), (int) smplChunk.getSize()); | |||||
| } | |||||
| output->writeInt (chunkName ("data")); | |||||
| output->writeInt (lengthInSamples * bytesPerFrame); | |||||
| usesFloatingPointData = (bitsPerSample == 32); | |||||
| } | |||||
| WavAudioFormatReader (const WavAudioFormatReader&); | |||||
| WavAudioFormatReader& operator= (const WavAudioFormatReader&); | |||||
| }; | |||||
| //============================================================================== | |||||
| class WavAudioFormatWriter : public AudioFormatWriter | |||||
| { | |||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| WavAudioFormatWriter (OutputStream* const out, | WavAudioFormatWriter (OutputStream* const out, | ||||
| @@ -700,104 +478,25 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| bool write (const int** data, int numSamples) | bool write (const int** data, int numSamples) | ||||
| { | { | ||||
| jassert (data != 0 && *data != 0); // the input must contain at least one channel! | |||||
| if (writeFailed) | if (writeFailed) | ||||
| return false; | return false; | ||||
| const int bytes = numChannels * numSamples * bitsPerSample / 8; | const int bytes = numChannels * numSamples * bitsPerSample / 8; | ||||
| tempBlock.ensureSize (bytes, false); | tempBlock.ensureSize (bytes, false); | ||||
| char* buffer = static_cast <char*> (tempBlock.getData()); | |||||
| const int* left = data[0]; | |||||
| const int* right = data[1]; | |||||
| if (right == 0) | |||||
| right = left; | |||||
| if (bitsPerSample == 16) | |||||
| { | |||||
| short* b = (short*) buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (short) ByteOrder::swapIfBigEndian ((unsigned short) (*left++ >> 16)); | |||||
| *b++ = (short) ByteOrder::swapIfBigEndian ((unsigned short) (*right++ >> 16)); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (short) ByteOrder::swapIfBigEndian ((unsigned short) (*left++ >> 16)); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 24) | |||||
| { | |||||
| char* b = buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| ByteOrder::littleEndian24BitToChars ((*left++) >> 8, b); | |||||
| b += 3; | |||||
| ByteOrder::littleEndian24BitToChars ((*right++) >> 8, b); | |||||
| b += 3; | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| ByteOrder::littleEndian24BitToChars ((*left++) >> 8, b); | |||||
| b += 3; | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 32) | |||||
| switch (bitsPerSample) | |||||
| { | { | ||||
| unsigned int* b = (unsigned int*) buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = ByteOrder::swapIfBigEndian ((unsigned int) *left++); | |||||
| *b++ = ByteOrder::swapIfBigEndian ((unsigned int) *right++); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = ByteOrder::swapIfBigEndian ((unsigned int) *left++); | |||||
| } | |||||
| } | |||||
| } | |||||
| else if (bitsPerSample == 8) | |||||
| { | |||||
| unsigned char* b = (unsigned char*) buffer; | |||||
| if (numChannels > 1) | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (unsigned char) (128 + (*left++ >> 24)); | |||||
| *b++ = (unsigned char) (128 + (*right++ >> 24)); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| for (int i = numSamples; --i >= 0;) | |||||
| { | |||||
| *b++ = (unsigned char) (128 + (*left++ >> 24)); | |||||
| } | |||||
| } | |||||
| case 8: WriteHelper<AudioData::UInt8, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::LittleEndian>::write (tempBlock.getData(), numChannels, data, numSamples); break; | |||||
| default: jassertfalse; break; | |||||
| } | } | ||||
| if (bytesWritten + bytes >= (uint32) 0xfff00000 | if (bytesWritten + bytes >= (uint32) 0xfff00000 | ||||
| || ! output->write (buffer, bytes)) | |||||
| || ! output->write (tempBlock.getData(), bytes)) | |||||
| { | { | ||||
| // failed to write to disk, so let's try writing the header. | // failed to write to disk, so let's try writing the header. | ||||
| // If it's just run out of disk space, then if it does manage | // If it's just run out of disk space, then if it does manage | ||||
| @@ -816,6 +515,63 @@ public: | |||||
| } | } | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | |||||
| ScopedPointer<AudioData::Converter> converter; | |||||
| MemoryBlock tempBlock, bwavChunk, smplChunk; | |||||
| uint32 lengthInSamples, bytesWritten; | |||||
| int64 headerPosition; | |||||
| bool writeFailed; | |||||
| static inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } | |||||
| void writeHeader() | |||||
| { | |||||
| const bool seekedOk = output->setPosition (headerPosition); | |||||
| (void) seekedOk; | |||||
| // if this fails, you've given it an output stream that can't seek! It needs | |||||
| // to be able to seek back to write the header | |||||
| jassert (seekedOk); | |||||
| const int bytesPerFrame = numChannels * bitsPerSample / 8; | |||||
| output->writeInt (chunkName ("RIFF")); | |||||
| output->writeInt ((int) (lengthInSamples * bytesPerFrame | |||||
| + ((bwavChunk.getSize() > 0) ? (44 + bwavChunk.getSize()) : 36))); | |||||
| output->writeInt (chunkName ("WAVE")); | |||||
| output->writeInt (chunkName ("fmt ")); | |||||
| output->writeInt (16); | |||||
| output->writeShort ((bitsPerSample < 32) ? (short) 1 /*WAVE_FORMAT_PCM*/ | |||||
| : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); | |||||
| output->writeShort ((short) numChannels); | |||||
| output->writeInt ((int) sampleRate); | |||||
| output->writeInt (bytesPerFrame * (int) sampleRate); | |||||
| output->writeShort ((short) bytesPerFrame); | |||||
| output->writeShort ((short) bitsPerSample); | |||||
| if (bwavChunk.getSize() > 0) | |||||
| { | |||||
| output->writeInt (chunkName ("bext")); | |||||
| output->writeInt ((int) bwavChunk.getSize()); | |||||
| output->write (bwavChunk.getData(), (int) bwavChunk.getSize()); | |||||
| } | |||||
| if (smplChunk.getSize() > 0) | |||||
| { | |||||
| output->writeInt (chunkName ("smpl")); | |||||
| output->writeInt ((int) smplChunk.getSize()); | |||||
| output->write (smplChunk.getData(), (int) smplChunk.getSize()); | |||||
| } | |||||
| output->writeInt (chunkName ("data")); | |||||
| output->writeInt (lengthInSamples * bytesPerFrame); | |||||
| usesFloatingPointData = (bitsPerSample == 32); | |||||
| } | |||||
| WavAudioFormatWriter (const WavAudioFormatWriter&); | |||||
| WavAudioFormatWriter& operator= (const WavAudioFormatWriter&); | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -840,15 +596,8 @@ const Array <int> WavAudioFormat::getPossibleBitDepths() | |||||
| return Array <int> (depths); | return Array <int> (depths); | ||||
| } | } | ||||
| bool WavAudioFormat::canDoStereo() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| bool WavAudioFormat::canDoMono() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| bool WavAudioFormat::canDoStereo() { return true; } | |||||
| bool WavAudioFormat::canDoMono() { return true; } | |||||
| AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, | AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, | ||||
| const bool deleteStreamIfOpeningFails) | const bool deleteStreamIfOpeningFails) | ||||
| @@ -531,7 +531,7 @@ void AudioDataConverters::deinterleaveSamples (const float* const source, | |||||
| } | } | ||||
| } | } | ||||
| /* | |||||
| #if JUCE_UNIT_TESTS | #if JUCE_UNIT_TESTS | ||||
| #include "../../utilities/juce_UnitTest.h" | #include "../../utilities/juce_UnitTest.h" | ||||
| @@ -546,6 +546,12 @@ public: | |||||
| struct Test5 | struct Test5 | ||||
| { | { | ||||
| static void test (UnitTest& unitTest) | static void test (UnitTest& unitTest) | ||||
| { | |||||
| test (unitTest, false); | |||||
| test (unitTest, true); | |||||
| } | |||||
| static void test (UnitTest& unitTest, bool inPlace) | |||||
| { | { | ||||
| const int numSamples = 2048; | const int numSamples = 2048; | ||||
| int32 original [numSamples], converted [numSamples], reversed [numSamples]; | int32 original [numSamples], converted [numSamples], reversed [numSamples]; | ||||
| @@ -572,13 +578,15 @@ public: | |||||
| // convert data from the source to dest format.. | // convert data from the source to dest format.. | ||||
| ScopedPointer<AudioData::Converter> conv (new AudioData::ConverterInstance <AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>, | ScopedPointer<AudioData::Converter> conv (new AudioData::ConverterInstance <AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>, | ||||
| AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst> >()); | AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst> >()); | ||||
| conv->convertSamples (converted, original, numSamples); | |||||
| conv->convertSamples (inPlace ? reversed : converted, original, numSamples); | |||||
| // ..and back again.. | // ..and back again.. | ||||
| conv = new AudioData::ConverterInstance <AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>, | conv = new AudioData::ConverterInstance <AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>, | ||||
| AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> >(); | AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::NonConst> >(); | ||||
| zerostruct (reversed); | |||||
| conv->convertSamples (reversed, converted, numSamples); | |||||
| if (! inPlace) | |||||
| zerostruct (reversed); | |||||
| conv->convertSamples (reversed, inPlace ? reversed : converted, numSamples); | |||||
| { | { | ||||
| int biggestDiff = 0; | int biggestDiff = 0; | ||||
| @@ -615,11 +623,12 @@ public: | |||||
| { | { | ||||
| static void test (UnitTest& unitTest) | static void test (UnitTest& unitTest) | ||||
| { | { | ||||
| Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest); | |||||
| Test3 <FormatType, Endianness, AudioData::UInt8>::test (unitTest); | |||||
| Test3 <FormatType, Endianness, AudioData::Int16>::test (unitTest); | Test3 <FormatType, Endianness, AudioData::Int16>::test (unitTest); | ||||
| Test3 <FormatType, Endianness, AudioData::Int24>::test (unitTest); | Test3 <FormatType, Endianness, AudioData::Int24>::test (unitTest); | ||||
| Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest); | Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest); | ||||
| Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest); | Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest); | ||||
| Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -648,6 +657,6 @@ public: | |||||
| static AudioConversionTests audioConversionUnitTests; | static AudioConversionTests audioConversionUnitTests; | ||||
| #endif | #endif | ||||
| */ | |||||
| END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
| @@ -26,7 +26,6 @@ | |||||
| #ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ | #ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ | ||||
| #define __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ | #define __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ | ||||
| #if 0 | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| @@ -42,6 +41,7 @@ public: | |||||
| // These types can be used as the SampleFormat template parameter for the AudioData::Pointer class. | // These types can be used as the SampleFormat template parameter for the AudioData::Pointer class. | ||||
| class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */ | class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */ | ||||
| class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */ | |||||
| class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */ | class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */ | ||||
| class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */ | class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */ | ||||
| class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */ | class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */ | ||||
| @@ -118,20 +118,19 @@ public: | |||||
| : InterleavingType (numInterleavedChannels), | : InterleavingType (numInterleavedChannels), | ||||
| data (Constness::toVoidPtr (sourceData)) | data (Constness::toVoidPtr (sourceData)) | ||||
| { | { | ||||
| // If you're using non-interleaved data, call the other constructor! If you're using non-interleaved data, | |||||
| // you should pass NonInterleaved as the template parameter for the interleaving type! | |||||
| static_jassert (InterleavingType::isInterleavedType != 0); | |||||
| } | } | ||||
| /** Creates a copy of another pointer. */ | /** Creates a copy of another pointer. */ | ||||
| Pointer (const Pointer& other) throw() | Pointer (const Pointer& other) throw() | ||||
| : data (other.data) | |||||
| : InterleavingType (other), | |||||
| data (other.data) | |||||
| { | { | ||||
| } | } | ||||
| Pointer& operator= (const Pointer& other) throw() | Pointer& operator= (const Pointer& other) throw() | ||||
| { | { | ||||
| data = other.data; | data = other.data; | ||||
| InterleavingType::copyFrom (other); | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| @@ -172,9 +171,12 @@ public: | |||||
| Endianness::setAsInt32 (data, newValue); | Endianness::setAsInt32 (data, newValue); | ||||
| } | } | ||||
| /** Adds a number of samples to the pointer's position. */ | |||||
| /** Moves the pointer along to the next sample. */ | |||||
| inline Pointer& operator++() throw() { advance(); return *this; } | inline Pointer& operator++() throw() { advance(); return *this; } | ||||
| /** Moves the pointer back to the previous sample. */ | |||||
| inline Pointer& operator--() throw() { advanceDataBy (data, -1); return *this; } | |||||
| /** Adds a number of samples to the pointer's position. */ | /** Adds a number of samples to the pointer's position. */ | ||||
| Pointer& operator+= (int samplesToJump) throw() { advanceDataBy (data, samplesToJump); return *this; } | Pointer& operator+= (int samplesToJump) throw() { advanceDataBy (data, samplesToJump); return *this; } | ||||
| @@ -203,12 +205,31 @@ public: | |||||
| static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! | static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! | ||||
| Pointer dest (*this); | Pointer dest (*this); | ||||
| while (--numSamples >= 0) | |||||
| if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples()) | |||||
| { | { | ||||
| Endianness::copyFrom (dest.data, source); | |||||
| dest.advance(); | |||||
| ++source; | |||||
| while (--numSamples >= 0) | |||||
| { | |||||
| Endianness::copyFrom (dest.data, source); | |||||
| dest.advance(); | |||||
| ++source; | |||||
| } | |||||
| } | } | ||||
| else // copy backwards if we're increasing the sample width.. | |||||
| { | |||||
| dest += numSamples; | |||||
| source += numSamples; | |||||
| while (--numSamples >= 0) | |||||
| Endianness::copyFrom ((--dest).data, --source); | |||||
| } | |||||
| } | |||||
| /** Sets a number of samples to zero. */ | |||||
| void clearSamples (int numSamples) const throw() | |||||
| { | |||||
| Pointer dest (*this); | |||||
| dest.clear (dest.data, numSamples); | |||||
| } | } | ||||
| /** Returns true if the pointer is using a floating-point format. */ | /** Returns true if the pointer is using a floating-point format. */ | ||||
| @@ -217,9 +238,15 @@ public: | |||||
| /** Returns true if the format is big-endian. */ | /** Returns true if the format is big-endian. */ | ||||
| static bool isBigEndian() throw() { return (bool) Endianness::isBigEndian; } | static bool isBigEndian() throw() { return (bool) Endianness::isBigEndian; } | ||||
| /** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */ | |||||
| static int getBytesPerSample() throw() { return (int) SampleFormat::bytesPerSample; } | |||||
| /** Returns the number of interleaved channels in the format. */ | /** Returns the number of interleaved channels in the format. */ | ||||
| int getNumInterleavedChannels() const throw() { return (int) this->numInterleavedChannels; } | int getNumInterleavedChannels() const throw() { return (int) this->numInterleavedChannels; } | ||||
| /** Returns the number of bytes between the start address of each sample. */ | |||||
| int getNumBytesBetweenSamples() const throw() { return InterleavingType::getNumBytesBetweenSamples (data); } | |||||
| /** Returns the accuracy of this format when represented as a 32-bit integer. | /** Returns the accuracy of this format when represented as a 32-bit integer. | ||||
| This is the smallest number above 0 that can be represented in the sample format, converted to | This is the smallest number above 0 that can be represented in the sample format, converted to | ||||
| a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit, | a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit, | ||||
| @@ -227,6 +254,9 @@ public: | |||||
| */ | */ | ||||
| static int get32BitResolution() throw() { return (int) SampleFormat::resolution; } | static int get32BitResolution() throw() { return (int) SampleFormat::resolution; } | ||||
| /** Returns a pointer to the underlying data. */ | |||||
| const void* getRawData() const throw() { return data.data; } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| SampleFormat data; | SampleFormat data; | ||||
| @@ -248,9 +278,15 @@ public: | |||||
| public: | public: | ||||
| virtual ~Converter() {} | virtual ~Converter() {} | ||||
| /** | |||||
| /** Converts a sequence of samples from the converter's source format into the dest format. */ | |||||
| virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0; | |||||
| /** Converts a sequence of samples from the converter's source format into the dest format. | |||||
| This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a | |||||
| particular sub-channel of the data to be used. | |||||
| */ | */ | ||||
| virtual void convertSamples (void* dest, const void* source, int numSamples) const = 0; | |||||
| virtual void convertSamples (void* destSamples, int destSubChannel, | |||||
| const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -266,19 +302,34 @@ public: | |||||
| class ConverterInstance : public Converter | class ConverterInstance : public Converter | ||||
| { | { | ||||
| public: | public: | ||||
| ConverterInstance() {} | |||||
| ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1) | |||||
| : sourceChannels (numSourceChannels), destChannels (numDestChannels) | |||||
| {} | |||||
| ~ConverterInstance() {} | ~ConverterInstance() {} | ||||
| void convertSamples (void* dest, const void* source, int numSamples) const | void convertSamples (void* dest, const void* source, int numSamples) const | ||||
| { | { | ||||
| SourceSampleType s (source); | |||||
| DestSampleType d (dest); | |||||
| SourceSampleType s (source, sourceChannels); | |||||
| DestSampleType d (dest, destChannels); | |||||
| d.convertSamples (s, numSamples); | |||||
| } | |||||
| void convertSamples (void* dest, int destSubChannel, | |||||
| const void* source, int sourceSubChannel, int numSamples) const | |||||
| { | |||||
| jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels); | |||||
| SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels); | |||||
| DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels); | |||||
| d.convertSamples (s, numSamples); | d.convertSamples (s, numSamples); | ||||
| } | } | ||||
| private: | private: | ||||
| ConverterInstance (const ConverterInstance&); | ConverterInstance (const ConverterInstance&); | ||||
| ConverterInstance& operator= (const ConverterInstance&); | ConverterInstance& operator= (const ConverterInstance&); | ||||
| const int sourceChannels, destChannels; | |||||
| }; | }; | ||||
| }; | }; | ||||
| @@ -324,18 +375,45 @@ public: | |||||
| inline void skip (int numSamples) throw() { data += numSamples; } | inline void skip (int numSamples) throw() { data += numSamples; } | ||||
| inline float getAsFloatLE() const throw() { return (float) (*data * (1.0 / (1.0 + maxValue))); } | inline float getAsFloatLE() const throw() { return (float) (*data * (1.0 / (1.0 + maxValue))); } | ||||
| inline float getAsFloatBE() const throw() { return getAsFloatLE(); } | inline float getAsFloatBE() const throw() { return getAsFloatLE(); } | ||||
| inline void setAsFloatLE (float newValue) throw() { *data = jlimit ((int8) -maxValue, (int8) maxValue, (int8) roundToInt (newValue * (1.0 + maxValue))); } | |||||
| inline void setAsFloatLE (float newValue) throw() { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); } | |||||
| inline void setAsFloatBE (float newValue) throw() { setAsFloatLE (newValue); } | inline void setAsFloatBE (float newValue) throw() { setAsFloatLE (newValue); } | ||||
| inline int32 getAsInt32LE() const throw() { return (int) (*data << 24); } | inline int32 getAsInt32LE() const throw() { return (int) (*data << 24); } | ||||
| inline int32 getAsInt32BE() const throw() { return getAsInt32LE(); } | inline int32 getAsInt32BE() const throw() { return getAsInt32LE(); } | ||||
| inline void setAsInt32LE (int newValue) throw() { *data = (int8) (newValue >> 24); } | inline void setAsInt32LE (int newValue) throw() { *data = (int8) (newValue >> 24); } | ||||
| inline void setAsInt32BE (int newValue) throw() { setAsInt32LE (newValue); } | inline void setAsInt32BE (int newValue) throw() { setAsInt32LE (newValue); } | ||||
| inline void clear() throw() { *data = 0; } | |||||
| inline void clearMultiple (int num) throw() { zeromem (data, num * bytesPerSample) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | ||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | ||||
| inline void copyFromSameType (Int8& source) throw() { *data = *source.data; } | inline void copyFromSameType (Int8& source) throw() { *data = *source.data; } | ||||
| int8* data; | int8* data; | ||||
| enum { maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; | |||||
| enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; | |||||
| }; | |||||
| class AudioData::UInt8 | |||||
| { | |||||
| public: | |||||
| inline UInt8 (void* data_) throw() : data (static_cast <uint8*> (data_)) {} | |||||
| inline void advance() throw() { ++data; } | |||||
| inline void skip (int numSamples) throw() { data += numSamples; } | |||||
| inline float getAsFloatLE() const throw() { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); } | |||||
| inline float getAsFloatBE() const throw() { return getAsFloatLE(); } | |||||
| inline void setAsFloatLE (float newValue) throw() { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); } | |||||
| inline void setAsFloatBE (float newValue) throw() { setAsFloatLE (newValue); } | |||||
| inline int32 getAsInt32LE() const throw() { return (int) ((*data - 128) << 24); } | |||||
| inline int32 getAsInt32BE() const throw() { return getAsInt32LE(); } | |||||
| inline void setAsInt32LE (int newValue) throw() { *data = (uint8) (128 + (newValue >> 24)); } | |||||
| inline void setAsInt32BE (int newValue) throw() { setAsInt32LE (newValue); } | |||||
| inline void clear() throw() { *data = 128; } | |||||
| inline void clearMultiple (int num) throw() { memset (data, 128, num) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | |||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | |||||
| inline void copyFromSameType (UInt8& source) throw() { *data = *source.data; } | |||||
| uint8* data; | |||||
| enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; | |||||
| }; | }; | ||||
| class AudioData::Int16 | class AudioData::Int16 | ||||
| @@ -353,12 +431,14 @@ public: | |||||
| inline int32 getAsInt32BE() const throw() { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); } | inline int32 getAsInt32BE() const throw() { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); } | ||||
| inline void setAsInt32LE (int32 newValue) throw() { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); } | inline void setAsInt32LE (int32 newValue) throw() { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); } | ||||
| inline void setAsInt32BE (int32 newValue) throw() { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); } | inline void setAsInt32BE (int32 newValue) throw() { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); } | ||||
| inline void clear() throw() { *data = 0; } | |||||
| inline void clearMultiple (int num) throw() { zeromem (data, num * bytesPerSample) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | ||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | ||||
| inline void copyFromSameType (Int16& source) throw() { *data = *source.data; } | inline void copyFromSameType (Int16& source) throw() { *data = *source.data; } | ||||
| uint16* data; | uint16* data; | ||||
| enum { maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; | |||||
| enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; | |||||
| }; | }; | ||||
| class AudioData::Int24 | class AudioData::Int24 | ||||
| @@ -376,12 +456,14 @@ public: | |||||
| inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::bigEndian24Bit (data) << 8; } | inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::bigEndian24Bit (data) << 8; } | ||||
| inline void setAsInt32LE (int32 newValue) throw() { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); } | inline void setAsInt32LE (int32 newValue) throw() { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); } | ||||
| inline void setAsInt32BE (int32 newValue) throw() { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); } | inline void setAsInt32BE (int32 newValue) throw() { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); } | ||||
| inline void clear() throw() { data[0] = 0; data[1] = 0; data[2] = 0; } | |||||
| inline void clearMultiple (int num) throw() { zeromem (data, num * bytesPerSample) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | ||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | ||||
| inline void copyFromSameType (Int24& source) throw() { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; } | inline void copyFromSameType (Int24& source) throw() { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; } | ||||
| char* data; | char* data; | ||||
| enum { maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; | |||||
| enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; | |||||
| }; | }; | ||||
| class AudioData::Int32 | class AudioData::Int32 | ||||
| @@ -399,12 +481,14 @@ public: | |||||
| inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::swapIfLittleEndian (*data); } | inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::swapIfLittleEndian (*data); } | ||||
| inline void setAsInt32LE (int32 newValue) throw() { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } | inline void setAsInt32LE (int32 newValue) throw() { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } | ||||
| inline void setAsInt32BE (int32 newValue) throw() { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); } | inline void setAsInt32BE (int32 newValue) throw() { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); } | ||||
| inline void clear() throw() { *data = 0; } | |||||
| inline void clearMultiple (int num) throw() { zeromem (data, num * bytesPerSample) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsInt32LE (source.getAsInt32()); } | ||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | ||||
| inline void copyFromSameType (Int32& source) throw() { *data = *source.data; } | inline void copyFromSameType (Int32& source) throw() { *data = *source.data; } | ||||
| uint32* data; | uint32* data; | ||||
| enum { maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; | |||||
| enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; | |||||
| }; | }; | ||||
| class AudioData::Float32 | class AudioData::Float32 | ||||
| @@ -429,12 +513,14 @@ public: | |||||
| inline int32 getAsInt32BE() const throw() { return (int32) roundToInt (jlimit (-1.0f, 1.0f, getAsFloatBE()) * (1.0 + maxValue)); } | inline int32 getAsInt32BE() const throw() { return (int32) roundToInt (jlimit (-1.0f, 1.0f, getAsFloatBE()) * (1.0 + maxValue)); } | ||||
| inline void setAsInt32LE (int32 newValue) throw() { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } | inline void setAsInt32LE (int32 newValue) throw() { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } | ||||
| inline void setAsInt32BE (int32 newValue) throw() { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } | inline void setAsInt32BE (int32 newValue) throw() { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } | ||||
| inline void clear() throw() { *data = 0; } | |||||
| inline void clearMultiple (int num) throw() { zeromem (data, num * bytesPerSample) ;} | |||||
| template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsFloatLE (source.getAsFloat()); } | template <class SourceType> inline void copyFromLE (SourceType& source) throw() { setAsFloatLE (source.getAsFloat()); } | ||||
| template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsFloatBE (source.getAsFloat()); } | template <class SourceType> inline void copyFromBE (SourceType& source) throw() { setAsFloatBE (source.getAsFloat()); } | ||||
| inline void copyFromSameType (Float32& source) throw() { *data = *source.data; } | inline void copyFromSameType (Float32& source) throw() { *data = *source.data; } | ||||
| float* data; | float* data; | ||||
| enum { maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 }; | |||||
| enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 }; | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -442,9 +528,14 @@ class AudioData::NonInterleaved | |||||
| { | { | ||||
| public: | public: | ||||
| inline NonInterleaved() throw() {} | inline NonInterleaved() throw() {} | ||||
| inline NonInterleaved (const NonInterleaved&) throw() {} | |||||
| inline NonInterleaved (const int) throw() {} | inline NonInterleaved (const int) throw() {} | ||||
| template <class SampleFormatType> inline void advanceData (SampleFormatType& s) throw() { s.advance(); } | |||||
| template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) throw() { s.skip (numSamples); } | |||||
| inline void copyFrom (const NonInterleaved&) throw() {} | |||||
| template <class SampleFormatType> inline void advanceData (SampleFormatType& s) throw() { s.advance(); } | |||||
| template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) throw() { s.skip (numSamples); } | |||||
| template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) throw() { s.clearMultiple (numSamples); } | |||||
| template <class SampleFormatType> inline static int getNumBytesBetweenSamples (const SampleFormatType&) throw() { return SampleFormatType::bytesPerSample; } | |||||
| enum { isInterleavedType = 0, numInterleavedChannels = 1 }; | enum { isInterleavedType = 0, numInterleavedChannels = 1 }; | ||||
| }; | }; | ||||
| @@ -452,14 +543,15 @@ class AudioData::Interleaved | |||||
| { | { | ||||
| public: | public: | ||||
| inline Interleaved() throw() : numInterleavedChannels (1) {} | inline Interleaved() throw() : numInterleavedChannels (1) {} | ||||
| inline Interleaved (const Interleaved& other) throw() : numInterleavedChannels (other.numInterleavedChannels) {} | |||||
| inline Interleaved (const int numInterleavedChannels_) throw() : numInterleavedChannels (numInterleavedChannels_) {} | inline Interleaved (const int numInterleavedChannels_) throw() : numInterleavedChannels (numInterleavedChannels_) {} | ||||
| inline void copyFrom (const Interleaved& other) throw() { numInterleavedChannels = other.numInterleavedChannels; } | |||||
| template <class SampleFormatType> inline void advanceData (SampleFormatType& s) throw() { s.skip (numInterleavedChannels); } | template <class SampleFormatType> inline void advanceData (SampleFormatType& s) throw() { s.skip (numInterleavedChannels); } | ||||
| template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) throw() { s.skip (numInterleavedChannels * numSamples); } | template <class SampleFormatType> inline void advanceDataBy (SampleFormatType& s, int numSamples) throw() { s.skip (numInterleavedChannels * numSamples); } | ||||
| const int numInterleavedChannels; | |||||
| template <class SampleFormatType> inline void clear (SampleFormatType& s, int numSamples) throw() { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } } | |||||
| template <class SampleFormatType> inline int getNumBytesBetweenSamples (const SampleFormatType& s) const throw() { return numInterleavedChannels * s.bytesPerSample; } | |||||
| int numInterleavedChannels; | |||||
| enum { isInterleavedType = 1 }; | enum { isInterleavedType = 1 }; | ||||
| private: | |||||
| Interleaved& operator= (const Interleaved&); | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -481,8 +573,6 @@ public: | |||||
| #endif | #endif | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| A set of routines to convert buffers of 32-bit floating point data to and from | A set of routines to convert buffers of 32-bit floating point data to and from | ||||
| @@ -528,13 +528,17 @@ public: | |||||
| /** Removes a previously added listener. */ | /** Removes a previously added listener. */ | ||||
| void removeListener (AudioProcessorListener* listenerToRemove); | void removeListener (AudioProcessorListener* listenerToRemove); | ||||
| //============================================================================== | |||||
| /** Tells the processor to use this playhead object. | |||||
| The processor will not take ownership of the object, so the caller must delete it when | |||||
| it is no longer being used. | |||||
| */ | |||||
| void setPlayHead (AudioPlayHead* newPlayHead) throw(); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Not for public use - this is called before deleting an editor component. */ | /** Not for public use - this is called before deleting an editor component. */ | ||||
| void editorBeingDeleted (AudioProcessorEditor* editor) throw(); | void editorBeingDeleted (AudioProcessorEditor* editor) throw(); | ||||
| /** Not for public use - this is called to initialise the processor. */ | |||||
| void setPlayHead (AudioPlayHead* newPlayHead) throw(); | |||||
| /** Not for public use - this is called to initialise the processor before playing. */ | /** Not for public use - this is called to initialise the processor before playing. */ | ||||
| void setPlayConfigDetails (int numIns, int numOuts, | void setPlayConfigDetails (int numIns, int numOuts, | ||||
| double sampleRate, | double sampleRate, | ||||
| @@ -122,6 +122,12 @@ public: | |||||
| */ | */ | ||||
| inline operator void*() const throw() { return static_cast <void*> (data); } | inline operator void*() const throw() { return static_cast <void*> (data); } | ||||
| /** Returns a void pointer to the allocated data. | |||||
| This may be a null pointer if the data hasn't yet been allocated, or if it has been | |||||
| freed by calling the free() method. | |||||
| */ | |||||
| inline operator const void*() const throw() { return static_cast <const void*> (data); } | |||||
| /** Lets you use indirect calls to the first element in the array. | /** Lets you use indirect calls to the first element in the array. | ||||
| Obviously this will cause problems if the array hasn't been initialised, because it'll | Obviously this will cause problems if the array hasn't been initialised, because it'll | ||||
| be referencing a null pointer. | be referencing a null pointer. | ||||
| @@ -63,6 +63,7 @@ public: | |||||
| class var::VariantType_Void : public var::VariantType | class var::VariantType_Void : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Void() {} | |||||
| static const VariantType_Void* getInstance() { static const VariantType_Void i; return &i; } | static const VariantType_Void* getInstance() { static const VariantType_Void i; return &i; } | ||||
| bool isVoid() const throw() { return true; } | bool isVoid() const throw() { return true; } | ||||
| @@ -74,6 +75,7 @@ public: | |||||
| class var::VariantType_Int : public var::VariantType | class var::VariantType_Int : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Int() {} | |||||
| static const VariantType_Int* getInstance() { static const VariantType_Int i; return &i; } | static const VariantType_Int* getInstance() { static const VariantType_Int i; return &i; } | ||||
| int toInt (const ValueUnion& data) const { return data.intValue; }; | int toInt (const ValueUnion& data) const { return data.intValue; }; | ||||
| @@ -100,6 +102,7 @@ public: | |||||
| class var::VariantType_Double : public var::VariantType | class var::VariantType_Double : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Double() {} | |||||
| static const VariantType_Double* getInstance() { static const VariantType_Double i; return &i; } | static const VariantType_Double* getInstance() { static const VariantType_Double i; return &i; } | ||||
| int toInt (const ValueUnion& data) const { return (int) data.doubleValue; }; | int toInt (const ValueUnion& data) const { return (int) data.doubleValue; }; | ||||
| @@ -126,6 +129,7 @@ public: | |||||
| class var::VariantType_Bool : public var::VariantType | class var::VariantType_Bool : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Bool() {} | |||||
| static const VariantType_Bool* getInstance() { static const VariantType_Bool i; return &i; } | static const VariantType_Bool* getInstance() { static const VariantType_Bool i; return &i; } | ||||
| int toInt (const ValueUnion& data) const { return data.boolValue ? 1 : 0; }; | int toInt (const ValueUnion& data) const { return data.boolValue ? 1 : 0; }; | ||||
| @@ -151,6 +155,7 @@ public: | |||||
| class var::VariantType_String : public var::VariantType | class var::VariantType_String : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_String() {} | |||||
| static const VariantType_String* getInstance() { static const VariantType_String i; return &i; } | static const VariantType_String* getInstance() { static const VariantType_String i; return &i; } | ||||
| void cleanUp (ValueUnion& data) const throw() { delete data.stringValue; } | void cleanUp (ValueUnion& data) const throw() { delete data.stringValue; } | ||||
| @@ -185,6 +190,7 @@ public: | |||||
| class var::VariantType_Object : public var::VariantType | class var::VariantType_Object : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Object() {} | |||||
| static const VariantType_Object* getInstance() { static const VariantType_Object i; return &i; } | static const VariantType_Object* getInstance() { static const VariantType_Object i; return &i; } | ||||
| void cleanUp (ValueUnion& data) const throw() { if (data.objectValue != 0) data.objectValue->decReferenceCount(); } | void cleanUp (ValueUnion& data) const throw() { if (data.objectValue != 0) data.objectValue->decReferenceCount(); } | ||||
| @@ -212,6 +218,7 @@ public: | |||||
| class var::VariantType_Method : public var::VariantType | class var::VariantType_Method : public var::VariantType | ||||
| { | { | ||||
| public: | public: | ||||
| VariantType_Method() {} | |||||
| static const VariantType_Method* getInstance() { static const VariantType_Method i; return &i; } | static const VariantType_Method* getInstance() { static const VariantType_Method i; return &i; } | ||||
| const String toString (const ValueUnion&) const { return "Method"; } | const String toString (const ValueUnion&) const { return "Method"; } | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 69 | |||||
| #define JUCE_BUILDNUMBER 70 | |||||
| /** Current Juce version number. | /** Current Juce version number. | ||||
| @@ -525,7 +525,7 @@ public: | |||||
| @see Component::createComponentSnapshot | @see Component::createComponentSnapshot | ||||
| */ | */ | ||||
| const Image createSnapshotOfSelectedRows (int& x, int& y); | |||||
| virtual const Image createSnapshotOfSelectedRows (int& x, int& y); | |||||
| /** Returns the viewport that this ListBox uses. | /** Returns the viewport that this ListBox uses. | ||||
| @@ -82,7 +82,7 @@ static void getDeviceProperties (const String& deviceID, | |||||
| if (snd_ctl_pcm_info (handle, info) >= 0) | if (snd_ctl_pcm_info (handle, info) >= 0) | ||||
| { | { | ||||
| snd_pcm_t* pcmHandle; | snd_pcm_t* pcmHandle; | ||||
| if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC | SND_PCM_NONBLOCK ) >= 0) | |||||
| if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0) | |||||
| { | { | ||||
| getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); | getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); | ||||
| getDeviceSampleRates (pcmHandle, rates); | getDeviceSampleRates (pcmHandle, rates); | ||||
| @@ -96,7 +96,7 @@ static void getDeviceProperties (const String& deviceID, | |||||
| if (snd_ctl_pcm_info (handle, info) >= 0) | if (snd_ctl_pcm_info (handle, info) >= 0) | ||||
| { | { | ||||
| snd_pcm_t* pcmHandle; | snd_pcm_t* pcmHandle; | ||||
| if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC | SND_PCM_NONBLOCK ) >= 0) | |||||
| if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0) | |||||
| { | { | ||||
| getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); | getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); | ||||
| @@ -115,14 +115,12 @@ static void getDeviceProperties (const String& deviceID, | |||||
| class ALSADevice | class ALSADevice | ||||
| { | { | ||||
| public: | public: | ||||
| ALSADevice (const String& deviceID, | |||||
| const bool forInput) | |||||
| ALSADevice (const String& deviceID, bool forInput) | |||||
| : handle (0), | : handle (0), | ||||
| bitDepth (16), | bitDepth (16), | ||||
| numChannelsRunning (0), | numChannelsRunning (0), | ||||
| latency (0), | latency (0), | ||||
| isInput (forInput), | |||||
| sampleFormat (AudioDataConverters::int16LE) | |||||
| isInput (forInput) | |||||
| { | { | ||||
| failed (snd_pcm_open (&handle, deviceID.toUTF8(), | failed (snd_pcm_open (&handle, deviceID.toUTF8(), | ||||
| forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | ||||
| @@ -156,22 +154,26 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32, AudioDataConverters::float32LE, | |||||
| SND_PCM_FORMAT_FLOAT_BE, 32, AudioDataConverters::float32BE, | |||||
| SND_PCM_FORMAT_S32_LE, 32, AudioDataConverters::int32LE, | |||||
| SND_PCM_FORMAT_S32_BE, 32, AudioDataConverters::int32BE, | |||||
| SND_PCM_FORMAT_S24_3LE, 24, AudioDataConverters::int24LE, | |||||
| SND_PCM_FORMAT_S24_3BE, 24, AudioDataConverters::int24BE, | |||||
| SND_PCM_FORMAT_S16_LE, 16, AudioDataConverters::int16LE, | |||||
| SND_PCM_FORMAT_S16_BE, 16, AudioDataConverters::int16BE }; | |||||
| enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17 }; | |||||
| const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit, | |||||
| SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit, | |||||
| SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit, | |||||
| SND_PCM_FORMAT_S32_BE, 32, | |||||
| SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit, | |||||
| SND_PCM_FORMAT_S24_3BE, 24, | |||||
| SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit, | |||||
| SND_PCM_FORMAT_S16_BE, 16 }; | |||||
| bitDepth = 0; | bitDepth = 0; | ||||
| for (int i = 0; i < numElementsInArray (formatsToTry); i += 3) | |||||
| for (int i = 0; i < numElementsInArray (formatsToTry); i += 2) | |||||
| { | { | ||||
| if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) | if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) | ||||
| { | { | ||||
| bitDepth = formatsToTry [i + 1]; | |||||
| sampleFormat = (AudioDataConverters::DataFormat) formatsToTry [i + 2]; | |||||
| bitDepth = formatsToTry [i + 1] & 255; | |||||
| const bool isFloat = (formatsToTry [i + 1] & isFloatBit) != 0; | |||||
| const bool isLittleEndian = (formatsToTry [i + 1] & isLittleEndianBit) != 0; | |||||
| converter = createConverter (isInput, bitDepth, isFloat, isLittleEndian, numChannels); | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -235,48 +237,44 @@ public: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| bool write (AudioSampleBuffer& outputChannelBuffer, const int numSamples) | |||||
| bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples) | |||||
| { | { | ||||
| jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); | jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); | ||||
| float** const data = outputChannelBuffer.getArrayOfChannels(); | float** const data = outputChannelBuffer.getArrayOfChannels(); | ||||
| snd_pcm_sframes_t numDone = 0; | |||||
| if (isInterleaved) | if (isInterleaved) | ||||
| { | { | ||||
| scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | ||||
| float* interleaved = static_cast <float*> (scratch.getData()); | |||||
| AudioDataConverters::interleaveSamples ((const float**) data, interleaved, numSamples, numChannelsRunning); | |||||
| AudioDataConverters::convertFloatToFormat (sampleFormat, interleaved, interleaved, numSamples * numChannelsRunning); | |||||
| snd_pcm_sframes_t num = snd_pcm_writei (handle, interleaved, numSamples); | |||||
| for (int i = 0; i < numChannelsRunning; ++i) | |||||
| converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples); | |||||
| if (failed (num) && num != -EPIPE && num != -ESTRPIPE) | |||||
| return false; | |||||
| numDone = snd_pcm_writei (handle, scratch.getData(), numSamples); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| for (int i = 0; i < numChannelsRunning; ++i) | for (int i = 0; i < numChannelsRunning; ++i) | ||||
| if (data[i] != 0) | |||||
| AudioDataConverters::convertFloatToFormat (sampleFormat, data[i], data[i], numSamples); | |||||
| converter->convertSamples (data[i], data[i], numSamples); | |||||
| snd_pcm_sframes_t num = snd_pcm_writen (handle, (void**) data, numSamples); | |||||
| numDone = snd_pcm_writen (handle, (void**) data, numSamples); | |||||
| } | |||||
| if (failed (num)) | |||||
| if (failed (numDone)) | |||||
| { | |||||
| if (numDone == -EPIPE) | |||||
| { | { | ||||
| if (num == -EPIPE) | |||||
| { | |||||
| if (failed (snd_pcm_prepare (handle))) | |||||
| return false; | |||||
| } | |||||
| else if (num != -ESTRPIPE) | |||||
| if (failed (snd_pcm_prepare (handle))) | |||||
| return false; | return false; | ||||
| } | } | ||||
| else if (numDone != -ESTRPIPE) | |||||
| return false; | |||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool read (AudioSampleBuffer& inputChannelBuffer, const int numSamples) | |||||
| bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples) | |||||
| { | { | ||||
| jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); | jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); | ||||
| float** const data = inputChannelBuffer.getArrayOfChannels(); | float** const data = inputChannelBuffer.getArrayOfChannels(); | ||||
| @@ -284,9 +282,8 @@ public: | |||||
| if (isInterleaved) | if (isInterleaved) | ||||
| { | { | ||||
| scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false); | ||||
| float* interleaved = static_cast <float*> (scratch.getData()); | |||||
| snd_pcm_sframes_t num = snd_pcm_readi (handle, interleaved, numSamples); | |||||
| snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples); | |||||
| if (failed (num)) | if (failed (num)) | ||||
| { | { | ||||
| @@ -299,8 +296,8 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| AudioDataConverters::convertFormatToFloat (sampleFormat, interleaved, interleaved, numSamples * numChannelsRunning); | |||||
| AudioDataConverters::deinterleaveSamples (interleaved, data, numSamples, numChannelsRunning); | |||||
| for (int i = 0; i < numChannelsRunning; ++i) | |||||
| converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -310,8 +307,7 @@ public: | |||||
| return false; | return false; | ||||
| for (int i = 0; i < numChannelsRunning; ++i) | for (int i = 0; i < numChannelsRunning; ++i) | ||||
| if (data[i] != 0) | |||||
| AudioDataConverters::convertFormatToFloat (sampleFormat, data[i], data[i], numSamples); | |||||
| converter->convertSamples (data[i], data[i], numSamples); | |||||
| } | } | ||||
| return true; | return true; | ||||
| @@ -329,7 +325,48 @@ private: | |||||
| const bool isInput; | const bool isInput; | ||||
| bool isInterleaved; | bool isInterleaved; | ||||
| MemoryBlock scratch; | MemoryBlock scratch; | ||||
| AudioDataConverters::DataFormat sampleFormat; | |||||
| ScopedPointer<AudioData::Converter> converter; | |||||
| //============================================================================== | |||||
| template <class SampleType> | |||||
| struct ConverterHelper | |||||
| { | |||||
| static AudioData::Converter* createConverter (const bool forInput, const bool isLittleEndian, const int numInterleavedChannels) | |||||
| { | |||||
| if (forInput) | |||||
| { | |||||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType; | |||||
| if (isLittleEndian) | |||||
| return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1); | |||||
| else | |||||
| return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1); | |||||
| } | |||||
| else | |||||
| { | |||||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceType; | |||||
| if (isLittleEndian) | |||||
| return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels); | |||||
| else | |||||
| return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels); | |||||
| } | |||||
| } | |||||
| }; | |||||
| static AudioData::Converter* createConverter (const bool forInput, const int bitDepth, const bool isFloat, const bool isLittleEndian, const int numInterleavedChannels) | |||||
| { | |||||
| switch (bitDepth) | |||||
| { | |||||
| case 16: return ConverterHelper <AudioData::Int16>::createConverter (forInput, isLittleEndian, numInterleavedChannels); | |||||
| case 24: return ConverterHelper <AudioData::Int24>::createConverter (forInput, isLittleEndian, numInterleavedChannels); | |||||
| case 32: return isFloat ? ConverterHelper <AudioData::Float32>::createConverter (forInput, isLittleEndian, numInterleavedChannels) | |||||
| : ConverterHelper <AudioData::Int32>::createConverter (forInput, isLittleEndian, numInterleavedChannels); | |||||
| default: jassertfalse; break; // unsupported format! | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| bool failed (const int errorNum) | bool failed (const int errorNum) | ||||
| @@ -522,7 +559,7 @@ public: | |||||
| { | { | ||||
| if (inputDevice != 0) | if (inputDevice != 0) | ||||
| { | { | ||||
| if (! inputDevice->read (inputChannelBuffer, bufferSize)) | |||||
| if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize)) | |||||
| { | { | ||||
| DBG ("ALSA: read failure"); | DBG ("ALSA: read failure"); | ||||
| break; | break; | ||||
| @@ -560,7 +597,7 @@ public: | |||||
| failed (snd_pcm_avail_update (outputDevice->handle)); | failed (snd_pcm_avail_update (outputDevice->handle)); | ||||
| if (! outputDevice->write (outputChannelBuffer, bufferSize)) | |||||
| if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize)) | |||||
| { | { | ||||
| DBG ("ALSA: write failure"); | DBG ("ALSA: write failure"); | ||||
| break; | break; | ||||
| @@ -279,10 +279,19 @@ END_JUCE_NAMESPACE | |||||
| source->getNextAudioBlock (info); | source->getNextAudioBlock (info); | ||||
| JUCE_NAMESPACE::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (0), | |||||
| buffer, numSamples, 4); | |||||
| JUCE_NAMESPACE::AudioDataConverters::convertFloatToInt16LE (tempBuffer.getSampleData (1), | |||||
| buffer + 2, numSamples, 4); | |||||
| typedef JUCE_NAMESPACE::AudioData::Pointer <JUCE_NAMESPACE::AudioData::Int16, | |||||
| JUCE_NAMESPACE::AudioData::LittleEndian, | |||||
| JUCE_NAMESPACE::AudioData::Interleaved, | |||||
| JUCE_NAMESPACE::AudioData::NonConst> CDSampleFormat; | |||||
| typedef JUCE_NAMESPACE::AudioData::Pointer <JUCE_NAMESPACE::AudioData::Float32, | |||||
| JUCE_NAMESPACE::AudioData::NativeEndian, | |||||
| JUCE_NAMESPACE::AudioData::NonInterleaved, | |||||
| JUCE_NAMESPACE::AudioData::Const> SourceSampleFormat; | |||||
| CDSampleFormat left (buffer, 2); | |||||
| left.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (0)), numSamples); | |||||
| CDSampleFormat right (buffer + 2, 2); | |||||
| right.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (1)), numSamples); | |||||
| readPosition += numSamples; | readPosition += numSamples; | ||||
| } | } | ||||
| @@ -306,7 +306,6 @@ public: | |||||
| size = sizeof (lat); | size = sizeof (lat); | ||||
| pa.mSelector = kAudioDevicePropertyLatency; | pa.mSelector = kAudioDevicePropertyLatency; | ||||
| pa.mScope = kAudioDevicePropertyScopeInput; | pa.mScope = kAudioDevicePropertyScopeInput; | ||||
| //if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) | |||||
| if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) | if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) | ||||
| inputLatency = (int) lat; | inputLatency = (int) lat; | ||||
| @@ -458,7 +458,7 @@ private: | |||||
| } | } | ||||
| // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! | // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts! | ||||
| return jmax (-1, c - 29); | |||||
| return jmax (-1, (int) c - 29); | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -82,7 +82,7 @@ public: | |||||
| JUCEApplication::appWillTerminateByForce(); | JUCEApplication::appWillTerminateByForce(); | ||||
| } | } | ||||
| virtual BOOL openFile (const NSString* filename) | |||||
| virtual BOOL openFile (NSString* filename) | |||||
| { | { | ||||
| if (JUCEApplication::getInstance() != 0) | if (JUCEApplication::getInstance() != 0) | ||||
| { | { | ||||
| @@ -2378,11 +2378,16 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) | |||||
| zeromem (buffer, bytesPerBlock); | zeromem (buffer, bytesPerBlock); | ||||
| AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (0, 0), | |||||
| buffer, samplesPerBlock, 4); | |||||
| typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, | |||||
| AudioData::Interleaved, AudioData::NonConst> CDSampleFormat; | |||||
| AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), | |||||
| buffer + 2, samplesPerBlock, 4); | |||||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, | |||||
| AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; | |||||
| CDSampleFormat left (buffer, 2); | |||||
| left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock); | |||||
| CDSampleFormat right (buffer + 2, 2); | |||||
| right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock); | |||||
| hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | ||||
| @@ -43,7 +43,7 @@ DynamicLibraryLoader::~DynamicLibraryLoader() | |||||
| void* DynamicLibraryLoader::findProcAddress (const String& functionName) | void* DynamicLibraryLoader::findProcAddress (const String& functionName) | ||||
| { | { | ||||
| return GetProcAddress ((HMODULE) libHandle, functionName.toCString()); | |||||
| return (void*) GetProcAddress ((HMODULE) libHandle, functionName.toCString()); // (void* cast is required for mingw) | |||||
| } | } | ||||
| @@ -331,7 +331,7 @@ void PlatformUtilities::freeDynamicLibrary (void* h) | |||||
| void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name) | void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name) | ||||
| { | { | ||||
| return (h != 0) ? GetProcAddress ((HMODULE) h, name.toCString()) : 0; | |||||
| return (h != 0) ? (void*) GetProcAddress ((HMODULE) h, name.toCString()) : 0; // (void* cast is required for mingw) | |||||
| } | } | ||||
| @@ -232,11 +232,12 @@ public: | |||||
| Array <double> rates; | Array <double> rates; | ||||
| HANDLE clientEvent; | HANDLE clientEvent; | ||||
| BigInteger channels; | BigInteger channels; | ||||
| AudioDataConverters::DataFormat dataFormat; | |||||
| Array <int> channelMaps; | Array <int> channelMaps; | ||||
| UINT32 actualBufferSize; | UINT32 actualBufferSize; | ||||
| int bytesPerSample; | int bytesPerSample; | ||||
| virtual void updateFormat (bool isFloat) = 0; | |||||
| private: | private: | ||||
| const ComSmartPtr <IAudioClient> createClient() | const ComSmartPtr <IAudioClient> createClient() | ||||
| { | { | ||||
| @@ -311,10 +312,8 @@ private: | |||||
| actualNumChannels = format.Format.nChannels; | actualNumChannels = format.Format.nChannels; | ||||
| const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | ||||
| bytesPerSample = format.Format.wBitsPerSample / 8; | bytesPerSample = format.Format.wBitsPerSample / 8; | ||||
| dataFormat = isFloat ? AudioDataConverters::float32LE | |||||
| : (bytesPerSample == 4 ? AudioDataConverters::int32LE | |||||
| : ((bytesPerSample == 3 ? AudioDataConverters::int24LE | |||||
| : AudioDataConverters::int16LE))); | |||||
| updateFormat (isFloat); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -356,6 +355,20 @@ public: | |||||
| reservoir.setSize (0); | reservoir.setSize (0); | ||||
| } | } | ||||
| void updateFormat (bool isFloat) | |||||
| { | |||||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> NativeType; | |||||
| if (isFloat) | |||||
| converter = new AudioData::ConverterInstance <AudioData::Pointer <AudioData::Float32, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1); | |||||
| else if (bytesPerSample == 4) | |||||
| converter = new AudioData::ConverterInstance <AudioData::Pointer <AudioData::Int32, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1); | |||||
| else if (bytesPerSample == 3) | |||||
| converter = new AudioData::ConverterInstance <AudioData::Pointer <AudioData::Int24, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1); | |||||
| else | |||||
| converter = new AudioData::ConverterInstance <AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1); | |||||
| } | |||||
| void copyBuffers (float** destBuffers, int numDestBuffers, int bufferSize, Thread& thread) | void copyBuffers (float** destBuffers, int numDestBuffers, int bufferSize, Thread& thread) | ||||
| { | { | ||||
| if (numChannels <= 0) | if (numChannels <= 0) | ||||
| @@ -370,31 +383,7 @@ public: | |||||
| const int samplesToDo = jmin (bufferSize, (int) reservoirSize); | const int samplesToDo = jmin (bufferSize, (int) reservoirSize); | ||||
| for (int i = 0; i < numDestBuffers; ++i) | for (int i = 0; i < numDestBuffers; ++i) | ||||
| { | |||||
| float* const dest = destBuffers[i] + offset; | |||||
| const int srcChan = channelMaps.getUnchecked(i); | |||||
| switch (dataFormat) | |||||
| { | |||||
| case AudioDataConverters::float32LE: | |||||
| AudioDataConverters::convertFloat32LEToFloat (((uint8*) reservoir.getData()) + 4 * srcChan, dest, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int32LE: | |||||
| AudioDataConverters::convertInt32LEToFloat (((uint8*) reservoir.getData()) + 4 * srcChan, dest, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int24LE: | |||||
| AudioDataConverters::convertInt24LEToFloat (((uint8*) reservoir.getData()) + 3 * srcChan, dest, samplesToDo, 3 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int16LE: | |||||
| AudioDataConverters::convertInt16LEToFloat (((uint8*) reservoir.getData()) + 2 * srcChan, dest, samplesToDo, 2 * actualNumChannels); | |||||
| break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| } | |||||
| converter->convertSamples (destBuffers[i], offset, reservoir.getData(), channelMaps.getUnchecked(i), samplesToDo); | |||||
| bufferSize -= samplesToDo; | bufferSize -= samplesToDo; | ||||
| offset += samplesToDo; | offset += samplesToDo; | ||||
| @@ -424,31 +413,7 @@ public: | |||||
| const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable); | const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable); | ||||
| for (int i = 0; i < numDestBuffers; ++i) | for (int i = 0; i < numDestBuffers; ++i) | ||||
| { | |||||
| float* const dest = destBuffers[i] + offset; | |||||
| const int srcChan = channelMaps.getUnchecked(i); | |||||
| switch (dataFormat) | |||||
| { | |||||
| case AudioDataConverters::float32LE: | |||||
| AudioDataConverters::convertFloat32LEToFloat (inputData + 4 * srcChan, dest, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int32LE: | |||||
| AudioDataConverters::convertInt32LEToFloat (inputData + 4 * srcChan, dest, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int24LE: | |||||
| AudioDataConverters::convertInt24LEToFloat (inputData + 3 * srcChan, dest, samplesToDo, 3 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int16LE: | |||||
| AudioDataConverters::convertInt16LEToFloat (inputData + 2 * srcChan, dest, samplesToDo, 2 * actualNumChannels); | |||||
| break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| } | |||||
| converter->convertSamples (destBuffers[i], offset, inputData, channelMaps.getUnchecked(i), samplesToDo); | |||||
| bufferSize -= samplesToDo; | bufferSize -= samplesToDo; | ||||
| offset += samplesToDo; | offset += samplesToDo; | ||||
| @@ -469,6 +434,7 @@ public: | |||||
| ComSmartPtr <IAudioCaptureClient> captureClient; | ComSmartPtr <IAudioCaptureClient> captureClient; | ||||
| MemoryBlock reservoir; | MemoryBlock reservoir; | ||||
| int reservoirSize, reservoirCapacity; | int reservoirSize, reservoirCapacity; | ||||
| ScopedPointer <AudioData::Converter> converter; | |||||
| private: | private: | ||||
| WASAPIInputDevice (const WASAPIInputDevice&); | WASAPIInputDevice (const WASAPIInputDevice&); | ||||
| @@ -501,6 +467,20 @@ public: | |||||
| renderClient = 0; | renderClient = 0; | ||||
| } | } | ||||
| void updateFormat (bool isFloat) | |||||
| { | |||||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> NativeType; | |||||
| if (isFloat) | |||||
| converter = new AudioData::ConverterInstance <NativeType, AudioData::Pointer <AudioData::Float32, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels); | |||||
| else if (bytesPerSample == 4) | |||||
| converter = new AudioData::ConverterInstance <NativeType, AudioData::Pointer <AudioData::Int32, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels); | |||||
| else if (bytesPerSample == 3) | |||||
| converter = new AudioData::ConverterInstance <NativeType, AudioData::Pointer <AudioData::Int24, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels); | |||||
| else | |||||
| converter = new AudioData::ConverterInstance <NativeType, AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels); | |||||
| } | |||||
| void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, Thread& thread) | void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, Thread& thread) | ||||
| { | { | ||||
| if (numChannels <= 0) | if (numChannels <= 0) | ||||
| @@ -530,31 +510,7 @@ public: | |||||
| if (OK (renderClient->GetBuffer (samplesToDo, &outputData))) | if (OK (renderClient->GetBuffer (samplesToDo, &outputData))) | ||||
| { | { | ||||
| for (int i = 0; i < numSrcBuffers; ++i) | for (int i = 0; i < numSrcBuffers; ++i) | ||||
| { | |||||
| const float* const source = srcBuffers[i] + offset; | |||||
| const int destChan = channelMaps.getUnchecked(i); | |||||
| switch (dataFormat) | |||||
| { | |||||
| case AudioDataConverters::float32LE: | |||||
| AudioDataConverters::convertFloatToFloat32LE (source, outputData + 4 * destChan, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int32LE: | |||||
| AudioDataConverters::convertFloatToInt32LE (source, outputData + 4 * destChan, samplesToDo, 4 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int24LE: | |||||
| AudioDataConverters::convertFloatToInt24LE (source, outputData + 3 * destChan, samplesToDo, 3 * actualNumChannels); | |||||
| break; | |||||
| case AudioDataConverters::int16LE: | |||||
| AudioDataConverters::convertFloatToInt16LE (source, outputData + 2 * destChan, samplesToDo, 2 * actualNumChannels); | |||||
| break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| } | |||||
| converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i], offset, samplesToDo); | |||||
| renderClient->ReleaseBuffer (samplesToDo, 0); | renderClient->ReleaseBuffer (samplesToDo, 0); | ||||
| @@ -565,6 +521,7 @@ public: | |||||
| } | } | ||||
| ComSmartPtr <IAudioRenderClient> renderClient; | ComSmartPtr <IAudioRenderClient> renderClient; | ||||
| ScopedPointer <AudioData::Converter> converter; | |||||
| private: | private: | ||||
| WASAPIOutputDevice (const WASAPIOutputDevice&); | WASAPIOutputDevice (const WASAPIOutputDevice&); | ||||
| @@ -350,7 +350,7 @@ namespace NumberToStringConverters | |||||
| static juce_wchar getDecimalPoint() | static juce_wchar getDecimalPoint() | ||||
| { | { | ||||
| #if JUCE_WINDOWS && _MSC_VER < 1400 | |||||
| #if JUCE_MSVC && _MSC_VER < 1400 | |||||
| static juce_wchar dp = std::_USE (std::locale(), std::numpunct <wchar_t>).decimal_point(); | static juce_wchar dp = std::_USE (std::locale(), std::numpunct <wchar_t>).decimal_point(); | ||||
| #else | #else | ||||
| static juce_wchar dp = std::use_facet <std::numpunct <wchar_t> > (std::locale()).decimal_point(); | static juce_wchar dp = std::use_facet <std::numpunct <wchar_t> > (std::locale()).decimal_point(); | ||||
| @@ -387,7 +387,7 @@ namespace NumberToStringConverters | |||||
| else | else | ||||
| { | { | ||||
| #if JUCE_WINDOWS | #if JUCE_WINDOWS | ||||
| #if _MSC_VER <= 1400 | |||||
| #if JUCE_MSVC && _MSC_VER <= 1400 | |||||
| len = _snwprintf (buffer, numChars, L"%.9g", n); | len = _snwprintf (buffer, numChars, L"%.9g", n); | ||||
| #else | #else | ||||
| len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); | len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); | ||||