|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2013 - Raw Material Software Ltd.
-
- Permission is granted to use this software under the terms of either:
- a) the GPL v2 (or any later version)
- b) the Affero GPL v3
-
- Details of these licenses can be found at: www.gnu.org/licenses
-
- JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
- ------------------------------------------------------------------------------
-
- To release a closed-source product which uses JUCE, commercial licenses are
- available: visit www.juce.com for more information.
-
- ==============================================================================
- */
-
- const char* const openSLTypeName = "Android OpenSL";
-
- bool isOpenSLAvailable()
- {
- DynamicLibrary library;
- return library.open ("libOpenSLES.so");
- }
-
- const unsigned short openSLRates[] = { 8000, 16000, 32000, 44100, 48000 };
- const unsigned short openSLBufferSizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size
-
- //==============================================================================
- class OpenSLAudioIODevice : public AudioIODevice,
- public Thread
- {
- public:
- OpenSLAudioIODevice (const String& deviceName)
- : AudioIODevice (deviceName, openSLTypeName),
- Thread ("OpenSL"),
- callback (nullptr), sampleRate (0), deviceOpen (false),
- inputBuffer (2, 2), outputBuffer (2, 2)
- {
- // OpenSL has piss-poor support for determining latency, so the only way I can find to
- // get a number for this is by asking the AudioTrack/AudioRecord classes..
- AndroidAudioIODevice javaDevice (String::empty);
-
- // this is a total guess about how to calculate the latency, but seems to vaguely agree
- // with the devices I've tested.. YMMV
- inputLatency = ((javaDevice.minBufferSizeIn * 2) / 3);
- outputLatency = ((javaDevice.minBufferSizeOut * 2) / 3);
-
- const int longestLatency = jmax (inputLatency, outputLatency);
- const int totalLatency = inputLatency + outputLatency;
- inputLatency = ((longestLatency * inputLatency) / totalLatency) & ~15;
- outputLatency = ((longestLatency * outputLatency) / totalLatency) & ~15;
- }
-
- ~OpenSLAudioIODevice()
- {
- close();
- }
-
- bool openedOk() const { return engine.outputMixObject != nullptr; }
-
- StringArray getOutputChannelNames()
- {
- StringArray s;
- s.add ("Left");
- s.add ("Right");
- return s;
- }
-
- StringArray getInputChannelNames()
- {
- StringArray s;
- s.add ("Audio Input");
- return s;
- }
-
- int getNumSampleRates() { return numElementsInArray (openSLRates); }
-
- double getSampleRate (int index)
- {
- jassert (index >= 0 && index < getNumSampleRates());
- return (int) openSLRates [index];
- }
-
- int getDefaultBufferSize() { return 1024; }
- int getNumBufferSizesAvailable() { return numElementsInArray (openSLBufferSizes); }
-
- int getBufferSizeSamples (int index)
- {
- jassert (index >= 0 && index < getNumBufferSizesAvailable());
- return (int) openSLBufferSizes [index];
- }
-
- String open (const BigInteger& inputChannels,
- const BigInteger& outputChannels,
- double requestedSampleRate,
- int bufferSize)
- {
- close();
-
- lastError = String::empty;
- sampleRate = (int) requestedSampleRate;
-
- int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
-
- activeOutputChans = outputChannels;
- activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
- numOutputChannels = activeOutputChans.countNumberOfSetBits();
-
- activeInputChans = inputChannels;
- activeInputChans.setRange (1, activeInputChans.getHighestBit(), false);
- numInputChannels = activeInputChans.countNumberOfSetBits();
-
- actualBufferSize = preferredBufferSize;
-
- inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize);
- outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize);
- outputBuffer.clear();
-
- recorder = engine.createRecorder (numInputChannels, sampleRate);
- player = engine.createPlayer (numOutputChannels, sampleRate);
-
- startThread (8);
-
- deviceOpen = true;
- return lastError;
- }
-
- void close()
- {
- stop();
- stopThread (6000);
- deviceOpen = false;
- recorder = nullptr;
- player = nullptr;
- }
-
- int getOutputLatencyInSamples() { return outputLatency; }
- int getInputLatencyInSamples() { return inputLatency; }
- bool isOpen() { return deviceOpen; }
- int getCurrentBufferSizeSamples() { return actualBufferSize; }
- int getCurrentBitDepth() { return 16; }
- double getCurrentSampleRate() { return sampleRate; }
- BigInteger getActiveOutputChannels() const { return activeOutputChans; }
- BigInteger getActiveInputChannels() const { return activeInputChans; }
- String getLastError() { return lastError; }
- bool isPlaying() { return callback != nullptr; }
-
- void start (AudioIODeviceCallback* newCallback)
- {
- stop();
-
- if (deviceOpen && callback != newCallback)
- {
- if (newCallback != nullptr)
- newCallback->audioDeviceAboutToStart (this);
-
- setCallback (newCallback);
- }
- }
-
- void stop()
- {
- if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr))
- oldCallback->audioDeviceStopped();
- }
-
- void run() override
- {
- if (recorder != nullptr) recorder->start();
- if (player != nullptr) player->start();
-
- while (! threadShouldExit())
- {
- if (player != nullptr) player->writeBuffer (outputBuffer, *this);
- if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this);
-
- const ScopedLock sl (callbackLock);
-
- if (callback != nullptr)
- {
- callback->audioDeviceIOCallback (numInputChannels > 0 ? (const float**) inputBuffer.getArrayOfChannels() : nullptr,
- numInputChannels,
- numOutputChannels > 0 ? outputBuffer.getArrayOfChannels() : nullptr,
- numOutputChannels,
- actualBufferSize);
- }
- else
- {
- outputBuffer.clear();
- }
- }
- }
-
- private:
- //==================================================================================================
- CriticalSection callbackLock;
- AudioIODeviceCallback* callback;
- int actualBufferSize, sampleRate;
- int inputLatency, outputLatency;
- bool deviceOpen;
- String lastError;
- BigInteger activeOutputChans, activeInputChans;
- int numInputChannels, numOutputChannels;
- AudioSampleBuffer inputBuffer, outputBuffer;
- struct Player;
- struct Recorder;
-
- AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
- {
- const ScopedLock sl (callbackLock);
- AudioIODeviceCallback* const oldCallback = callback;
- callback = newCallback;
- return oldCallback;
- }
-
- //==================================================================================================
- struct Engine
- {
- Engine()
- : engineObject (nullptr), engineInterface (nullptr), outputMixObject (nullptr)
- {
- if (library.open ("libOpenSLES.so"))
- {
- typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*);
-
- if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine"))
- {
- check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr));
-
- SLInterfaceID* SL_IID_ENGINE = (SLInterfaceID*) library.getFunction ("SL_IID_ENGINE");
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
- SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY");
- SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD");
-
- check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE));
- check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface));
-
- check ((*engineInterface)->CreateOutputMix (engineInterface, &outputMixObject, 0, nullptr, nullptr));
- check ((*outputMixObject)->Realize (outputMixObject, SL_BOOLEAN_FALSE));
- }
- }
- }
-
- ~Engine()
- {
- if (outputMixObject != nullptr) (*outputMixObject)->Destroy (outputMixObject);
- if (engineObject != nullptr) (*engineObject)->Destroy (engineObject);
- }
-
- Player* createPlayer (const int numChannels, const int sampleRate)
- {
- if (numChannels <= 0)
- return nullptr;
-
- ScopedPointer<Player> player (new Player (numChannels, sampleRate, *this));
- return player->openedOk() ? player.release() : nullptr;
- }
-
- Recorder* createRecorder (const int numChannels, const int sampleRate)
- {
- if (numChannels <= 0)
- return nullptr;
-
- ScopedPointer<Recorder> recorder (new Recorder (numChannels, sampleRate, *this));
- return recorder->openedOk() ? recorder.release() : nullptr;
- }
-
- SLObjectItf engineObject;
- SLEngineItf engineInterface;
- SLObjectItf outputMixObject;
-
- SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
- SLInterfaceID* SL_IID_PLAY;
- SLInterfaceID* SL_IID_RECORD;
-
- private:
- DynamicLibrary library;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine)
- };
-
- //==================================================================================================
- struct BufferList
- {
- BufferList (const int numChannels_)
- : numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0)
- {
- }
-
- int16* waitForFreeBuffer (Thread& threadToCheck)
- {
- while (numBlocksOut.get() == numBuffers)
- {
- dataArrived.wait (1);
-
- if (threadToCheck.threadShouldExit())
- return nullptr;
- }
-
- return getNextBuffer();
- }
-
- int16* getNextBuffer()
- {
- if (++nextBlock == numBuffers)
- nextBlock = 0;
-
- return bufferSpace + nextBlock * numChannels * numSamples;
- }
-
- void bufferReturned() { --numBlocksOut; dataArrived.signal(); }
- void bufferSent() { ++numBlocksOut; dataArrived.signal(); }
-
- int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); }
-
- const int numChannels;
- enum { numSamples = 256, numBuffers = 16 };
-
- private:
- HeapBlock<int16> bufferSpace;
- int nextBlock;
- Atomic<int> numBlocksOut;
- WaitableEvent dataArrived;
- };
-
- //==================================================================================================
- struct Player
- {
- Player (int numChannels, int sampleRate, Engine& engine)
- : playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr),
- bufferList (numChannels)
- {
- jassert (numChannels == 2);
-
- SLDataFormat_PCM pcmFormat =
- {
- SL_DATAFORMAT_PCM,
- numChannels,
- sampleRate * 1000, // (sample rate units are millihertz)
- SL_PCMSAMPLEFORMAT_FIXED_16,
- SL_PCMSAMPLEFORMAT_FIXED_16,
- SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
- SL_BYTEORDER_LITTLEENDIAN
- };
-
- SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
- SLDataSource audioSrc = { &bufferQueue, &pcmFormat };
-
- SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject };
- SLDataSink audioSink = { &outputMix, nullptr };
-
- // (SL_IID_BUFFERQUEUE is not guaranteed to remain future-proof, so use SL_IID_ANDROIDSIMPLEBUFFERQUEUE)
- const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
- const SLboolean flags[] = { SL_BOOLEAN_TRUE };
-
- check ((*engine.engineInterface)->CreateAudioPlayer (engine.engineInterface, &playerObject, &audioSrc, &audioSink,
- 1, interfaceIDs, flags));
-
- check ((*playerObject)->Realize (playerObject, SL_BOOLEAN_FALSE));
- check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_PLAY, &playerPlay));
- check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerBufferQueue));
- check ((*playerBufferQueue)->RegisterCallback (playerBufferQueue, staticCallback, this));
- }
-
- ~Player()
- {
- if (playerPlay != nullptr)
- check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_STOPPED));
-
- if (playerBufferQueue != nullptr)
- check ((*playerBufferQueue)->Clear (playerBufferQueue));
-
- if (playerObject != nullptr)
- (*playerObject)->Destroy (playerObject);
- }
-
- bool openedOk() const noexcept { return playerBufferQueue != nullptr; }
-
- void start()
- {
- jassert (openedOk());
- check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING));
- }
-
- void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread)
- {
- jassert (buffer.getNumChannels() == bufferList.numChannels);
- jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
-
- int offset = 0;
- int numSamples = buffer.getNumSamples();
-
- while (numSamples > 0)
- {
- int16* const destBuffer = bufferList.waitForFreeBuffer (thread);
-
- if (destBuffer == nullptr)
- break;
-
- for (int i = 0; i < bufferList.numChannels; ++i)
- {
- typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> DstSampleType;
- typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SrcSampleType;
-
- DstSampleType dstData (destBuffer + i, bufferList.numChannels);
- SrcSampleType srcData (buffer.getSampleData (i, offset));
- dstData.convertSamples (srcData, bufferList.numSamples);
- }
-
- check ((*playerBufferQueue)->Enqueue (playerBufferQueue, destBuffer, bufferList.getBufferSizeBytes()));
- bufferList.bufferSent();
-
- numSamples -= bufferList.numSamples;
- offset += bufferList.numSamples;
- }
- }
-
- private:
- SLObjectItf playerObject;
- SLPlayItf playerPlay;
- SLAndroidSimpleBufferQueueItf playerBufferQueue;
-
- BufferList bufferList;
-
- static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
- {
- jassert (queue == static_cast <Player*> (context)->playerBufferQueue); (void) queue;
- static_cast <Player*> (context)->bufferList.bufferReturned();
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player)
- };
-
- //==================================================================================================
- struct Recorder
- {
- Recorder (int numChannels, int sampleRate, Engine& engine)
- : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr),
- bufferList (numChannels)
- {
- jassert (numChannels == 1); // STEREO doesn't always work!!
-
- SLDataFormat_PCM pcmFormat =
- {
- SL_DATAFORMAT_PCM,
- numChannels,
- sampleRate * 1000, // (sample rate units are millihertz)
- SL_PCMSAMPLEFORMAT_FIXED_16,
- SL_PCMSAMPLEFORMAT_FIXED_16,
- (numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT),
- SL_BYTEORDER_LITTLEENDIAN
- };
-
- SLDataLocator_IODevice ioDevice = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr };
- SLDataSource audioSrc = { &ioDevice, nullptr };
-
- SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
- SLDataSink audioSink = { &bufferQueue, &pcmFormat };
-
- const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
- const SLboolean flags[] = { SL_BOOLEAN_TRUE };
-
- if (check ((*engine.engineInterface)->CreateAudioRecorder (engine.engineInterface, &recorderObject, &audioSrc,
- &audioSink, 1, interfaceIDs, flags)))
- {
- if (check ((*recorderObject)->Realize (recorderObject, SL_BOOLEAN_FALSE)))
- {
- check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord));
- check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue));
- check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this));
- check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
-
- for (int i = bufferList.numBuffers; --i >= 0;)
- {
- int16* const buffer = bufferList.getNextBuffer();
- jassert (buffer != nullptr);
- enqueueBuffer (buffer);
- }
- }
- }
- }
-
- ~Recorder()
- {
- if (recorderRecord != nullptr)
- check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
-
- if (recorderBufferQueue != nullptr)
- check ((*recorderBufferQueue)->Clear (recorderBufferQueue));
-
- if (recorderObject != nullptr)
- (*recorderObject)->Destroy (recorderObject);
- }
-
- bool openedOk() const noexcept { return recorderBufferQueue != nullptr; }
-
- void start()
- {
- jassert (openedOk());
- check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_RECORDING));
- }
-
- void readNextBlock (AudioSampleBuffer& buffer, Thread& thread)
- {
- jassert (buffer.getNumChannels() == bufferList.numChannels);
- jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
- jassert ((buffer.getNumSamples() % bufferList.numSamples) == 0);
-
- int offset = 0;
- int numSamples = buffer.getNumSamples();
-
- while (numSamples > 0)
- {
- int16* const srcBuffer = bufferList.waitForFreeBuffer (thread);
-
- if (srcBuffer == nullptr)
- break;
-
- for (int i = 0; i < bufferList.numChannels; ++i)
- {
- typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DstSampleType;
- typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SrcSampleType;
-
- DstSampleType dstData (buffer.getSampleData (i, offset));
- SrcSampleType srcData (srcBuffer + i, bufferList.numChannels);
- dstData.convertSamples (srcData, bufferList.numSamples);
- }
-
- enqueueBuffer (srcBuffer);
-
- numSamples -= bufferList.numSamples;
- offset += bufferList.numSamples;
- }
- }
-
- private:
- SLObjectItf recorderObject;
- SLRecordItf recorderRecord;
- SLAndroidSimpleBufferQueueItf recorderBufferQueue;
-
- BufferList bufferList;
-
- void enqueueBuffer (int16* buffer)
- {
- check ((*recorderBufferQueue)->Enqueue (recorderBufferQueue, buffer, bufferList.getBufferSizeBytes()));
- bufferList.bufferSent();
- }
-
- static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
- {
- jassert (queue == static_cast <Recorder*> (context)->recorderBufferQueue); (void) queue;
- static_cast <Recorder*> (context)->bufferList.bufferReturned();
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder)
- };
-
-
- //==============================================================================
- Engine engine;
-
- ScopedPointer<Player> player;
- ScopedPointer<Recorder> recorder;
-
- //==============================================================================
- static bool check (const SLresult result)
- {
- jassert (result == SL_RESULT_SUCCESS);
- return result == SL_RESULT_SUCCESS;
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice)
- };
-
-
- //==============================================================================
- class OpenSLAudioDeviceType : public AudioIODeviceType
- {
- public:
- OpenSLAudioDeviceType() : AudioIODeviceType (openSLTypeName) {}
-
- //==============================================================================
- void scanForDevices() {}
- StringArray getDeviceNames (bool wantInputNames) const { return StringArray (openSLTypeName); }
- int getDefaultDeviceIndex (bool forInput) const { return 0; }
- int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; }
- bool hasSeparateInputsAndOutputs() const { return false; }
-
- AudioIODevice* createDevice (const String& outputDeviceName,
- const String& inputDeviceName)
- {
- ScopedPointer<OpenSLAudioIODevice> dev;
-
- if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
- {
- dev = new OpenSLAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
- : inputDeviceName);
- if (! dev->openedOk())
- dev = nullptr;
- }
-
- return dev.release();
- }
-
- private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType)
- };
-
-
- //==============================================================================
- AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES()
- {
- return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr;
- }
|