| @@ -36,7 +36,9 @@ | |||
| */ | |||
| #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 | |||
| @@ -27,7 +27,8 @@ | |||
| //[MiscUserDefs] You can add your own user definitions and misc code here... | |||
| class DemoThumbnailComp : public Component, | |||
| public ChangeListener | |||
| public ChangeListener, | |||
| public FileDragAndDropTarget | |||
| { | |||
| public: | |||
| DemoThumbnailComp() | |||
| @@ -104,6 +105,19 @@ public: | |||
| 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; | |||
| AudioThumbnailCache thumbnailCache; | |||
| 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... | |||
| void AudioDemoPlaybackPage::showFile (const File& file) | |||
| { | |||
| loadFileIntoTransport (file); | |||
| zoomSlider->setValue (0, false, false); | |||
| thumbnail->setFile (file); | |||
| } | |||
| void AudioDemoPlaybackPage::loadFileIntoTransport (const File& audioFile) | |||
| { | |||
| // unload the previous file source and delete it.. | |||
| @@ -292,10 +314,7 @@ void AudioDemoPlaybackPage::loadFileIntoTransport (const File& audioFile) | |||
| 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&) | |||
| @@ -54,6 +54,7 @@ public: | |||
| void selectionChanged(); | |||
| void fileClicked (const File& file, const MouseEvent& e); | |||
| void fileDoubleClicked (const File& file); | |||
| void showFile (const File& file); | |||
| //[/UserMethods] | |||
| void paint (Graphics& g); | |||
| @@ -147,7 +147,7 @@ | |||
| reduce code size. | |||
| */ | |||
| #if (! defined (JUCE_USE_CDBURNER)) && ! (JUCE_WINDOWS && ! JUCE_MSVC) | |||
| #define JUCE_USE_CDBURNER 0 | |||
| #define JUCE_USE_CDBURNER 1 | |||
| #endif | |||
| /** JUCE_USE_CDREADER: Enables the audio CD reader code (Mac and Windows only). | |||
| @@ -155,7 +155,7 @@ | |||
| reduce code size. | |||
| */ | |||
| #ifndef JUCE_USE_CDREADER | |||
| #define JUCE_USE_CDREADER 0 | |||
| #define JUCE_USE_CDREADER 1 | |||
| #endif | |||
| //============================================================================= | |||
| @@ -244,7 +244,7 @@ | |||
| Carbon isn't required for a normal app, but may be needed by specialised classes like | |||
| plugin-hosts, which support older APIs. | |||
| */ | |||
| #ifndef JUCE_SUPPORT_CARBON | |||
| #if ! (defined (JUCE_SUPPORT_CARBON) || defined (__LP64__)) | |||
| #define JUCE_SUPPORT_CARBON 1 | |||
| #endif | |||
| @@ -32,6 +32,7 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "../../core/juce_PlatformUtilities.h" | |||
| #include "../../text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| static const char* const aiffFormatName = "AIFF file"; | |||
| static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; | |||
| @@ -72,14 +73,14 @@ public: | |||
| hasGotVer = true; | |||
| const int ver = input->readIntBigEndian(); | |||
| if (ver != 0 && ver != (int)0xa2805140) | |||
| if (ver != 0 && ver != (int) 0xa2805140) | |||
| break; | |||
| } | |||
| else if (type == chunkName ("COMM")) | |||
| { | |||
| hasGotType = true; | |||
| numChannels = (unsigned int)input->readShortBigEndian(); | |||
| numChannels = (unsigned int) input->readShortBigEndian(); | |||
| lengthInSamples = input->readIntBigEndian(); | |||
| bitsPerSample = input->readShortBigEndian(); | |||
| bytesPerFrame = (numChannels * bitsPerSample) >> 3; | |||
| @@ -167,318 +168,42 @@ public: | |||
| input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | |||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||
| char tempBuffer [tempBufSize]; | |||
| 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 bytesRead = input->read (tempBuffer, 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; | |||
| } | |||
| if (numSamples > 0) | |||
| { | |||
| for (int i = numDestChannels; --i >= 0;) | |||
| if (destSamples[i] != 0) | |||
| zeromem (destSamples[i] + startOffsetInDestBuffer, | |||
| sizeof (int) * numSamples); | |||
| } | |||
| return true; | |||
| } | |||
| @@ -509,6 +226,68 @@ private: | |||
| //============================================================================== | |||
| 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; | |||
| uint32 lengthInSamples, bytesWritten; | |||
| int64 headerPosition; | |||
| @@ -592,153 +371,6 @@ class AiffAudioFormatWriter : public AudioFormatWriter | |||
| 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); | |||
| } | |||
| bool AiffAudioFormat::canDoStereo() | |||
| { | |||
| return true; | |||
| } | |||
| bool AiffAudioFormat::canDoMono() | |||
| { | |||
| return true; | |||
| } | |||
| bool AiffAudioFormat::canDoStereo() { return true; } | |||
| bool AiffAudioFormat::canDoMono() { return true; } | |||
| #if JUCE_MAC | |||
| bool AiffAudioFormat::canHandleFile (const File& f) | |||
| @@ -785,8 +410,7 @@ bool AiffAudioFormat::canHandleFile (const File& f) | |||
| } | |||
| #endif | |||
| AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, | |||
| const bool deleteStreamIfOpeningFails) | |||
| AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) | |||
| { | |||
| ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream)); | |||
| @@ -801,18 +425,13 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, | |||
| AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, | |||
| double sampleRate, | |||
| unsigned int chans, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& /*metadataValues*/, | |||
| int /*qualityOptionIndex*/) | |||
| { | |||
| if (getPossibleBitDepths().contains (bitsPerSample)) | |||
| { | |||
| return new AiffAudioFormatWriter (out, | |||
| sampleRate, | |||
| chans, | |||
| bitsPerSample); | |||
| } | |||
| return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, bitsPerSample); | |||
| return 0; | |||
| } | |||
| @@ -28,6 +28,7 @@ | |||
| #include "../../io/streams/juce_InputStream.h" | |||
| #include "../../text/juce_StringPairArray.h" | |||
| #include "../dsp/juce_AudioDataConverters.h" | |||
| class AudioFormat; | |||
| @@ -218,6 +219,32 @@ public: | |||
| //============================================================================== | |||
| 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: | |||
| String formatName; | |||
| @@ -160,6 +160,26 @@ protected: | |||
| /** The output stream for Use by subclasses. */ | |||
| 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: | |||
| String formatName; | |||
| @@ -38,7 +38,6 @@ BEGIN_JUCE_NAMESPACE | |||
| static const char* const wavFormatName = "WAV file"; | |||
| static const char* const wavExtensions[] = { ".wav", ".bwf", 0 }; | |||
| //============================================================================== | |||
| const char* const WavAudioFormat::bwavDescription = "bwav description"; | |||
| const char* const WavAudioFormat::bwavOriginator = "bwav originator"; | |||
| @@ -258,17 +257,7 @@ struct ExtensibleWavSubFormat | |||
| //============================================================================== | |||
| 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: | |||
| int64 bwavChunkStart, bwavSize; | |||
| //============================================================================== | |||
| WavAudioFormatReader (InputStream* const in) | |||
| : AudioFormatReader (in, TRANS (wavFormatName)), | |||
| @@ -389,6 +378,7 @@ public: | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) | |||
| { | |||
| jassert (destSamples != 0); | |||
| const int64 samplesAvailable = lengthInSamples - startSampleInFile; | |||
| if (samplesAvailable < numSamples) | |||
| @@ -405,267 +395,55 @@ public: | |||
| input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | |||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||
| char tempBuffer [tempBufSize]; | |||
| 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 bytesRead = input->read (tempBuffer, 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; | |||
| numSamples -= numThisTime; | |||
| } | |||
| if (numSamples > 0) | |||
| { | |||
| for (int i = numDestChannels; --i >= 0;) | |||
| if (destSamples[i] != 0) | |||
| zeromem (destSamples[i] + startOffsetInDestBuffer, | |||
| sizeof (int) * numSamples); | |||
| } | |||
| return true; | |||
| } | |||
| int64 bwavChunkStart, bwavSize; | |||
| 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); } | |||
| 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: | |||
| //============================================================================== | |||
| WavAudioFormatWriter (OutputStream* const out, | |||
| @@ -700,104 +478,25 @@ public: | |||
| //============================================================================== | |||
| 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); | |||
| 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 | |||
| || ! output->write (buffer, bytes)) | |||
| || ! 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 | |||
| @@ -816,6 +515,63 @@ public: | |||
| } | |||
| 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); | |||
| } | |||
| 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, | |||
| const bool deleteStreamIfOpeningFails) | |||
| @@ -531,7 +531,7 @@ void AudioDataConverters::deinterleaveSamples (const float* const source, | |||
| } | |||
| } | |||
| /* | |||
| #if JUCE_UNIT_TESTS | |||
| #include "../../utilities/juce_UnitTest.h" | |||
| @@ -546,6 +546,12 @@ public: | |||
| struct Test5 | |||
| { | |||
| static void test (UnitTest& unitTest) | |||
| { | |||
| test (unitTest, false); | |||
| test (unitTest, true); | |||
| } | |||
| static void test (UnitTest& unitTest, bool inPlace) | |||
| { | |||
| const int numSamples = 2048; | |||
| int32 original [numSamples], converted [numSamples], reversed [numSamples]; | |||
| @@ -572,13 +578,15 @@ public: | |||
| // convert data from the source to dest format.. | |||
| ScopedPointer<AudioData::Converter> conv (new AudioData::ConverterInstance <AudioData::Pointer<F1, E1, AudioData::NonInterleaved, AudioData::Const>, | |||
| AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::NonConst> >()); | |||
| conv->convertSamples (converted, original, numSamples); | |||
| conv->convertSamples (inPlace ? reversed : converted, original, numSamples); | |||
| // ..and back again.. | |||
| conv = new AudioData::ConverterInstance <AudioData::Pointer<F2, E2, AudioData::NonInterleaved, AudioData::Const>, | |||
| 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; | |||
| @@ -615,11 +623,12 @@ public: | |||
| { | |||
| 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::Int24>::test (unitTest); | |||
| Test3 <FormatType, Endianness, AudioData::Int32>::test (unitTest); | |||
| Test3 <FormatType, Endianness, AudioData::Float32>::test (unitTest); | |||
| Test3 <FormatType, Endianness, AudioData::Int8>::test (unitTest); | |||
| } | |||
| }; | |||
| @@ -648,6 +657,6 @@ public: | |||
| static AudioConversionTests audioConversionUnitTests; | |||
| #endif | |||
| */ | |||
| END_JUCE_NAMESPACE | |||
| @@ -26,7 +26,6 @@ | |||
| #ifndef __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. | |||
| 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 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. */ | |||
| @@ -118,20 +118,19 @@ public: | |||
| : InterleavingType (numInterleavedChannels), | |||
| 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. */ | |||
| Pointer (const Pointer& other) throw() | |||
| : data (other.data) | |||
| : InterleavingType (other), | |||
| data (other.data) | |||
| { | |||
| } | |||
| Pointer& operator= (const Pointer& other) throw() | |||
| { | |||
| data = other.data; | |||
| InterleavingType::copyFrom (other); | |||
| return *this; | |||
| } | |||
| @@ -172,9 +171,12 @@ public: | |||
| 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; } | |||
| /** 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. */ | |||
| 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! | |||
| 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. */ | |||
| @@ -217,9 +238,15 @@ public: | |||
| /** Returns true if the format is big-endian. */ | |||
| 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. */ | |||
| 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. | |||
| 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, | |||
| @@ -227,6 +254,9 @@ public: | |||
| */ | |||
| static int get32BitResolution() throw() { return (int) SampleFormat::resolution; } | |||
| /** Returns a pointer to the underlying data. */ | |||
| const void* getRawData() const throw() { return data.data; } | |||
| private: | |||
| //============================================================================== | |||
| SampleFormat data; | |||
| @@ -248,9 +278,15 @@ public: | |||
| public: | |||
| 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 | |||
| { | |||
| public: | |||
| ConverterInstance() {} | |||
| ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1) | |||
| : sourceChannels (numSourceChannels), destChannels (numDestChannels) | |||
| {} | |||
| ~ConverterInstance() {} | |||
| 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); | |||
| } | |||
| private: | |||
| ConverterInstance (const ConverterInstance&); | |||
| ConverterInstance& operator= (const ConverterInstance&); | |||
| const int sourceChannels, destChannels; | |||
| }; | |||
| }; | |||
| @@ -324,18 +375,45 @@ public: | |||
| inline void skip (int numSamples) throw() { data += numSamples; } | |||
| inline float getAsFloatLE() const throw() { return (float) (*data * (1.0 / (1.0 + maxValue))); } | |||
| 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 int32 getAsInt32LE() const throw() { return (int) (*data << 24); } | |||
| inline int32 getAsInt32BE() const throw() { return getAsInt32LE(); } | |||
| inline void setAsInt32LE (int newValue) throw() { *data = (int8) (newValue >> 24); } | |||
| 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 copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | |||
| inline void copyFromSameType (Int8& source) throw() { *data = *source.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 | |||
| @@ -353,12 +431,14 @@ public: | |||
| 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 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 copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | |||
| inline void copyFromSameType (Int16& source) throw() { *data = *source.data; } | |||
| uint16* data; | |||
| enum { maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; | |||
| enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; | |||
| }; | |||
| class AudioData::Int24 | |||
| @@ -376,12 +456,14 @@ public: | |||
| inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::bigEndian24Bit (data) << 8; } | |||
| inline void setAsInt32LE (int32 newValue) throw() { ByteOrder::littleEndian24BitToChars (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 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]; } | |||
| char* data; | |||
| enum { maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; | |||
| enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; | |||
| }; | |||
| class AudioData::Int32 | |||
| @@ -399,12 +481,14 @@ public: | |||
| inline int32 getAsInt32BE() const throw() { return (int32) ByteOrder::swapIfLittleEndian (*data); } | |||
| inline void setAsInt32LE (int32 newValue) throw() { *data = ByteOrder::swapIfBigEndian ((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 copyFromBE (SourceType& source) throw() { setAsInt32BE (source.getAsInt32()); } | |||
| inline void copyFromSameType (Int32& source) throw() { *data = *source.data; } | |||
| uint32* data; | |||
| enum { maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; | |||
| enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; | |||
| }; | |||
| 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 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 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 copyFromBE (SourceType& source) throw() { setAsFloatBE (source.getAsFloat()); } | |||
| inline void copyFromSameType (Float32& source) throw() { *data = *source.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: | |||
| inline NonInterleaved() throw() {} | |||
| inline NonInterleaved (const NonInterleaved&) 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 }; | |||
| }; | |||
| @@ -452,14 +543,15 @@ class AudioData::Interleaved | |||
| { | |||
| public: | |||
| inline Interleaved() throw() : numInterleavedChannels (1) {} | |||
| inline Interleaved (const Interleaved& other) throw() : numInterleavedChannels (other.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 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 }; | |||
| private: | |||
| Interleaved& operator= (const Interleaved&); | |||
| }; | |||
| //============================================================================== | |||
| @@ -481,8 +573,6 @@ public: | |||
| #endif | |||
| #endif | |||
| //============================================================================== | |||
| /** | |||
| 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. */ | |||
| 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. */ | |||
| 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. */ | |||
| void setPlayConfigDetails (int numIns, int numOuts, | |||
| double sampleRate, | |||
| @@ -122,6 +122,12 @@ public: | |||
| */ | |||
| 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. | |||
| Obviously this will cause problems if the array hasn't been initialised, because it'll | |||
| be referencing a null pointer. | |||
| @@ -63,6 +63,7 @@ public: | |||
| class var::VariantType_Void : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Void() {} | |||
| static const VariantType_Void* getInstance() { static const VariantType_Void i; return &i; } | |||
| bool isVoid() const throw() { return true; } | |||
| @@ -74,6 +75,7 @@ public: | |||
| class var::VariantType_Int : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Int() {} | |||
| static const VariantType_Int* getInstance() { static const VariantType_Int i; return &i; } | |||
| int toInt (const ValueUnion& data) const { return data.intValue; }; | |||
| @@ -100,6 +102,7 @@ public: | |||
| class var::VariantType_Double : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Double() {} | |||
| static const VariantType_Double* getInstance() { static const VariantType_Double i; return &i; } | |||
| int toInt (const ValueUnion& data) const { return (int) data.doubleValue; }; | |||
| @@ -126,6 +129,7 @@ public: | |||
| class var::VariantType_Bool : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Bool() {} | |||
| static const VariantType_Bool* getInstance() { static const VariantType_Bool i; return &i; } | |||
| int toInt (const ValueUnion& data) const { return data.boolValue ? 1 : 0; }; | |||
| @@ -151,6 +155,7 @@ public: | |||
| class var::VariantType_String : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_String() {} | |||
| static const VariantType_String* getInstance() { static const VariantType_String i; return &i; } | |||
| void cleanUp (ValueUnion& data) const throw() { delete data.stringValue; } | |||
| @@ -185,6 +190,7 @@ public: | |||
| class var::VariantType_Object : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Object() {} | |||
| 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(); } | |||
| @@ -212,6 +218,7 @@ public: | |||
| class var::VariantType_Method : public var::VariantType | |||
| { | |||
| public: | |||
| VariantType_Method() {} | |||
| static const VariantType_Method* getInstance() { static const VariantType_Method i; return &i; } | |||
| const String toString (const ValueUnion&) const { return "Method"; } | |||
| @@ -33,7 +33,7 @@ | |||
| */ | |||
| #define JUCE_MAJOR_VERSION 1 | |||
| #define JUCE_MINOR_VERSION 52 | |||
| #define JUCE_BUILDNUMBER 69 | |||
| #define JUCE_BUILDNUMBER 70 | |||
| /** Current Juce version number. | |||
| @@ -525,7 +525,7 @@ public: | |||
| @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. | |||
| @@ -82,7 +82,7 @@ static void getDeviceProperties (const String& deviceID, | |||
| if (snd_ctl_pcm_info (handle, info) >= 0) | |||
| { | |||
| 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); | |||
| getDeviceSampleRates (pcmHandle, rates); | |||
| @@ -96,7 +96,7 @@ static void getDeviceProperties (const String& deviceID, | |||
| if (snd_ctl_pcm_info (handle, info) >= 0) | |||
| { | |||
| 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); | |||
| @@ -115,14 +115,12 @@ static void getDeviceProperties (const String& deviceID, | |||
| class ALSADevice | |||
| { | |||
| public: | |||
| ALSADevice (const String& deviceID, | |||
| const bool forInput) | |||
| ALSADevice (const String& deviceID, bool forInput) | |||
| : handle (0), | |||
| bitDepth (16), | |||
| numChannelsRunning (0), | |||
| latency (0), | |||
| isInput (forInput), | |||
| sampleFormat (AudioDataConverters::int16LE) | |||
| isInput (forInput) | |||
| { | |||
| failed (snd_pcm_open (&handle, deviceID.toUTF8(), | |||
| forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | |||
| @@ -156,22 +154,26 @@ public: | |||
| 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; | |||
| 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) | |||
| { | |||
| 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; | |||
| } | |||
| } | |||
| @@ -235,48 +237,44 @@ public: | |||
| } | |||
| //============================================================================== | |||
| bool write (AudioSampleBuffer& outputChannelBuffer, const int numSamples) | |||
| bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples) | |||
| { | |||
| jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); | |||
| float** const data = outputChannelBuffer.getArrayOfChannels(); | |||
| snd_pcm_sframes_t numDone = 0; | |||
| if (isInterleaved) | |||
| { | |||
| 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 | |||
| { | |||
| 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; | |||
| } | |||
| else if (numDone != -ESTRPIPE) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| bool read (AudioSampleBuffer& inputChannelBuffer, const int numSamples) | |||
| bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples) | |||
| { | |||
| jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); | |||
| float** const data = inputChannelBuffer.getArrayOfChannels(); | |||
| @@ -284,9 +282,8 @@ public: | |||
| if (isInterleaved) | |||
| { | |||
| 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)) | |||
| { | |||
| @@ -299,8 +296,8 @@ public: | |||
| 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 | |||
| { | |||
| @@ -310,8 +307,7 @@ public: | |||
| return false; | |||
| 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; | |||
| @@ -329,7 +325,48 @@ private: | |||
| const bool isInput; | |||
| bool isInterleaved; | |||
| 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) | |||
| @@ -522,7 +559,7 @@ public: | |||
| { | |||
| if (inputDevice != 0) | |||
| { | |||
| if (! inputDevice->read (inputChannelBuffer, bufferSize)) | |||
| if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize)) | |||
| { | |||
| DBG ("ALSA: read failure"); | |||
| break; | |||
| @@ -560,7 +597,7 @@ public: | |||
| failed (snd_pcm_avail_update (outputDevice->handle)); | |||
| if (! outputDevice->write (outputChannelBuffer, bufferSize)) | |||
| if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize)) | |||
| { | |||
| DBG ("ALSA: write failure"); | |||
| break; | |||
| @@ -279,10 +279,19 @@ END_JUCE_NAMESPACE | |||
| 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; | |||
| } | |||
| @@ -306,7 +306,6 @@ public: | |||
| size = sizeof (lat); | |||
| pa.mSelector = kAudioDevicePropertyLatency; | |||
| pa.mScope = kAudioDevicePropertyScopeInput; | |||
| //if (AudioDeviceGetProperty (deviceID, 0, true, kAudioDevicePropertyLatency, &size, &lat) == noErr) | |||
| if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) | |||
| 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! | |||
| return jmax (-1, c - 29); | |||
| return jmax (-1, (int) c - 29); | |||
| } | |||
| private: | |||
| @@ -82,7 +82,7 @@ public: | |||
| JUCEApplication::appWillTerminateByForce(); | |||
| } | |||
| virtual BOOL openFile (const NSString* filename) | |||
| virtual BOOL openFile (NSString* filename) | |||
| { | |||
| if (JUCEApplication::getInstance() != 0) | |||
| { | |||
| @@ -2378,11 +2378,16 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) | |||
| 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); | |||
| @@ -43,7 +43,7 @@ DynamicLibraryLoader::~DynamicLibraryLoader() | |||
| 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) | |||
| { | |||
| 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; | |||
| HANDLE clientEvent; | |||
| BigInteger channels; | |||
| AudioDataConverters::DataFormat dataFormat; | |||
| Array <int> channelMaps; | |||
| UINT32 actualBufferSize; | |||
| int bytesPerSample; | |||
| virtual void updateFormat (bool isFloat) = 0; | |||
| private: | |||
| const ComSmartPtr <IAudioClient> createClient() | |||
| { | |||
| @@ -311,10 +312,8 @@ private: | |||
| actualNumChannels = format.Format.nChannels; | |||
| const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | |||
| bytesPerSample = format.Format.wBitsPerSample / 8; | |||
| dataFormat = isFloat ? AudioDataConverters::float32LE | |||
| : (bytesPerSample == 4 ? AudioDataConverters::int32LE | |||
| : ((bytesPerSample == 3 ? AudioDataConverters::int24LE | |||
| : AudioDataConverters::int16LE))); | |||
| updateFormat (isFloat); | |||
| return true; | |||
| } | |||
| @@ -356,6 +355,20 @@ public: | |||
| 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) | |||
| { | |||
| if (numChannels <= 0) | |||
| @@ -370,31 +383,7 @@ public: | |||
| const int samplesToDo = jmin (bufferSize, (int) reservoirSize); | |||
| 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; | |||
| offset += samplesToDo; | |||
| @@ -424,31 +413,7 @@ public: | |||
| const int samplesToDo = jmin (bufferSize, (int) numSamplesAvailable); | |||
| 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; | |||
| offset += samplesToDo; | |||
| @@ -469,6 +434,7 @@ public: | |||
| ComSmartPtr <IAudioCaptureClient> captureClient; | |||
| MemoryBlock reservoir; | |||
| int reservoirSize, reservoirCapacity; | |||
| ScopedPointer <AudioData::Converter> converter; | |||
| private: | |||
| WASAPIInputDevice (const WASAPIInputDevice&); | |||
| @@ -501,6 +467,20 @@ public: | |||
| 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) | |||
| { | |||
| if (numChannels <= 0) | |||
| @@ -530,31 +510,7 @@ public: | |||
| if (OK (renderClient->GetBuffer (samplesToDo, &outputData))) | |||
| { | |||
| 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); | |||
| @@ -565,6 +521,7 @@ public: | |||
| } | |||
| ComSmartPtr <IAudioRenderClient> renderClient; | |||
| ScopedPointer <AudioData::Converter> converter; | |||
| private: | |||
| WASAPIOutputDevice (const WASAPIOutputDevice&); | |||
| @@ -350,7 +350,7 @@ namespace NumberToStringConverters | |||
| 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(); | |||
| #else | |||
| static juce_wchar dp = std::use_facet <std::numpunct <wchar_t> > (std::locale()).decimal_point(); | |||
| @@ -387,7 +387,7 @@ namespace NumberToStringConverters | |||
| else | |||
| { | |||
| #if JUCE_WINDOWS | |||
| #if _MSC_VER <= 1400 | |||
| #if JUCE_MSVC && _MSC_VER <= 1400 | |||
| len = _snwprintf (buffer, numChars, L"%.9g", n); | |||
| #else | |||
| len = _snwprintf_s (buffer, numChars, _TRUNCATE, L"%.9g", n); | |||