| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library - "Jules' Utility Class Extensions"
 -    Copyright 2004-11 by Raw Material Software Ltd.
 - 
 -   ------------------------------------------------------------------------------
 - 
 -    JUCE can be redistributed and/or modified under the terms of the GNU General
 -    Public License (Version 2), as published by the Free Software Foundation.
 -    A copy of the license is included in the JUCE distribution, or can be found
 -    online 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.rawmaterialsoftware.com/juce for more information.
 - 
 -   ==============================================================================
 - */
 - 
 - //==============================================================================
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
 -  STATICMETHOD (getMinBufferSize,            "getMinBufferSize",             "(III)I") \
 -  STATICMETHOD (getNativeOutputSampleRate,   "getNativeOutputSampleRate",    "(I)I") \
 -  METHOD (constructor,   "<init>",   "(IIIIII)V") \
 -  METHOD (getState,      "getState", "()I") \
 -  METHOD (play,          "play",     "()V") \
 -  METHOD (stop,          "stop",     "()V") \
 -  METHOD (release,       "release",  "()V") \
 -  METHOD (flush,         "flush",    "()V") \
 -  METHOD (write,         "write",    "([SII)I") \
 - 
 - DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack");
 - #undef JNI_CLASS_MEMBERS
 - 
 - //==============================================================================
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
 -  STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
 -  METHOD (constructor,       "<init>",           "(IIIII)V") \
 -  METHOD (getState,          "getState",         "()I") \
 -  METHOD (startRecording,    "startRecording",   "()V") \
 -  METHOD (stop,              "stop",             "()V") \
 -  METHOD (read,              "read",             "([SII)I") \
 -  METHOD (release,           "release",          "()V") \
 - 
 - DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord");
 - #undef JNI_CLASS_MEMBERS
 - 
 - //==============================================================================
 - enum
 - {
 -     CHANNEL_OUT_STEREO  = 12,
 -     CHANNEL_IN_STEREO   = 12,
 -     CHANNEL_IN_MONO     = 16,
 -     ENCODING_PCM_16BIT  = 2,
 -     STREAM_MUSIC        = 3,
 -     MODE_STREAM         = 1,
 -     STATE_UNINITIALIZED = 0
 - };
 - 
 - const char* const javaAudioTypeName = "Android Audio";
 - 
 - //==============================================================================
 - class AndroidAudioIODevice  : public AudioIODevice,
 -                               public Thread
 - {
 - public:
 -     //==============================================================================
 -     AndroidAudioIODevice (const String& deviceName)
 -         : AudioIODevice (deviceName, javaAudioTypeName),
 -           Thread ("audio"),
 -           minBufferSizeOut (0), minBufferSizeIn (0), callback (0), sampleRate (0),
 -           numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2),
 -           numClientOutputChannels (0), numDeviceOutputChannels (0),
 -           actualBufferSize (0), isRunning (false),
 -           outputChannelBuffer (1, 1),
 -           inputChannelBuffer (1, 1)
 -     {
 -         JNIEnv* env = getEnv();
 -         sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM);
 - 
 -         minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack,  AudioTrack.getMinBufferSize,  sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT);
 -         minBufferSizeIn  = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO,  ENCODING_PCM_16BIT);
 - 
 -         if (minBufferSizeIn <= 0)
 -         {
 -             minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT);
 - 
 -             if (minBufferSizeIn > 0)
 -                 numDeviceInputChannelsAvailable = 1;
 -             else
 -                 numDeviceInputChannelsAvailable = 0;
 -         }
 - 
 -         DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; "
 -               << sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable);
 -     }
 - 
 -     ~AndroidAudioIODevice()
 -     {
 -         close();
 -     }
 - 
 -     StringArray getOutputChannelNames()
 -     {
 -         StringArray s;
 -         s.add ("Left");
 -         s.add ("Right");
 -         return s;
 -     }
 - 
 -     StringArray getInputChannelNames()
 -     {
 -         StringArray s;
 - 
 -         if (numDeviceInputChannelsAvailable == 2)
 -         {
 -             s.add ("Left");
 -             s.add ("Right");
 -         }
 -         else if (numDeviceInputChannelsAvailable == 1)
 -         {
 -             s.add ("Audio Input");
 -         }
 - 
 -         return s;
 -     }
 - 
 -     int getNumSampleRates()             { return 1;}
 -     double getSampleRate (int index)    { return sampleRate; }
 - 
 -     int getDefaultBufferSize()          { return 2048; }
 -     int getNumBufferSizesAvailable()    { return 50; }
 - 
 -     int getBufferSizeSamples (int index)
 -     {
 -         int n = 16;
 -         for (int i = 0; i < index; ++i)
 -             n += n < 64 ? 16
 -                         : (n < 512 ? 32
 -                                    : (n < 1024 ? 64
 -                                                : (n < 2048 ? 128 : 256)));
 - 
 -         return n;
 -     }
 - 
 - 
 -     String open (const BigInteger& inputChannels,
 -                  const BigInteger& outputChannels,
 -                  double requestedSampleRate,
 -                  int bufferSize)
 -     {
 -         close();
 - 
 -         if (sampleRate != (int) requestedSampleRate)
 -             return "Sample rate not allowed";
 - 
 -         lastError = String::empty;
 -         int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
 - 
 -         numDeviceInputChannels = 0;
 -         numDeviceOutputChannels = 0;
 - 
 -         activeOutputChans = outputChannels;
 -         activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
 -         numClientOutputChannels = activeOutputChans.countNumberOfSetBits();
 - 
 -         activeInputChans = inputChannels;
 -         activeInputChans.setRange (2, activeInputChans.getHighestBit(), false);
 -         numClientInputChannels = activeInputChans.countNumberOfSetBits();
 - 
 -         actualBufferSize = preferredBufferSize;
 -         inputChannelBuffer.setSize (2, actualBufferSize);
 -         inputChannelBuffer.clear();
 -         outputChannelBuffer.setSize (2, actualBufferSize);
 -         outputChannelBuffer.clear();
 - 
 -         JNIEnv* env = getEnv();
 - 
 -         if (numClientOutputChannels > 0)
 -         {
 -             numDeviceOutputChannels = 2;
 -             outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor,
 -                                                       STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
 -                                                       (jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM));
 - 
 -             if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED)
 -                 isRunning = true;
 -             else
 -                 outputDevice.clear(); // failed to open the device
 -         }
 - 
 -         if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0)
 -         {
 -             numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
 -             inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor,
 -                                                      0 /* (default audio source) */, sampleRate,
 -                                                      numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
 -                                                      ENCODING_PCM_16BIT,
 -                                                      (jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16))));
 - 
 -             if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED)
 -                 isRunning = true;
 -             else
 -                 inputDevice.clear(); // failed to open the device
 -         }
 - 
 -         if (isRunning)
 -         {
 -             if (outputDevice != nullptr)
 -                 env->CallVoidMethod (outputDevice, AudioTrack.play);
 - 
 -             if (inputDevice != nullptr)
 -                 env->CallVoidMethod (inputDevice, AudioRecord.startRecording);
 - 
 -             startThread (8);
 -         }
 -         else
 -         {
 -             closeDevices();
 -         }
 - 
 -         return lastError;
 -     }
 - 
 -     void close()
 -     {
 -         if (isRunning)
 -         {
 -             stopThread (2000);
 -             isRunning = false;
 -             closeDevices();
 -         }
 -     }
 - 
 -     int getOutputLatencyInSamples()                     { return (minBufferSizeOut * 3) / 4; }
 -     int getInputLatencyInSamples()                      { return (minBufferSizeIn * 3) / 4; }
 -     bool isOpen()                                       { return isRunning; }
 -     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 isRunning && callback != 0; }
 - 
 -     void start (AudioIODeviceCallback* newCallback)
 -     {
 -         if (isRunning && callback != newCallback)
 -         {
 -             if (newCallback != nullptr)
 -                 newCallback->audioDeviceAboutToStart (this);
 - 
 -             const ScopedLock sl (callbackLock);
 -             callback = newCallback;
 -         }
 -     }
 - 
 -     void stop()
 -     {
 -         if (isRunning)
 -         {
 -             AudioIODeviceCallback* lastCallback;
 - 
 -             {
 -                 const ScopedLock sl (callbackLock);
 -                 lastCallback = callback;
 -                 callback = nullptr;
 -             }
 - 
 -             if (lastCallback != nullptr)
 -                 lastCallback->audioDeviceStopped();
 -         }
 -     }
 - 
 -     void run()
 -     {
 -         JNIEnv* env = getEnv();
 -         jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels));
 - 
 -         while (! threadShouldExit())
 -         {
 -             if (inputDevice != nullptr)
 -             {
 -                 jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels);
 - 
 -                 if (numRead < actualBufferSize * numDeviceInputChannels)
 -                 {
 -                     DBG ("Audio read under-run! " << numRead);
 -                 }
 - 
 -                 jshort* const src = env->GetShortArrayElements (audioBuffer, 0);
 - 
 -                 for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan)
 -                 {
 -                     AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> d (inputChannelBuffer.getSampleData (chan));
 - 
 -                     if (chan < numDeviceInputChannels)
 -                     {
 -                         AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const> s (src + chan, numDeviceInputChannels);
 -                         d.convertSamples (s, actualBufferSize);
 -                     }
 -                     else
 -                     {
 -                         d.clearSamples (actualBufferSize);
 -                     }
 -                 }
 - 
 -                 env->ReleaseShortArrayElements (audioBuffer, src, 0);
 -             }
 - 
 -             if (threadShouldExit())
 -                 break;
 - 
 -             {
 -                 const ScopedLock sl (callbackLock);
 - 
 -                 if (callback != nullptr)
 -                 {
 -                     callback->audioDeviceIOCallback ((const float**) inputChannelBuffer.getArrayOfChannels(), numClientInputChannels,
 -                                                      outputChannelBuffer.getArrayOfChannels(), numClientOutputChannels,
 -                                                      actualBufferSize);
 -                 }
 -                 else
 -                 {
 -                     outputChannelBuffer.clear();
 -                 }
 -             }
 - 
 -             if (outputDevice != nullptr)
 -             {
 -                 if (threadShouldExit())
 -                     break;
 - 
 -                 jshort* const dest = env->GetShortArrayElements (audioBuffer, 0);
 - 
 -                 for (int chan = 0; chan < numDeviceOutputChannels; ++chan)
 -                 {
 -                     AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst> d (dest + chan, numDeviceOutputChannels);
 - 
 -                     const float* const sourceChanData = outputChannelBuffer.getSampleData (jmin (chan, outputChannelBuffer.getNumChannels() - 1));
 -                     AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> s (sourceChanData);
 -                     d.convertSamples (s, actualBufferSize);
 -                 }
 - 
 -                 env->ReleaseShortArrayElements (audioBuffer, dest, 0);
 -                 jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels);
 - 
 -                 if (numWritten < actualBufferSize * numDeviceOutputChannels)
 -                 {
 -                     DBG ("Audio write underrun! " << numWritten);
 -                 }
 -             }
 -         }
 -     }
 - 
 -     int minBufferSizeOut, minBufferSizeIn;
 - 
 - private:
 -     //==================================================================================================
 -     CriticalSection callbackLock;
 -     AudioIODeviceCallback* callback;
 -     jint sampleRate;
 -     int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable;
 -     int numClientOutputChannels, numDeviceOutputChannels;
 -     int actualBufferSize;
 -     bool isRunning;
 -     String lastError;
 -     BigInteger activeOutputChans, activeInputChans;
 -     GlobalRef outputDevice, inputDevice;
 -     AudioSampleBuffer inputChannelBuffer, outputChannelBuffer;
 - 
 -     void closeDevices()
 -     {
 -         if (outputDevice != nullptr)
 -         {
 -             outputDevice.callVoidMethod (AudioTrack.stop);
 -             outputDevice.callVoidMethod (AudioTrack.release);
 -             outputDevice.clear();
 -         }
 - 
 -         if (inputDevice != nullptr)
 -         {
 -             inputDevice.callVoidMethod (AudioRecord.stop);
 -             inputDevice.callVoidMethod (AudioRecord.release);
 -             inputDevice.clear();
 -         }
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice);
 - };
 - 
 - //==============================================================================
 - class AndroidAudioIODeviceType  : public AudioIODeviceType
 - {
 - public:
 -     AndroidAudioIODeviceType()  : AudioIODeviceType (javaAudioTypeName) {}
 - 
 -     //==============================================================================
 -     void scanForDevices() {}
 -     StringArray getDeviceNames (bool wantInputNames) const              { return StringArray (javaAudioTypeName); }
 -     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<AndroidAudioIODevice> dev;
 - 
 -         if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
 -         {
 -             dev = new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
 -                                                                           : inputDeviceName);
 - 
 -             if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0)
 -                 dev = nullptr;
 -         }
 - 
 -         return dev.release();
 -     }
 - 
 - private:
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType);
 - };
 - 
 - 
 - //==============================================================================
 - extern bool isOpenSLAvailable();
 - 
 - AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android()
 - {
 -    #if JUCE_USE_ANDROID_OPENSLES
 -     if (isOpenSLAvailable())
 -         return nullptr;
 -    #endif
 - 
 -     return new AndroidAudioIODeviceType();
 - }
 
 
  |