| @@ -78,7 +78,7 @@ static void getDeviceSampleRates (snd_pcm_t* handle, Array <int>& rates) | |||
| if (snd_pcm_hw_params_any (handle, hwParams) >= 0 | |||
| && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) | |||
| { | |||
| rates.add (ratesToTry[i]); | |||
| rates.addIfNotAlreadyThere (ratesToTry[i]); | |||
| } | |||
| } | |||
| } | |||
| @@ -102,6 +102,9 @@ static void getDeviceProperties (const String& id, | |||
| unsigned int& maxChansIn, | |||
| Array <int>& rates) | |||
| { | |||
| if (id.isEmpty()) | |||
| return; | |||
| snd_ctl_t* handle; | |||
| if (snd_ctl_open (&handle, id.upToLastOccurrenceOf (T(","), false, false), SND_CTL_NONBLOCK) >= 0) | |||
| @@ -149,7 +152,7 @@ static void getDeviceProperties (const String& id, | |||
| class ALSADevice | |||
| { | |||
| public: | |||
| ALSADevice (const String& deviceName, | |||
| ALSADevice (const String& id, | |||
| const bool forInput) | |||
| : handle (0), | |||
| bitDepth (16), | |||
| @@ -157,7 +160,7 @@ public: | |||
| isInput (forInput), | |||
| sampleFormat (AudioDataConverters::int16LE) | |||
| { | |||
| failed (snd_pcm_open (&handle, deviceName, | |||
| failed (snd_pcm_open (&handle, id, | |||
| forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, | |||
| SND_PCM_ASYNC)); | |||
| } | |||
| @@ -364,12 +367,14 @@ private: | |||
| class ALSAThread : public Thread | |||
| { | |||
| public: | |||
| ALSAThread (const String& deviceName_) | |||
| ALSAThread (const String& inputId_, | |||
| const String& outputId_) | |||
| : Thread ("Juce ALSA"), | |||
| sampleRate (0), | |||
| bufferSize (0), | |||
| callback (0), | |||
| deviceName (deviceName_), | |||
| inputId (inputId_), | |||
| outputId (outputId_), | |||
| outputDevice (0), | |||
| inputDevice (0), | |||
| numCallbacks (0), | |||
| @@ -402,16 +407,9 @@ public: | |||
| currentInputChans.clear(); | |||
| currentOutputChans.clear(); | |||
| numChannelsRunning = jmax (inputChannels.getHighestBit(), | |||
| outputChannels.getHighestBit()) + 1; | |||
| numChannelsRunning = jmin (maxNumChans, jlimit ((int) minChansIn, | |||
| (int) maxChansIn, | |||
| numChannelsRunning)); | |||
| if (inputChannels.getHighestBit() >= 0) | |||
| { | |||
| for (int i = 0; i < numChannelsRunning; ++i) | |||
| for (int i = 0; i <= inputChannels.getHighestBit(); ++i) | |||
| { | |||
| inputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); | |||
| @@ -425,7 +423,7 @@ public: | |||
| if (outputChannels.getHighestBit() >= 0) | |||
| { | |||
| for (int i = 0; i < numChannelsRunning; ++i) | |||
| for (int i = 0; i <= outputChannels.getHighestBit(); ++i) | |||
| { | |||
| outputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize); | |||
| @@ -437,9 +435,9 @@ public: | |||
| } | |||
| } | |||
| if (totalNumOutputChannels > 0) | |||
| if (totalNumOutputChannels > 0 && outputId.isNotEmpty()) | |||
| { | |||
| outputDevice = new ALSADevice (deviceName, false); | |||
| outputDevice = new ALSADevice (outputId, false); | |||
| if (outputDevice->error.isNotEmpty()) | |||
| { | |||
| @@ -448,7 +446,9 @@ public: | |||
| return; | |||
| } | |||
| if (! outputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize)) | |||
| if (! outputDevice->setParameters ((unsigned int) sampleRate, | |||
| currentOutputChans.getHighestBit() + 1, | |||
| bufferSize)) | |||
| { | |||
| error = outputDevice->error; | |||
| deleteAndZero (outputDevice); | |||
| @@ -456,9 +456,9 @@ public: | |||
| } | |||
| } | |||
| if (totalNumInputChannels > 0) | |||
| if (totalNumInputChannels > 0 && inputId.isNotEmpty()) | |||
| { | |||
| inputDevice = new ALSADevice (deviceName, true); | |||
| inputDevice = new ALSADevice (inputId, true); | |||
| if (inputDevice->error.isNotEmpty()) | |||
| { | |||
| @@ -467,7 +467,9 @@ public: | |||
| return; | |||
| } | |||
| if (! inputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize)) | |||
| if (! inputDevice->setParameters ((unsigned int) sampleRate, | |||
| currentInputChans.getHighestBit() + 1, | |||
| bufferSize)) | |||
| { | |||
| error = inputDevice->error; | |||
| deleteAndZero (inputDevice); | |||
| @@ -527,7 +529,6 @@ public: | |||
| zeromem (inputChannelDataForCallback, sizeof (inputChannelDataForCallback)); | |||
| totalNumOutputChannels = 0; | |||
| totalNumInputChannels = 0; | |||
| numChannelsRunning = 0; | |||
| numCallbacks = 0; | |||
| } | |||
| @@ -544,8 +545,6 @@ public: | |||
| { | |||
| if (inputDevice != 0) | |||
| { | |||
| jassert (numChannelsRunning >= inputDevice->numChannelsRunning); | |||
| if (! inputDevice->read (inputChannelData, bufferSize)) | |||
| { | |||
| DBG ("ALSA: read failure"); | |||
| @@ -584,7 +583,6 @@ public: | |||
| failed (snd_pcm_avail_update (outputDevice->handle)); | |||
| jassert (numChannelsRunning >= outputDevice->numChannelsRunning); | |||
| if (! outputDevice->write (outputChannelData, bufferSize)) | |||
| { | |||
| DBG ("ALSA: write failure"); | |||
| @@ -619,7 +617,7 @@ public: | |||
| private: | |||
| //============================================================================== | |||
| const String deviceName; | |||
| const String inputId, outputId; | |||
| ALSADevice* outputDevice; | |||
| ALSADevice* inputDevice; | |||
| int numCallbacks; | |||
| @@ -632,7 +630,6 @@ private: | |||
| float* inputChannelData [maxNumChans]; | |||
| float* inputChannelDataForCallback [maxNumChans]; | |||
| int totalNumOutputChannels; | |||
| int numChannelsRunning; | |||
| unsigned int minChansOut, maxChansOut; | |||
| unsigned int minChansIn, maxChansIn; | |||
| @@ -656,8 +653,10 @@ private: | |||
| maxChansOut = 0; | |||
| minChansIn = 0; | |||
| maxChansIn = 0; | |||
| unsigned int dummy = 0; | |||
| getDeviceProperties (deviceName, minChansOut, maxChansOut, minChansIn, maxChansIn, sampleRates); | |||
| getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates); | |||
| getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates); | |||
| unsigned int i; | |||
| for (i = 0; i < maxChansOut; ++i) | |||
| @@ -674,13 +673,16 @@ class ALSAAudioIODevice : public AudioIODevice | |||
| { | |||
| public: | |||
| ALSAAudioIODevice (const String& deviceName, | |||
| const String& deviceId) | |||
| const String& inputId_, | |||
| const String& outputId_) | |||
| : AudioIODevice (deviceName, T("ALSA")), | |||
| inputId (inputId_), | |||
| outputId (outputId_), | |||
| isOpen_ (false), | |||
| isStarted (false), | |||
| internal (0) | |||
| { | |||
| internal = new ALSAThread (deviceId); | |||
| internal = new ALSAThread (inputId, outputId); | |||
| } | |||
| ~ALSAAudioIODevice() | |||
| @@ -839,6 +841,8 @@ public: | |||
| return internal->error; | |||
| } | |||
| String inputId, outputId; | |||
| private: | |||
| bool isOpen_, isStarted; | |||
| ALSAThread* internal; | |||
| @@ -863,10 +867,14 @@ public: | |||
| //============================================================================== | |||
| void scanForDevices() | |||
| { | |||
| hasScanned = true; | |||
| if (hasScanned) | |||
| return; | |||
| names.clear(); | |||
| ids.clear(); | |||
| hasScanned = true; | |||
| inputNames.clear(); | |||
| inputIds.clear(); | |||
| outputNames.clear(); | |||
| outputIds.clear(); | |||
| snd_ctl_t* handle; | |||
| snd_ctl_card_info_t* info; | |||
| @@ -874,7 +882,7 @@ public: | |||
| int cardNum = -1; | |||
| while (ids.size() <= 24) | |||
| while (outputIds.size() + inputIds.size() <= 32) | |||
| { | |||
| snd_card_next (&cardNum); | |||
| @@ -900,18 +908,26 @@ public: | |||
| String id, name; | |||
| id << "hw:" << cardId << ',' << device; | |||
| if (testDevice (id)) | |||
| bool isInput, isOutput; | |||
| if (testDevice (id, isInput, isOutput)) | |||
| { | |||
| name << snd_ctl_card_info_get_name (info); | |||
| if (name.isEmpty()) | |||
| name = id; | |||
| if (device > 0) | |||
| name << " (" << (device + 1) << ')'; | |||
| ids.add (id); | |||
| names.add (name); | |||
| if (isInput) | |||
| { | |||
| inputNames.add (name); | |||
| inputIds.add (id); | |||
| } | |||
| if (isOutput) | |||
| { | |||
| outputNames.add (name); | |||
| outputIds.add (id); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -919,35 +935,54 @@ public: | |||
| snd_ctl_close (handle); | |||
| } | |||
| } | |||
| inputNames.appendNumbersToDuplicates (false, true); | |||
| outputNames.appendNumbersToDuplicates (false, true); | |||
| } | |||
| const StringArray getDeviceNames (const bool /*preferInputNames*/) const | |||
| const StringArray getDeviceNames (const bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| StringArray namesCopy (names); | |||
| namesCopy.removeDuplicates (true); | |||
| return wantInputNames ? inputNames : outputNames; | |||
| } | |||
| return namesCopy; | |||
| int getDefaultDeviceIndex (const bool forInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return 0; | |||
| } | |||
| const String getDefaultDeviceName (const bool /*preferInputNames*/, | |||
| const int /*numInputChannelsNeeded*/, | |||
| const int /*numOutputChannelsNeeded*/) const | |||
| bool hasSeparateInputsAndOutputs() const { return true; } | |||
| int getIndexOfDevice (AudioIODevice* device, const bool asInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return names[0]; | |||
| ALSAAudioIODevice* const d = dynamic_cast <ALSAAudioIODevice*> (device); | |||
| if (d == 0) | |||
| return -1; | |||
| return asInput ? inputIds.indexOf (d->inputId) | |||
| : outputIds.indexOf (d->outputId); | |||
| } | |||
| AudioIODevice* createDevice (const String& deviceName) | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| const int index = names.indexOf (deviceName); | |||
| const int inputIndex = inputNames.indexOf (inputDeviceName); | |||
| const int outputIndex = outputNames.indexOf (outputDeviceName); | |||
| String deviceName (outputDeviceName); | |||
| if (deviceName.isEmpty()) | |||
| deviceName = inputDeviceName; | |||
| if (index >= 0) | |||
| return new ALSAAudioIODevice (deviceName, ids [index]); | |||
| return new ALSAAudioIODevice (deviceName, | |||
| inputIds [inputIndex], | |||
| outputIds [outputIndex]); | |||
| return 0; | |||
| } | |||
| @@ -956,10 +991,10 @@ public: | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| StringArray names, ids; | |||
| StringArray inputNames, outputNames, inputIds, outputIds; | |||
| bool hasScanned; | |||
| static bool testDevice (const String& id) | |||
| static bool testDevice (const String& id, bool& isInput, bool& isOutput) | |||
| { | |||
| unsigned int minChansOut = 0, maxChansOut = 0; | |||
| unsigned int minChansIn = 0, maxChansIn = 0; | |||
| @@ -972,7 +1007,10 @@ private: | |||
| + T(" ins=") + String ((int) minChansIn) + T("-") + String ((int) maxChansIn) | |||
| + T(" rates=") + String (rates.size())); | |||
| return (maxChansOut > 0 || maxChansIn > 0) && rates.size() > 0; | |||
| isInput = maxChansIn > 0; | |||
| isOutput = maxChansOut > 0; | |||
| return (isInput || isOutput) && rates.size() > 0; | |||
| } | |||
| ALSAAudioIODeviceType (const ALSAAudioIODeviceType&); | |||
| @@ -40,6 +40,11 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_core/threads/juce_ScopedLock.h" | |||
| #include "../../../src/juce_core/threads/juce_Thread.h" | |||
| #include "../../../src/juce_core/text/juce_LocalisedStrings.h" | |||
| #include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h" | |||
| #include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h" | |||
| #include "../../../src/juce_appframework/gui/components/controls/juce_ComboBox.h" | |||
| #include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h" | |||
| #include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" | |||
| //============================================================================== | |||
| @@ -613,7 +618,7 @@ public: | |||
| for (i = numOutputChans; --i >= 0;) | |||
| { | |||
| const CallbackDetailsForChannel& info = outputChannelInfo[i]; | |||
| const float* src = tempOutputBuffers [info.sourceChannelNum]; | |||
| const float* src = tempOutputBuffers [i]; | |||
| float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) | |||
| + info.dataOffsetSamples; | |||
| const int stride = info.dataStrideSamples; | |||
| @@ -833,31 +838,52 @@ class CoreAudioIODevice : public AudioIODevice | |||
| { | |||
| public: | |||
| CoreAudioIODevice (const String& deviceName, | |||
| AudioDeviceID deviceId1) | |||
| AudioDeviceID inputDeviceId, | |||
| const int inputIndex_, | |||
| AudioDeviceID outputDeviceId, | |||
| const int outputIndex_) | |||
| : AudioIODevice (deviceName, "CoreAudio"), | |||
| inputIndex (inputIndex_), | |||
| outputIndex (outputIndex_), | |||
| isOpen_ (false), | |||
| isStarted (false) | |||
| { | |||
| internal = 0; | |||
| CoreAudioInternal* device = 0; | |||
| CoreAudioInternal* device = new CoreAudioInternal (deviceId1); | |||
| lastError = device->error; | |||
| if (lastError.isNotEmpty()) | |||
| if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) | |||
| { | |||
| deleteAndZero (device); | |||
| jassert (inputDeviceId != 0); | |||
| device = new CoreAudioInternal (inputDeviceId); | |||
| lastError = device->error; | |||
| if (lastError.isNotEmpty()) | |||
| deleteAndZero (device); | |||
| } | |||
| else | |||
| { | |||
| CoreAudioInternal* secondDevice = device->getRelatedDevice(); | |||
| device = new CoreAudioInternal (outputDeviceId); | |||
| lastError = device->error; | |||
| if (secondDevice != 0) | |||
| if (lastError.isNotEmpty()) | |||
| { | |||
| if (device->inChanNames.size() > secondDevice->inChanNames.size()) | |||
| swapVariables (device, secondDevice); | |||
| deleteAndZero (device); | |||
| } | |||
| else if (inputDeviceId != 0) | |||
| { | |||
| CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId); | |||
| lastError = device->error; | |||
| device->inputDevice = secondDevice; | |||
| secondDevice->isSlaveDevice = true; | |||
| if (lastError.isNotEmpty()) | |||
| { | |||
| delete secondDevice; | |||
| } | |||
| else | |||
| { | |||
| device->inputDevice = secondDevice; | |||
| secondDevice->isSlaveDevice = true; | |||
| } | |||
| } | |||
| } | |||
| @@ -1035,6 +1061,8 @@ public: | |||
| return lastError; | |||
| } | |||
| int inputIndex, outputIndex; | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| @@ -1065,6 +1093,326 @@ private: | |||
| const CoreAudioIODevice& operator= (const CoreAudioIODevice&); | |||
| }; | |||
| //============================================================================== | |||
| class CoreAudioDevicePanel : public Component, | |||
| public ComboBoxListener, | |||
| public ChangeListener, | |||
| public ButtonListener | |||
| { | |||
| public: | |||
| CoreAudioDevicePanel (AudioIODeviceType* type_, | |||
| AudioIODeviceType::DeviceSetupDetails& setup_) | |||
| : type (type_), | |||
| setup (setup_) | |||
| { | |||
| sampleRateDropDown = 0; | |||
| sampleRateLabel = 0; | |||
| bufferSizeDropDown = 0; | |||
| bufferSizeLabel = 0; | |||
| outputDeviceDropDown = 0; | |||
| outputDeviceLabel = 0; | |||
| inputDeviceDropDown = 0; | |||
| inputDeviceLabel = 0; | |||
| testButton = 0; | |||
| inputLevelMeter = 0; | |||
| type->scanForDevices(); | |||
| if (setup.maxNumOutputChannels > 0) | |||
| { | |||
| outputDeviceDropDown = new ComboBox (String::empty); | |||
| addNamesToDeviceBox (*outputDeviceDropDown, false); | |||
| outputDeviceDropDown->addListener (this); | |||
| addAndMakeVisible (outputDeviceDropDown); | |||
| outputDeviceLabel = new Label (String::empty, TRANS ("output:")); | |||
| outputDeviceLabel->attachToComponent (outputDeviceDropDown, true); | |||
| addAndMakeVisible (testButton = new TextButton (TRANS ("Test"))); | |||
| testButton->addButtonListener (this); | |||
| } | |||
| if (setup.maxNumInputChannels > 0) | |||
| { | |||
| inputDeviceDropDown = new ComboBox (String::empty); | |||
| addNamesToDeviceBox (*inputDeviceDropDown, true); | |||
| inputDeviceDropDown->addListener (this); | |||
| addAndMakeVisible (inputDeviceDropDown); | |||
| inputDeviceLabel = new Label (String::empty, TRANS ("input:")); | |||
| inputDeviceLabel->attachToComponent (inputDeviceDropDown, true); | |||
| addAndMakeVisible (inputLevelMeter = AudioDeviceSelectorComponent::createSimpleLevelMeterComponent (setup_.manager)); | |||
| } | |||
| setup.manager->addChangeListener (this); | |||
| changeListenerCallback (0); | |||
| } | |||
| ~CoreAudioDevicePanel() | |||
| { | |||
| setup.manager->removeChangeListener (this); | |||
| deleteAndZero (outputDeviceLabel); | |||
| deleteAndZero (inputDeviceLabel); | |||
| deleteAndZero (sampleRateLabel); | |||
| deleteAndZero (bufferSizeLabel); | |||
| deleteAllChildren(); | |||
| } | |||
| void resized() | |||
| { | |||
| const int lx = proportionOfWidth (0.35f); | |||
| const int w = proportionOfWidth (0.5f); | |||
| const int h = 24; | |||
| const int space = 6; | |||
| const int dh = h + space; | |||
| int y = 0; | |||
| if (outputDeviceDropDown != 0) | |||
| { | |||
| outputDeviceDropDown->setBounds (lx, y, w, h); | |||
| testButton->setBounds (outputDeviceDropDown->getRight() + 8, | |||
| outputDeviceDropDown->getY(), | |||
| getWidth() - outputDeviceDropDown->getRight() - 10, | |||
| h); | |||
| y += dh; | |||
| } | |||
| if (inputDeviceDropDown != 0) | |||
| { | |||
| inputDeviceDropDown->setBounds (lx, y, w, h); | |||
| inputLevelMeter->setBounds (inputDeviceDropDown->getRight() + 8, | |||
| inputDeviceDropDown->getY(), | |||
| getWidth() - inputDeviceDropDown->getRight() - 10, | |||
| h); | |||
| y += dh; | |||
| } | |||
| y += space * 2; | |||
| if (sampleRateDropDown != 0) | |||
| { | |||
| sampleRateDropDown->setBounds (lx, y, w, h); | |||
| y += dh; | |||
| } | |||
| if (bufferSizeDropDown != 0) | |||
| { | |||
| bufferSizeDropDown->setBounds (lx, y, w, h); | |||
| y += dh; | |||
| } | |||
| } | |||
| void comboBoxChanged (ComboBox* comboBoxThatHasChanged) | |||
| { | |||
| if (comboBoxThatHasChanged == 0) | |||
| return; | |||
| AudioDeviceManager::AudioDeviceSetup config; | |||
| setup.manager->getAudioDeviceSetup (config); | |||
| String error; | |||
| if (comboBoxThatHasChanged == outputDeviceDropDown | |||
| || comboBoxThatHasChanged == inputDeviceDropDown) | |||
| { | |||
| config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||
| : outputDeviceDropDown->getText(); | |||
| config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty | |||
| : inputDeviceDropDown->getText(); | |||
| if (comboBoxThatHasChanged == inputDeviceDropDown) | |||
| config.useDefaultInputChannels = true; | |||
| else | |||
| config.useDefaultOutputChannels = true; | |||
| error = setup.manager->setAudioDeviceSetup (config, true); | |||
| showCorrectDeviceName (inputDeviceDropDown, true); | |||
| showCorrectDeviceName (outputDeviceDropDown, false); | |||
| } | |||
| else if (comboBoxThatHasChanged == sampleRateDropDown) | |||
| { | |||
| if (sampleRateDropDown->getSelectedId() > 0) | |||
| { | |||
| config.sampleRate = sampleRateDropDown->getSelectedId(); | |||
| error = setup.manager->setAudioDeviceSetup (config, true); | |||
| } | |||
| } | |||
| else if (comboBoxThatHasChanged == bufferSizeDropDown) | |||
| { | |||
| if (bufferSizeDropDown->getSelectedId() > 0) | |||
| { | |||
| config.bufferSize = bufferSizeDropDown->getSelectedId(); | |||
| error = setup.manager->setAudioDeviceSetup (config, true); | |||
| } | |||
| } | |||
| if (error.isNotEmpty()) | |||
| { | |||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||
| T("Error when trying to open audio device!"), | |||
| error); | |||
| } | |||
| } | |||
| void buttonClicked (Button*) | |||
| { | |||
| setup.manager->playTestSound(); | |||
| } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice(); | |||
| if (currentDevice != 0) | |||
| { | |||
| showCorrectDeviceName (inputDeviceDropDown, true); | |||
| showCorrectDeviceName (outputDeviceDropDown, false); | |||
| // sample rate.. | |||
| { | |||
| if (sampleRateDropDown == 0) | |||
| { | |||
| addAndMakeVisible (sampleRateDropDown = new ComboBox (String::empty)); | |||
| sampleRateDropDown->addListener (this); | |||
| delete sampleRateLabel; | |||
| sampleRateLabel = new Label (String::empty, TRANS ("sample rate:")); | |||
| sampleRateLabel->attachToComponent (sampleRateDropDown, true); | |||
| } | |||
| else | |||
| { | |||
| sampleRateDropDown->clear(); | |||
| sampleRateDropDown->removeListener (this); | |||
| } | |||
| const int numRates = currentDevice->getNumSampleRates(); | |||
| for (int i = 0; i < numRates; ++i) | |||
| { | |||
| const int rate = roundDoubleToInt (currentDevice->getSampleRate (i)); | |||
| sampleRateDropDown->addItem (String (rate) + T(" Hz"), rate); | |||
| } | |||
| sampleRateDropDown->setSelectedId (roundDoubleToInt (currentDevice->getCurrentSampleRate()), true); | |||
| sampleRateDropDown->addListener (this); | |||
| } | |||
| // buffer size | |||
| { | |||
| if (bufferSizeDropDown == 0) | |||
| { | |||
| addAndMakeVisible (bufferSizeDropDown = new ComboBox (String::empty)); | |||
| bufferSizeDropDown->addListener (this); | |||
| delete bufferSizeLabel; | |||
| bufferSizeLabel = new Label (String::empty, TRANS ("audio buffer size:")); | |||
| bufferSizeLabel->attachToComponent (bufferSizeDropDown, true); | |||
| } | |||
| else | |||
| { | |||
| bufferSizeDropDown->clear(); | |||
| } | |||
| const int numBufferSizes = currentDevice->getNumBufferSizesAvailable(); | |||
| double currentRate = currentDevice->getCurrentSampleRate(); | |||
| if (currentRate == 0) | |||
| currentRate = 44100.0; | |||
| for (int i = 0; i < numBufferSizes; ++i) | |||
| { | |||
| const int bs = currentDevice->getBufferSizeSamples (i); | |||
| bufferSizeDropDown->addItem (String (bs) | |||
| + T(" samples (") | |||
| + String (bs * 1000.0 / currentRate, 1) | |||
| + T(" ms)"), | |||
| bs); | |||
| } | |||
| bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), true); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| jassert (setup.manager->getCurrentAudioDevice() == 0); // not the correct device type! | |||
| deleteAndZero (sampleRateLabel); | |||
| deleteAndZero (bufferSizeLabel); | |||
| deleteAndZero (sampleRateDropDown); | |||
| deleteAndZero (bufferSizeDropDown); | |||
| if (outputDeviceDropDown != 0) | |||
| outputDeviceDropDown->setSelectedId (-1, true); | |||
| if (inputDeviceDropDown != 0) | |||
| inputDeviceDropDown->setSelectedId (-1, true); | |||
| } | |||
| resized(); | |||
| setSize (getWidth(), getLowestY() + 4); | |||
| } | |||
| private: | |||
| AudioIODeviceType* const type; | |||
| const AudioIODeviceType::DeviceSetupDetails setup; | |||
| ComboBox* outputDeviceDropDown; | |||
| ComboBox* inputDeviceDropDown; | |||
| ComboBox* sampleRateDropDown; | |||
| ComboBox* bufferSizeDropDown; | |||
| Label* outputDeviceLabel; | |||
| Label* inputDeviceLabel; | |||
| Label* sampleRateLabel; | |||
| Label* bufferSizeLabel; | |||
| TextButton* testButton; | |||
| Component* inputLevelMeter; | |||
| void showCorrectDeviceName (ComboBox* const box, const bool isInput) | |||
| { | |||
| if (box != 0) | |||
| { | |||
| CoreAudioIODevice* const currentDevice = dynamic_cast <CoreAudioIODevice*> (setup.manager->getCurrentAudioDevice()); | |||
| const int index = (currentDevice == 0) ? -1 | |||
| : (isInput ? currentDevice->inputIndex | |||
| : currentDevice->outputIndex); | |||
| if (index >= 0) | |||
| box->setText (type->getDeviceNames (isInput) [index], true); | |||
| else | |||
| box->setSelectedId (-1, true); | |||
| if (! isInput) | |||
| testButton->setEnabled (index >= 0); | |||
| } | |||
| } | |||
| void addNamesToDeviceBox (ComboBox& combo, bool isInputs) | |||
| { | |||
| const StringArray devs (type->getDeviceNames (isInputs)); | |||
| for (int i = 0; i < devs.size(); ++i) | |||
| combo.addItem (devs[i], i + 1); | |||
| combo.addItem (TRANS("<< none >>"), -1); | |||
| combo.setSelectedId (-1, true); | |||
| } | |||
| int getLowestY() const | |||
| { | |||
| int y = 0; | |||
| for (int i = getNumChildComponents(); --i >= 0;) | |||
| y = jmax (y, getChildComponent (i)->getBottom()); | |||
| return y; | |||
| } | |||
| CoreAudioDevicePanel (const CoreAudioDevicePanel&); | |||
| const CoreAudioDevicePanel& operator= (const CoreAudioDevicePanel&); | |||
| }; | |||
| //============================================================================== | |||
| class CoreAudioIODeviceType : public AudioIODeviceType | |||
| @@ -1086,8 +1434,10 @@ public: | |||
| { | |||
| hasScanned = true; | |||
| names.clear(); | |||
| ids.clear(); | |||
| inputDeviceNames.clear(); | |||
| outputDeviceNames.clear(); | |||
| inputIds.clear(); | |||
| outputIds.clear(); | |||
| UInt32 size; | |||
| if (OK (AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, 0))) | |||
| @@ -1109,8 +1459,20 @@ public: | |||
| if (! alreadyLogged) | |||
| log (T("CoreAudio device: ") + nameString); | |||
| names.add (nameString); | |||
| ids.add (devs[i]); | |||
| const int numIns = getNumChannels (devs[i], true); | |||
| const int numOuts = getNumChannels (devs[i], false); | |||
| if (numIns > 0) | |||
| { | |||
| inputDeviceNames.add (nameString); | |||
| inputIds.add (devs[i]); | |||
| } | |||
| if (numOuts > 0) | |||
| { | |||
| outputDeviceNames.add (nameString); | |||
| outputIds.add (devs[i]); | |||
| } | |||
| } | |||
| } | |||
| @@ -1119,52 +1481,83 @@ public: | |||
| juce_free (devs); | |||
| } | |||
| inputDeviceNames.appendNumbersToDuplicates (false, true); | |||
| outputDeviceNames.appendNumbersToDuplicates (false, true); | |||
| } | |||
| const StringArray getDeviceNames (const bool /*preferInputNames*/) const | |||
| const StringArray getDeviceNames (const bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| StringArray namesCopy (names); | |||
| namesCopy.removeDuplicates (true); | |||
| return namesCopy; | |||
| if (wantInputNames) | |||
| return inputDeviceNames; | |||
| else | |||
| return outputDeviceNames; | |||
| } | |||
| const String getDefaultDeviceName (const bool preferInputNames, | |||
| const int numInputChannelsNeeded, | |||
| const int numOutputChannelsNeeded) const | |||
| int getDefaultDeviceIndex (const bool forInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| String result (names[0]); | |||
| AudioDeviceID deviceID; | |||
| UInt32 size = sizeof (deviceID); | |||
| // if they're asking for any input channels at all, use the default input, so we | |||
| // get the built-in mic rather than the built-in output with no inputs.. | |||
| if (AudioHardwareGetProperty (numInputChannelsNeeded > 0 | |||
| ? kAudioHardwarePropertyDefaultInputDevice | |||
| : kAudioHardwarePropertyDefaultOutputDevice, | |||
| if (AudioHardwareGetProperty (forInput ? kAudioHardwarePropertyDefaultInputDevice | |||
| : kAudioHardwarePropertyDefaultOutputDevice, | |||
| &size, &deviceID) == noErr) | |||
| { | |||
| for (int i = ids.size(); --i >= 0;) | |||
| if (ids[i] == deviceID) | |||
| result = names[i]; | |||
| if (forInput) | |||
| { | |||
| for (int i = inputIds.size(); --i >= 0;) | |||
| if (inputIds[i] == deviceID) | |||
| return i; | |||
| } | |||
| else | |||
| { | |||
| for (int i = outputIds.size(); --i >= 0;) | |||
| if (outputIds[i] == deviceID) | |||
| return i; | |||
| } | |||
| } | |||
| return result; | |||
| return 0; | |||
| } | |||
| AudioIODevice* createDevice (const String& deviceName) | |||
| int getIndexOfDevice (AudioIODevice* device, const bool asInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| const int index = names.indexOf (deviceName); | |||
| CoreAudioIODevice* const d = dynamic_cast <CoreAudioIODevice*> (device); | |||
| if (d == 0) | |||
| return -1; | |||
| return asInput ? d->inputIndex | |||
| : d->outputIndex; | |||
| } | |||
| bool hasSeparateInputsAndOutputs() const { return true; } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); | |||
| const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); | |||
| String deviceName (outputDeviceName); | |||
| if (deviceName.isEmpty()) | |||
| deviceName = inputDeviceName; | |||
| if (index >= 0) | |||
| return new CoreAudioIODevice (deviceName, ids [index]); | |||
| return new CoreAudioIODevice (deviceName, | |||
| inputIds [inputIndex], | |||
| inputIndex, | |||
| outputIds [outputIndex], | |||
| outputIndex); | |||
| return 0; | |||
| } | |||
| @@ -1173,11 +1566,37 @@ public: | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| StringArray names; | |||
| Array <AudioDeviceID> ids; | |||
| StringArray inputDeviceNames, outputDeviceNames; | |||
| Array <AudioDeviceID> inputIds, outputIds; | |||
| bool hasScanned; | |||
| static int getNumChannels (AudioDeviceID deviceID, bool input) | |||
| { | |||
| int total = 0; | |||
| UInt32 size; | |||
| if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0))) | |||
| { | |||
| AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size); | |||
| if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList))) | |||
| { | |||
| const int numStreams = bufList->mNumberBuffers; | |||
| for (int i = 0; i < numStreams; ++i) | |||
| { | |||
| const AudioBuffer& b = bufList->mBuffers[i]; | |||
| total += b.mNumberChannels; | |||
| } | |||
| } | |||
| juce_free (bufList); | |||
| } | |||
| return total; | |||
| } | |||
| CoreAudioIODeviceType (const CoreAudioIODeviceType&); | |||
| const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&); | |||
| }; | |||
| @@ -66,12 +66,17 @@ | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h" | |||
| #include "../../../src/juce_appframework/audio/devices/juce_AudioDeviceManager.h" | |||
| #include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h" | |||
| #include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h" | |||
| #include "../../../src/juce_core/threads/juce_ScopedLock.h" | |||
| #include "../../../src/juce_appframework/gui/components/juce_Component.h" | |||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||
| #include "../../../src/juce_core/threads/juce_Thread.h" | |||
| #include "../../../src/juce_appframework/events/juce_Timer.h" | |||
| #include "../../../src/juce_appframework/events/juce_MessageManager.h" | |||
| #include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" | |||
| #include "../../../src/juce_appframework/gui/components/controls/juce_ListBox.h" | |||
| #include "../../../src/juce_core/text/juce_LocalisedStrings.h" | |||
| //============================================================================== | |||
| @@ -120,7 +125,6 @@ static const int maxASIOChannels = 160; | |||
| //============================================================================== | |||
| class JUCE_API ASIOAudioIODevice : public AudioIODevice, | |||
| private Thread, | |||
| private Timer | |||
| { | |||
| public: | |||
| @@ -128,7 +132,6 @@ public: | |||
| ASIOAudioIODevice (const String& name_, const CLSID classId_, const int slotNumber) | |||
| : AudioIODevice (name_, T("ASIO")), | |||
| Thread ("Juce ASIO"), | |||
| asioObject (0), | |||
| classId (classId_), | |||
| currentBitDepth (16), | |||
| @@ -496,8 +499,6 @@ public: | |||
| { | |||
| buffersCreated = true; | |||
| jassert (! isThreadRunning()); | |||
| juce_free (tempBuffer); | |||
| tempBuffer = (float*) juce_calloc (totalBuffers * currentBlockSizeSamples * sizeof (float) + 128); | |||
| @@ -601,7 +602,6 @@ public: | |||
| } | |||
| isOpen_ = true; | |||
| isThreadReady = false; | |||
| log ("starting ASIO"); | |||
| calledback = false; | |||
| @@ -808,26 +808,6 @@ public: | |||
| return done; | |||
| } | |||
| void run() | |||
| { | |||
| isThreadReady = true; | |||
| for (;;) | |||
| { | |||
| event1.wait(); | |||
| if (threadShouldExit()) | |||
| break; | |||
| processBuffer(); | |||
| } | |||
| if (bufferIndex < 0) | |||
| { | |||
| log ("! ASIO callback never called"); | |||
| } | |||
| } | |||
| void resetRequest() throw() | |||
| { | |||
| needToReset = true; | |||
| @@ -912,7 +892,7 @@ private: | |||
| bool isOpen_, isStarted; | |||
| bool volatile isASIOOpen; | |||
| bool volatile calledback; | |||
| bool volatile littleEndian, postOutput, needToReset, isReSync, isThreadReady; | |||
| bool volatile littleEndian, postOutput, needToReset, isReSync; | |||
| bool volatile insideControlPanelModalLoop; | |||
| bool volatile shouldUsePreferredSize; | |||
| @@ -1809,46 +1789,65 @@ public: | |||
| } | |||
| } | |||
| const StringArray getDeviceNames (const bool /*preferInputNames*/) const | |||
| const StringArray getDeviceNames (const bool /*wantInputNames*/) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return deviceNames; | |||
| } | |||
| const String getDefaultDeviceName (const bool /*preferInputNames*/, | |||
| const int /*numInputChannelsNeeded*/, | |||
| const int /*numOutputChannelsNeeded*/) const | |||
| int getDefaultDeviceIndex (const bool) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return deviceNames [0]; | |||
| for (int i = deviceNames.size(); --i >= 0;) | |||
| if (deviceNames[i].containsIgnoreCase (T("asio4all"))) | |||
| return i; // asio4all is a safe choice for a default.. | |||
| #if JUCE_DEBUG | |||
| if (deviceNames.size() > 1 && deviceNames[0].containsIgnoreCase (T("digidesign"))) | |||
| return 1; // (the digi m-box driver crashes the app when you run | |||
| // it in the debugger, which can be a bit annoying) | |||
| #endif | |||
| return 0; | |||
| } | |||
| AudioIODevice* createDevice (const String& deviceName) | |||
| static int findFreeSlot() | |||
| { | |||
| for (int i = 0; i < numElementsInArray (currentASIODev); ++i) | |||
| if (currentASIODev[i] == 0) | |||
| return i; | |||
| jassertfalse; // unfortunately you can only have a finite number | |||
| // of ASIO devices open at the same time.. | |||
| return -1; | |||
| } | |||
| int getIndexOfDevice (AudioIODevice* d, const bool /*asInput*/) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| const int index = deviceNames.indexOf (deviceName); | |||
| return d == 0 ? -1 : deviceNames.indexOf (d->getName()); | |||
| } | |||
| if (index >= 0) | |||
| { | |||
| int freeSlot = -1; | |||
| bool hasSeparateInputsAndOutputs() const { return false; } | |||
| for (int i = 0; i < numElementsInArray (currentASIODev); ++i) | |||
| { | |||
| if (currentASIODev[i] == 0) | |||
| { | |||
| freeSlot = i; | |||
| break; | |||
| } | |||
| } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| jassert (inputDeviceName == outputDeviceName); | |||
| (void) inputDeviceName; | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| jassert (freeSlot >= 0); // unfortunately you can only have a finite number | |||
| // of ASIO devices open at the same time.. | |||
| const int index = deviceNames.indexOf (outputDeviceName); | |||
| if (index >= 0) | |||
| { | |||
| const int freeSlot = findFreeSlot(); | |||
| if (freeSlot >= 0) | |||
| return new ASIOAudioIODevice (deviceName, *(classIds [index]), freeSlot); | |||
| return new ASIOAudioIODevice (outputDeviceName, *(classIds [index]), freeSlot); | |||
| } | |||
| return 0; | |||
| @@ -1923,6 +1922,7 @@ private: | |||
| return ok; | |||
| } | |||
| //============================================================================== | |||
| void addDriverInfo (const String& keyName, HKEY hk) | |||
| { | |||
| HKEY subKey; | |||
| @@ -1974,6 +1974,17 @@ AudioIODeviceType* juce_createASIOAudioIODeviceType() | |||
| return new ASIOAudioIODeviceType(); | |||
| } | |||
| AudioIODevice* juce_createASIOAudioIODeviceForGUID (const String& name, | |||
| void* guid) | |||
| { | |||
| const int freeSlot = ASIOAudioIODeviceType::findFreeSlot(); | |||
| if (freeSlot < 0) | |||
| return 0; | |||
| return new ASIOAudioIODevice (name, *(CLSID*) guid, freeSlot); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -141,12 +141,16 @@ DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h" | |||
| #include "../../../src/juce_appframework/audio/devices/juce_AudioDeviceManager.h" | |||
| #include "../../../src/juce_appframework/application/juce_Application.h" | |||
| #include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h" | |||
| #include "../../../src/juce_core/threads/juce_Thread.h" | |||
| #include "../../../src/juce_core/basics/juce_Singleton.h" | |||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||
| #include "../../../src/juce_core/containers/juce_OwnedArray.h" | |||
| #include "../../../src/juce_core/text/juce_LocalisedStrings.h" | |||
| #include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h" | |||
| #include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h" | |||
| static const String getDSErrorMessage (HRESULT hr) | |||
| @@ -949,7 +953,7 @@ public: | |||
| }; | |||
| //============================================================================== | |||
| static int findBestMatchForName (const String& name, const StringArray& names) | |||
| /*static int findBestMatchForName (const String& name, const StringArray& names) | |||
| { | |||
| int i = names.indexOf (name); | |||
| @@ -978,21 +982,21 @@ static int findBestMatchForName (const String& name, const StringArray& names) | |||
| } | |||
| return bestResult; | |||
| } | |||
| }*/ | |||
| class DSoundAudioIODevice : public AudioIODevice, | |||
| public Thread | |||
| { | |||
| public: | |||
| DSoundAudioIODevice (const String& deviceName, | |||
| const int index, | |||
| const int inputIndex_) | |||
| const int outputDeviceIndex_, | |||
| const int inputDeviceIndex_) | |||
| : AudioIODevice (deviceName, "DirectSound"), | |||
| Thread ("Juce DSound"), | |||
| isOpen_ (false), | |||
| isStarted (false), | |||
| deviceIndex (index), | |||
| inputIndex (inputIndex_), | |||
| outputDeviceIndex (outputDeviceIndex_), | |||
| inputDeviceIndex (inputDeviceIndex_), | |||
| inChans (4), | |||
| outChans (4), | |||
| numInputBuffers (0), | |||
| @@ -1004,6 +1008,17 @@ public: | |||
| callback (0), | |||
| bufferSizeSamples (0) | |||
| { | |||
| if (outputDeviceIndex_ >= 0) | |||
| { | |||
| outChannels.add (TRANS("Left")); | |||
| outChannels.add (TRANS("Right")); | |||
| } | |||
| if (inputDeviceIndex_ >= 0) | |||
| { | |||
| inChannels.add (TRANS("Left")); | |||
| inChannels.add (TRANS("Right")); | |||
| } | |||
| } | |||
| ~DSoundAudioIODevice() | |||
| @@ -1176,13 +1191,13 @@ public: | |||
| juce_UseDebuggingNewOperator | |||
| StringArray inChannels, outChannels; | |||
| int outputDeviceIndex, inputDeviceIndex; | |||
| private: | |||
| bool isOpen_; | |||
| bool isStarted; | |||
| String lastError; | |||
| int deviceIndex, inputIndex; | |||
| OwnedArray <DSoundInternalInChannel> inChans; | |||
| OwnedArray <DSoundInternalOutChannel> outChans; | |||
| WaitableEvent startEvent; | |||
| @@ -1381,6 +1396,7 @@ public: | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class DSoundAudioIODeviceType : public AudioIODeviceType | |||
| { | |||
| @@ -1413,72 +1429,46 @@ public: | |||
| } | |||
| } | |||
| const StringArray getDeviceNames (const bool preferInputNames) const | |||
| const StringArray getDeviceNames (const bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return preferInputNames ? inputDeviceNames | |||
| : outputDeviceNames; | |||
| return wantInputNames ? inputDeviceNames | |||
| : outputDeviceNames; | |||
| } | |||
| const String getDefaultDeviceName (const bool preferInputNames, | |||
| const int /*numInputChannelsNeeded*/, | |||
| const int /*numOutputChannelsNeeded*/) const | |||
| int getDefaultDeviceIndex (const bool /*forInput*/) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return getDeviceNames (preferInputNames) [0]; | |||
| return 0; | |||
| } | |||
| AudioIODevice* createDevice (const String& deviceName) | |||
| int getIndexOfDevice (AudioIODevice* device, const bool asInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| if (deviceName.isEmpty() || deviceName.equalsIgnoreCase (T("DirectSound"))) | |||
| { | |||
| DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, -1, -1); | |||
| DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device); | |||
| if (d == 0) | |||
| return -1; | |||
| int i; | |||
| for (i = 0; i < outputDeviceNames.size(); ++i) | |||
| { | |||
| device->outChannels.add (outputDeviceNames[i] + TRANS(" (left)")); | |||
| device->outChannels.add (outputDeviceNames[i] + TRANS(" (right)")); | |||
| } | |||
| for (i = 0; i < inputDeviceNames.size(); ++i) | |||
| { | |||
| device->inChannels.add (inputDeviceNames[i] + TRANS(" (left)")); | |||
| device->inChannels.add (inputDeviceNames[i] + TRANS(" (right)")); | |||
| } | |||
| return device; | |||
| } | |||
| else if (outputDeviceNames.contains (deviceName) | |||
| || inputDeviceNames.contains (deviceName)) | |||
| { | |||
| int outputIndex = outputDeviceNames.indexOf (deviceName); | |||
| int inputIndex = findBestMatchForName (deviceName, inputDeviceNames); | |||
| if (outputIndex < 0) | |||
| { | |||
| // using an input device name instead.. | |||
| inputIndex = inputDeviceNames.indexOf (deviceName); | |||
| outputIndex = jmax (0, findBestMatchForName (deviceName, outputDeviceNames)); | |||
| } | |||
| return asInput ? d->inputDeviceIndex | |||
| : d->outputDeviceIndex; | |||
| } | |||
| DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, outputIndex, inputIndex); | |||
| bool hasSeparateInputsAndOutputs() const { return true; } | |||
| device->outChannels.add (TRANS("Left")); | |||
| device->outChannels.add (TRANS("Right")); | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| if (inputIndex >= 0) | |||
| { | |||
| device->inChannels.add (TRANS("Left")); | |||
| device->inChannels.add (TRANS("Right")); | |||
| } | |||
| const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); | |||
| const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); | |||
| return device; | |||
| } | |||
| if (outputIndex >= 0 || inputIndex >= 0) | |||
| return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
| : inputDeviceName, | |||
| outputIndex, inputIndex); | |||
| return 0; | |||
| } | |||
| @@ -1619,8 +1609,8 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, | |||
| right = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); | |||
| if (left != 0 || right != 0) | |||
| inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [i / 2], | |||
| dlh.inputGuids [i / 2], | |||
| inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex], | |||
| dlh.inputGuids [inputDeviceIndex], | |||
| (int) sampleRate, bufferSizeSamples, | |||
| left, right)); | |||
| } | |||
| @@ -1647,8 +1637,8 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, | |||
| right = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float)); | |||
| if (left != 0 || right != 0) | |||
| outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[i / 2], | |||
| dlh.outputGuids [i / 2], | |||
| outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex], | |||
| dlh.outputGuids [outputDeviceIndex], | |||
| (int) sampleRate, bufferSizeSamples, | |||
| left, right)); | |||
| } | |||
| @@ -1714,6 +1704,7 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels, | |||
| return error; | |||
| } | |||
| #undef log | |||
| END_JUCE_NAMESPACE | |||
| @@ -154,7 +154,7 @@ void AudioFilterStreamingDeviceManager::setFilter (AudioProcessor* filterToStrea | |||
| { | |||
| if (streamer != 0) | |||
| { | |||
| removeMidiInputCallback (streamer); | |||
| removeMidiInputCallback (String::empty, streamer); | |||
| setAudioCallback (0); | |||
| delete streamer; | |||
| @@ -479,7 +479,7 @@ public: | |||
| ~AudioDemo() | |||
| { | |||
| audioDeviceManager.removeMidiInputCallback (&synthSource.midiCollector); | |||
| audioDeviceManager.removeMidiInputCallback (String::empty, &synthSource.midiCollector); | |||
| audioDeviceManager.setAudioCallback (0); | |||
| transportSource.removeChangeListener (this); | |||
| @@ -196,7 +196,7 @@ | |||
| /** Enabling this builds support for VST audio plugins. | |||
| @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU | |||
| */ | |||
| #define JUCE_PLUGINHOST_VST 1 | |||
| //#define JUCE_PLUGINHOST_VST 1 | |||
| /** Enabling this builds support for AudioUnit audio plugins. | |||
| @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST | |||
| @@ -4138,6 +4138,8 @@ public: | |||
| @param minutes minutes 0 to 59 | |||
| @param seconds seconds 0 to 59 | |||
| @param milliseconds milliseconds 0 to 999 | |||
| @param useLocalTime if true, encode using the current machine's local time; if | |||
| false, it will always work in GMT. | |||
| */ | |||
| Time (const int year, | |||
| const int month, | |||
| @@ -4145,7 +4147,8 @@ public: | |||
| const int hours, | |||
| const int minutes, | |||
| const int seconds = 0, | |||
| const int milliseconds = 0) throw(); | |||
| const int milliseconds = 0, | |||
| const bool useLocalTime = true) throw(); | |||
| /** Destructor. */ | |||
| ~Time() throw(); | |||
| @@ -12994,6 +12997,11 @@ public: | |||
| */ | |||
| static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) throw(); | |||
| /** WIN32 ONLY - Gets the command-line params as a string. | |||
| This is needed to avoid unicode problems with the argc type params. | |||
| */ | |||
| static const String JUCE_CALLTYPE getCurrentCommandLineParams() throw(); | |||
| #endif | |||
| /** Clears the floating point unit's flags. | |||
| @@ -20315,6 +20323,13 @@ public: | |||
| /** Tries to give the window keyboard focus. */ | |||
| virtual void grabFocus() = 0; | |||
| /** Tells the window that text input may be required at the given position. | |||
| This may cause things like a virtual on-screen keyboard to appear, depending | |||
| on the OS. | |||
| */ | |||
| virtual void textInputRequired (int x, int y) = 0; | |||
| /** Called when the window gains keyboard focus. */ | |||
| void handleFocusGain(); | |||
| /** Called when the window loses keyboard focus. */ | |||
| @@ -31673,6 +31688,9 @@ private: | |||
| #ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ | |||
| #define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ | |||
| class AudioDeviceManager; | |||
| class Component; | |||
| /** | |||
| Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc. | |||
| @@ -31709,19 +31727,6 @@ class JUCE_API AudioIODeviceType | |||
| { | |||
| public: | |||
| /** Creates a list of available types. | |||
| This will add a set of new AudioIODeviceType objects to the specified list, to | |||
| represent each available types of device. | |||
| The objects that are created should be managed by the caller (the OwnedArray | |||
| will delete them when the array is itself deleted). | |||
| When created, the objects are uninitialised, so you should call scanForDevices() | |||
| on each one before getting its list of devices. | |||
| */ | |||
| static void createDeviceTypes (OwnedArray <AudioIODeviceType>& list); | |||
| /** Returns the name of this type of driver that this object manages. | |||
| This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc. | |||
| @@ -31739,34 +31744,46 @@ public: | |||
| The scanForDevices() method must have been called to create this list. | |||
| @param preferInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| @param wantInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| */ | |||
| virtual const StringArray getDeviceNames (const bool preferInputNames = false) const = 0; | |||
| virtual const StringArray getDeviceNames (const bool wantInputNames = false) const = 0; | |||
| /** Returns the name of the default device. | |||
| This will be one of the names from the getDeviceNames() list. | |||
| @param preferInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| @param numInputChannelsNeeded the number of input channels the user is expecting to need - this | |||
| may be used to help decide which device would be most suitable | |||
| @param numOutputChannelsNeeded the number of output channels the user is expecting to need - this | |||
| may be used to help decide which device would be most suitable | |||
| @param forInput if true, this means that a default input device should be | |||
| returned; if false, it should return the default output | |||
| */ | |||
| virtual int getDefaultDeviceIndex (const bool forInput) const = 0; | |||
| /** Returns the index of a given device in the list of device names. | |||
| If asInput is true, it shows the index in the inputs list, otherwise it | |||
| looks for it in the outputs list. | |||
| */ | |||
| virtual const String getDefaultDeviceName (const bool preferInputNames, | |||
| const int numInputChannelsNeeded, | |||
| const int numOutputChannelsNeeded) const = 0; | |||
| virtual int getIndexOfDevice (AudioIODevice* device, const bool asInput) const = 0; | |||
| /** Returns true if two different devices can be used for the input and output. | |||
| */ | |||
| virtual bool hasSeparateInputsAndOutputs() const = 0; | |||
| /** Creates one of the devices of this type. | |||
| The deviceName must be one of the strings returned by getDeviceNames(), and | |||
| scanForDevices() must have been called before this method is used. | |||
| */ | |||
| virtual AudioIODevice* createDevice (const String& deviceName) = 0; | |||
| virtual AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) = 0; | |||
| struct DeviceSetupDetails | |||
| { | |||
| AudioDeviceManager* manager; | |||
| int minNumInputChannels, maxNumInputChannels; | |||
| int minNumOutputChannels, maxNumOutputChannels; | |||
| bool useStereoPairs; | |||
| }; | |||
| /** Destructor. */ | |||
| virtual ~AudioIODeviceType(); | |||
| @@ -33561,52 +33578,41 @@ public: | |||
| */ | |||
| XmlElement* createStateXml() const; | |||
| /** Returns a list of the audio devices that can be used. | |||
| On Windows, this will include both DSound and ASIO devices if they are available. On | |||
| the Mac, it'll be a list of the CoreAudio devices. | |||
| These names are used by setAudioDevice() when changing devices. | |||
| /** | |||
| */ | |||
| const StringArray getAvailableAudioDeviceNames() const; | |||
| /** Rescans the list of known audio devices, in case it's changed. */ | |||
| void refreshDeviceList() const; | |||
| /** Sets a flag to indicate that when listing audio device names, it should treat them | |||
| as inputs rather than outputs. | |||
| This only really applies to DirectSound, where input and output devices are | |||
| separate. On ASIO and CoreAudio this makes no difference. | |||
| struct AudioDeviceSetup | |||
| { | |||
| AudioDeviceSetup(); | |||
| @see getAvailableAudioDeviceNames | |||
| */ | |||
| void setInputDeviceNamesUsed (const bool useInputNames); | |||
| bool operator== (const AudioDeviceSetup& other) const; | |||
| /** Just adds the list of device names to a combo box. | |||
| /** | |||
| */ | |||
| String outputDeviceName; | |||
| /** | |||
| */ | |||
| String inputDeviceName; | |||
| /** | |||
| */ | |||
| double sampleRate; | |||
| /** | |||
| */ | |||
| int bufferSize; | |||
| /** | |||
| */ | |||
| BitArray inputChannels; | |||
| bool useDefaultInputChannels; | |||
| /** | |||
| */ | |||
| BitArray outputChannels; | |||
| bool useDefaultOutputChannels; | |||
| }; | |||
| The only reason this is in this class is so that it can divide DSound | |||
| and ASIO devices into labelled sections, which makes it look much neater. | |||
| /** | |||
| */ | |||
| void addDeviceNamesToComboBox (ComboBox& combo) const; | |||
| /** Changes the audio device that should be used. | |||
| void getAudioDeviceSetup (AudioDeviceSetup& setup); | |||
| If deviceName is empty or not a valid name returned by getAvailableAudioDeviceNames(), | |||
| it will disable the current device. | |||
| @param deviceName the name of the device you want to use (or an empty string to | |||
| deselect the current device) | |||
| @param blockSizeToUse the samples-per-block you want to use, or 0 to use a default | |||
| value | |||
| @param sampleRateToUse the target sample-rate, or 0 to use a default that the device | |||
| is capable of | |||
| @param inputChans which of the audio device's input channels to open - pass 0 to | |||
| open as many of the the first ones as are needed for the number | |||
| of input channels that the app has requested | |||
| @param outputChans which of the audio device's input channels to open - pass 0 to | |||
| open as many of the the first ones as are needed for the number | |||
| of output channels that the app has requested | |||
| /** | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| @@ -33614,15 +33620,17 @@ public: | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @returns an error message if anything went wrong, or an empty string if it worked ok. | |||
| */ | |||
| const String setAudioDevice (const String& deviceName, | |||
| int blockSizeToUse, | |||
| double sampleRateToUse, | |||
| const BitArray* inputChans, | |||
| const BitArray* outputChans, | |||
| const bool treatAsChosenDevice); | |||
| const String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, | |||
| const bool treatAsChosenDevice); | |||
| /** Returns the currently-active audio device. */ | |||
| AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } | |||
| const String getCurrentAudioDeviceType() const throw() { return currentDeviceType; } | |||
| void setCurrentAudioDeviceType (const String& type, | |||
| const bool treatAsChosenDevice); | |||
| /** Closes the currently-open device. | |||
| @@ -33641,59 +33649,6 @@ public: | |||
| */ | |||
| void restartLastAudioDevice(); | |||
| /** Returns the name of the currently selected audio device. | |||
| This will be an empty string if none is active. | |||
| */ | |||
| const String getCurrentAudioDeviceName() const; | |||
| /** Returns the currently-active audio device. */ | |||
| AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } | |||
| /** Returns the set of audio input channels currently being used. | |||
| To select different channels, use setInputChannels(), or call setAudioDevice() to | |||
| reopen the device with a different set of channels. | |||
| */ | |||
| const BitArray getInputChannels() const throw() { return inputChannels; } | |||
| /** Changes the active set of input channels. | |||
| @param newEnabledChannels the set of channels to enable | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| will be returned. If it's false, then the device is treated as a | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @see getInputChannels | |||
| */ | |||
| void setInputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice); | |||
| /** Returns the set of audio output channels currently being used. | |||
| To select different channels, use setOutputChannels(), or call setAudioDevice() to | |||
| reopen the device with a different set of channels. | |||
| */ | |||
| const BitArray getOutputChannels() const throw() { return outputChannels; } | |||
| /** Changes the active set of output channels. | |||
| @param newEnabledChannels the set of channels to enable | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| will be returned. If it's false, then the device is treated as a | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @see getOutputChannels | |||
| */ | |||
| void setOutputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice); | |||
| /** Gives the manager an audio callback to use. | |||
| The manager will redirect callbacks from whatever audio device is currently | |||
| @@ -33751,7 +33706,8 @@ public: | |||
| /** Removes a listener that was previously registered with addMidiInputCallback(). | |||
| */ | |||
| void removeMidiInputCallback (MidiInputCallback* callback); | |||
| void removeMidiInputCallback (const String& midiInputDeviceName, | |||
| MidiInputCallback* callback); | |||
| /** Sets a midi output device to use as the default. | |||
| @@ -33782,19 +33738,69 @@ public: | |||
| */ | |||
| MidiOutput* getDefaultMidiOutput() const throw() { return defaultMidiOutput; } | |||
| /** | |||
| */ | |||
| const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes() const throw() { return availableDeviceTypes; } | |||
| /** Creates a list of available types. | |||
| This will add a set of new AudioIODeviceType objects to the specified list, to | |||
| represent each available types of device. | |||
| You can override this if your app needs to do something specific, like avoid | |||
| using DirectSound devices, etc. | |||
| */ | |||
| virtual void createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& types); | |||
| /** Plays a beep through the current audio device. | |||
| This is here to allow the audio setup UI panels to easily include a "test" | |||
| button so that the user can check where the audio is coming from. | |||
| */ | |||
| void playTestSound(); | |||
| /** Turns on level-measuring. | |||
| When enabled, the device manager will measure the peak input level | |||
| across all channels, and you can get this level by calling getCurrentInputLevel(). | |||
| This is mainly intended for audio setup UI panels to use to create a mic | |||
| level display, so that the user can check that they've selected the right | |||
| device. | |||
| A simple filter is used to make the level decay smoothly, but this is | |||
| only intended for giving rough feedback, and not for any kind of accurate | |||
| measurement. | |||
| */ | |||
| void enableInputLevelMeasurement (const bool enableMeasurement); | |||
| /** Returns the current input level. | |||
| To use this, you must first enable it by calling enableInputLevelMeasurement(). | |||
| See enableInputLevelMeasurement() for more info. | |||
| */ | |||
| double getCurrentInputLevel() const; | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| OwnedArray <AudioIODeviceType> availableDeviceTypes; | |||
| OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs; | |||
| AudioDeviceSetup currentSetup; | |||
| AudioIODevice* currentAudioDevice; | |||
| AudioIODeviceCallback* currentCallback; | |||
| int numInputChansNeeded, numOutputChansNeeded; | |||
| String currentDeviceType; | |||
| BitArray inputChannels, outputChannels; | |||
| XmlElement* lastExplicitSettings; | |||
| mutable bool listNeedsScanning; | |||
| bool useInputNames; | |||
| bool useInputNames, inputLevelMeasurementEnabled; | |||
| double inputLevel; | |||
| AudioSampleBuffer* testSound; | |||
| int testSoundPosition; | |||
| StringArray midiInsFromXml; | |||
| OwnedArray <MidiInput> enabledMidiInputs; | |||
| @@ -33826,11 +33832,6 @@ private: | |||
| }; | |||
| CallbackHandler callbackHandler; | |||
| String lastRunningDevice; | |||
| int lastRunningBlockSize; | |||
| double lastRunningSampleRate; | |||
| BitArray lastRunningIns, lastRunningOuts; | |||
| friend class CallbackHandler; | |||
| void audioDeviceIOCallbackInt (const float** inputChannelData, | |||
| @@ -33849,6 +33850,15 @@ private: | |||
| void updateXml(); | |||
| void createDeviceTypesIfNeeded(); | |||
| void scanDevicesIfNeeded(); | |||
| void deleteCurrentDevice(); | |||
| double chooseBestSampleRate (double preferred) const; | |||
| AudioIODeviceType* getCurrentDeviceTypeObject() const; | |||
| void insertDefaultDeviceNames (AudioDeviceSetup& setup) const; | |||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | |||
| AudioDeviceManager (const AudioDeviceManager&); | |||
| const AudioDeviceManager& operator= (const AudioDeviceManager&); | |||
| }; | |||
| @@ -49093,8 +49103,8 @@ private: | |||
| @see DirectoryContentsList, FileTreeComponent | |||
| */ | |||
| class JUCE_API FileListComponent : public DirectoryContentsDisplayComponent, | |||
| public ListBox, | |||
| class JUCE_API FileListComponent : public ListBox, | |||
| public DirectoryContentsDisplayComponent, | |||
| private ListBoxModel, | |||
| private ChangeListener | |||
| { | |||
| @@ -49454,8 +49464,8 @@ private: | |||
| @see DirectoryContentsList, FileListComponent | |||
| */ | |||
| class JUCE_API FileTreeComponent : public DirectoryContentsDisplayComponent, | |||
| public TreeView | |||
| class JUCE_API FileTreeComponent : public TreeView, | |||
| public DirectoryContentsDisplayComponent | |||
| { | |||
| public: | |||
| @@ -50416,7 +50426,7 @@ private: | |||
| #ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ | |||
| #define __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__ | |||
| class AudioDeviceSelectorComponentListBox; | |||
| class MidiInputSelectorComponentListBox; | |||
| /** | |||
| A component containing controls to let the user change the audio settings of | |||
| @@ -50467,23 +50477,22 @@ public: | |||
| /** @internal */ | |||
| void changeListenerCallback (void*); | |||
| /** Called by the device-specific displays to create a little level meter that | |||
| just displays the current total input levels from the given device manager. | |||
| */ | |||
| static Component* createSimpleLevelMeterComponent (AudioDeviceManager* managerToDisplay); | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| AudioDeviceManager& deviceManager; | |||
| ComboBox* audioDeviceDropDown; | |||
| ComboBox* deviceTypeDropDown; | |||
| Label* deviceTypeDropDownLabel; | |||
| Component* audioDeviceSettingsComp; | |||
| String audioDeviceSettingsCompType; | |||
| const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels; | |||
| ComboBox* sampleRateDropDown; | |||
| AudioDeviceSelectorComponentListBox* inputChansBox; | |||
| Label* inputsLabel; | |||
| AudioDeviceSelectorComponentListBox* outputChansBox; | |||
| Label* outputsLabel; | |||
| Label* sampleRateLabel; | |||
| ComboBox* bufferSizeDropDown; | |||
| Label* bufferSizeLabel; | |||
| Button* launchUIButton; | |||
| AudioDeviceSelectorComponentListBox* midiInputsList; | |||
| MidiInputSelectorComponentListBox* midiInputsList; | |||
| Label* midiInputsLabel; | |||
| ComboBox* midiOutputSelector; | |||
| Label* midiOutputLabel; | |||
| @@ -51562,7 +51571,7 @@ public: | |||
| @see newOpenGLContextCreated() | |||
| */ | |||
| OpenGLContext* getCurrentContext() const throw(); | |||
| OpenGLContext* getCurrentContext() const throw() { return context; } | |||
| /** Makes this component the current openGL context. | |||
| @@ -53340,23 +53349,24 @@ END_JUCE_NAMESPACE | |||
| #ifdef _CONSOLE | |||
| #define START_JUCE_APPLICATION(AppClass) \ | |||
| int main (int argc, char* argv[]) \ | |||
| int main (int, char* argv[]) \ | |||
| { \ | |||
| return JUCE_NAMESPACE::JUCEApplication::main (argc, argv, new AppClass()); \ | |||
| JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \ | |||
| return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \ | |||
| } | |||
| #elif ! defined (_AFXDLL) | |||
| #ifdef _WINDOWS_ | |||
| #define START_JUCE_APPLICATION(AppClass) \ | |||
| int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR commandLine, int) \ | |||
| int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int) \ | |||
| { \ | |||
| JUCE_NAMESPACE::String commandLineString (commandLine); \ | |||
| JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \ | |||
| return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \ | |||
| } | |||
| #else | |||
| #define START_JUCE_APPLICATION(AppClass) \ | |||
| int __stdcall WinMain (int, int, const char* commandLine, int) \ | |||
| int __stdcall WinMain (int, int, const char*, int) \ | |||
| { \ | |||
| JUCE_NAMESPACE::String commandLineString (commandLine); \ | |||
| JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \ | |||
| return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \ | |||
| } | |||
| #endif | |||
| @@ -36,8 +36,30 @@ BEGIN_JUCE_NAMESPACE | |||
| #include "juce_AudioDeviceManager.h" | |||
| #include "../../gui/components/juce_Desktop.h" | |||
| #include "../../../juce_core/text/juce_LocalisedStrings.h" | |||
| #include "../dsp/juce_AudioSampleBuffer.h" | |||
| //============================================================================== | |||
| AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup() | |||
| : sampleRate (0), | |||
| bufferSize (0), | |||
| useDefaultInputChannels (true), | |||
| useDefaultOutputChannels (true) | |||
| { | |||
| } | |||
| bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const | |||
| { | |||
| return outputDeviceName == other.outputDeviceName | |||
| && inputDeviceName == other.inputDeviceName | |||
| && sampleRate == other.sampleRate | |||
| && bufferSize == other.bufferSize | |||
| && inputChannels == other.inputChannels | |||
| && useDefaultInputChannels == other.useDefaultInputChannels | |||
| && outputChannels == other.outputChannels | |||
| && useDefaultOutputChannels == other.useDefaultOutputChannels; | |||
| } | |||
| //============================================================================== | |||
| AudioDeviceManager::AudioDeviceManager() | |||
| : currentAudioDevice (0), | |||
| @@ -47,6 +69,9 @@ AudioDeviceManager::AudioDeviceManager() | |||
| lastExplicitSettings (0), | |||
| listNeedsScanning (true), | |||
| useInputNames (false), | |||
| inputLevelMeasurementEnabled (false), | |||
| inputLevel (0), | |||
| testSound (0), | |||
| enabledMidiInputs (4), | |||
| midiCallbacks (4), | |||
| midiCallbackDevices (4), | |||
| @@ -55,18 +80,58 @@ AudioDeviceManager::AudioDeviceManager() | |||
| timeToCpuScale (0) | |||
| { | |||
| callbackHandler.owner = this; | |||
| AudioIODeviceType::createDeviceTypes (availableDeviceTypes); | |||
| } | |||
| AudioDeviceManager::~AudioDeviceManager() | |||
| { | |||
| stopDevice(); | |||
| deleteAndZero (currentAudioDevice); | |||
| deleteAndZero (defaultMidiOutput); | |||
| delete lastExplicitSettings; | |||
| delete testSound; | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::createDeviceTypesIfNeeded() | |||
| { | |||
| if (availableDeviceTypes.size() == 0) | |||
| { | |||
| createAudioDeviceTypes (availableDeviceTypes); | |||
| while (lastDeviceTypeConfigs.size() < availableDeviceTypes.size()) | |||
| lastDeviceTypeConfigs.add (new AudioDeviceSetup()); | |||
| if (availableDeviceTypes.size() > 0) | |||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| extern AudioIODeviceType* juce_createDefaultAudioIODeviceType(); | |||
| #if JUCE_WIN32 && JUCE_ASIO | |||
| extern AudioIODeviceType* juce_createASIOAudioIODeviceType(); | |||
| #endif | |||
| #if JUCE_WIN32 && JUCE_WDM_AUDIO | |||
| extern AudioIODeviceType* juce_createWDMAudioIODeviceType(); | |||
| #endif | |||
| void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& list) | |||
| { | |||
| AudioIODeviceType* const defaultDeviceType = juce_createDefaultAudioIODeviceType(); | |||
| if (defaultDeviceType != 0) | |||
| list.add (defaultDeviceType); | |||
| #if JUCE_WIN32 && JUCE_ASIO | |||
| list.add (juce_createASIOAudioIODeviceType()); | |||
| #endif | |||
| #if JUCE_WIN32 && JUCE_WDM_AUDIO | |||
| list.add (juce_createWDMAudioIODeviceType()); | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||
| @@ -75,26 +140,51 @@ const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||
| const bool selectDefaultDeviceOnFailure, | |||
| const String& preferredDefaultDeviceName) | |||
| { | |||
| if (listNeedsScanning) | |||
| refreshDeviceList(); | |||
| scanDevicesIfNeeded(); | |||
| numInputChansNeeded = numInputChannelsNeeded; | |||
| numOutputChansNeeded = numOutputChannelsNeeded; | |||
| if (e != 0 && e->hasTagName (T("DEVICESETUP"))) | |||
| { | |||
| delete lastExplicitSettings; | |||
| lastExplicitSettings = new XmlElement (*e); | |||
| BitArray ins, outs; | |||
| ins.parseString (e->getStringAttribute (T("audioDeviceInChans"), T("11")), 2); | |||
| outs.parseString (e->getStringAttribute (T("audioDeviceOutChans"), T("11")), 2); | |||
| String error; | |||
| AudioDeviceSetup setup; | |||
| if (e->getStringAttribute (T("audioDeviceName")).isNotEmpty()) | |||
| { | |||
| setup.inputDeviceName = setup.outputDeviceName | |||
| = e->getStringAttribute (T("audioDeviceName")); | |||
| } | |||
| else | |||
| { | |||
| setup.inputDeviceName = e->getStringAttribute (T("audioInputDeviceName")); | |||
| setup.outputDeviceName = e->getStringAttribute (T("audioOutputDeviceName")); | |||
| } | |||
| currentDeviceType = e->getStringAttribute (T("deviceType")); | |||
| if (currentDeviceType.isEmpty()) | |||
| { | |||
| AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName); | |||
| if (type != 0) | |||
| currentDeviceType = type->getTypeName(); | |||
| else if (availableDeviceTypes.size() > 0) | |||
| currentDeviceType = availableDeviceTypes[0]->getTypeName(); | |||
| } | |||
| setup.bufferSize = e->getIntAttribute (T("audioDeviceBufferSize")); | |||
| setup.sampleRate = e->getDoubleAttribute (T("audioDeviceRate")); | |||
| setup.inputChannels.parseString (e->getStringAttribute (T("audioDeviceInChans"), T("11")), 2); | |||
| setup.outputChannels.parseString (e->getStringAttribute (T("audioDeviceOutChans"), T("11")), 2); | |||
| String error (setAudioDevice (e->getStringAttribute (T("audioDeviceName")), | |||
| e->getIntAttribute (T("audioDeviceBufferSize")), | |||
| e->getDoubleAttribute (T("audioDeviceRate")), | |||
| e->hasAttribute (T("audioDeviceInChans")) ? &ins : 0, | |||
| e->hasAttribute (T("audioDeviceOutChans")) ? &outs : 0, | |||
| true)); | |||
| setup.useDefaultInputChannels = ! e->hasAttribute (T("audioDeviceInChans")); | |||
| setup.useDefaultOutputChannels = ! e->hasAttribute (T("audioDeviceOutChans")); | |||
| error = setAudioDeviceSetup (setup, true); | |||
| midiInsFromXml.clear(); | |||
| forEachXmlChildElementWithTagName (*e, c, T("MIDIINPUT")) | |||
| @@ -115,33 +205,54 @@ const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||
| } | |||
| else | |||
| { | |||
| setInputDeviceNamesUsed (numOutputChannelsNeeded == 0); | |||
| String defaultDevice; | |||
| AudioDeviceSetup setup; | |||
| if (preferredDefaultDeviceName.isNotEmpty()) | |||
| { | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| for (int j = availableDeviceTypes.size(); --j >= 0;) | |||
| { | |||
| const StringArray devs (availableDeviceTypes.getUnchecked(i)->getDeviceNames()); | |||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); | |||
| StringArray outs (type->getDeviceNames (false)); | |||
| int i; | |||
| for (i = 0; i < outs.size(); ++i) | |||
| { | |||
| if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||
| { | |||
| setup.outputDeviceName = outs[i]; | |||
| break; | |||
| } | |||
| } | |||
| for (int j = 0; j < devs.size(); ++j) | |||
| StringArray ins (type->getDeviceNames (true)); | |||
| for (i = 0; i < ins.size(); ++i) | |||
| { | |||
| if (devs[j].matchesWildcard (preferredDefaultDeviceName, true)) | |||
| if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||
| { | |||
| defaultDevice = devs[j]; | |||
| setup.inputDeviceName = ins[i]; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if (defaultDevice.isEmpty() && availableDeviceTypes [0] != 0) | |||
| defaultDevice = availableDeviceTypes[0]->getDefaultDeviceName (numOutputChannelsNeeded == 0, | |||
| numInputChannelsNeeded, | |||
| numOutputChannelsNeeded); | |||
| insertDefaultDeviceNames (setup); | |||
| return setAudioDeviceSetup (setup, false); | |||
| } | |||
| } | |||
| void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const | |||
| { | |||
| AudioIODeviceType* type = getCurrentDeviceTypeObject(); | |||
| if (type != 0) | |||
| { | |||
| if (setup.outputDeviceName.isEmpty()) | |||
| setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; | |||
| return setAudioDevice (defaultDevice, 0, 0, 0, 0, false); | |||
| if (setup.inputDeviceName.isEmpty()) | |||
| setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)]; | |||
| } | |||
| } | |||
| @@ -151,144 +262,372 @@ XmlElement* AudioDeviceManager::createStateXml() const | |||
| } | |||
| //============================================================================== | |||
| const StringArray AudioDeviceManager::getAvailableAudioDeviceNames() const | |||
| void AudioDeviceManager::scanDevicesIfNeeded() | |||
| { | |||
| if (listNeedsScanning) | |||
| refreshDeviceList(); | |||
| { | |||
| listNeedsScanning = false; | |||
| StringArray names; | |||
| createDeviceTypesIfNeeded(); | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| names.addArray (availableDeviceTypes[i]->getDeviceNames (useInputNames)); | |||
| for (int i = availableDeviceTypes.size(); --i >= 0;) | |||
| availableDeviceTypes.getUnchecked(i)->scanForDevices(); | |||
| } | |||
| } | |||
| AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName) | |||
| { | |||
| scanDevicesIfNeeded(); | |||
| for (int i = availableDeviceTypes.size(); --i >= 0;) | |||
| { | |||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i); | |||
| return names; | |||
| if ((inputName.isNotEmpty() && type->getDeviceNames (true).contains (inputName, true)) | |||
| || (outputName.isNotEmpty() && type->getDeviceNames (false).contains (outputName, true))) | |||
| { | |||
| return type; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| void AudioDeviceManager::refreshDeviceList() const | |||
| void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) | |||
| { | |||
| listNeedsScanning = false; | |||
| setup = currentSetup; | |||
| } | |||
| void AudioDeviceManager::deleteCurrentDevice() | |||
| { | |||
| deleteAndZero (currentAudioDevice); | |||
| currentSetup.inputDeviceName = String::empty; | |||
| currentSetup.outputDeviceName = String::empty; | |||
| } | |||
| void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| availableDeviceTypes[i]->scanForDevices(); | |||
| { | |||
| if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type | |||
| && currentDeviceType != type) | |||
| { | |||
| currentDeviceType = type; | |||
| AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i)); | |||
| insertDefaultDeviceNames (s); | |||
| setAudioDeviceSetup (s, treatAsChosenDevice); | |||
| sendChangeMessage (this); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| void AudioDeviceManager::setInputDeviceNamesUsed (const bool useInputNames_) | |||
| AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const | |||
| { | |||
| useInputNames = useInputNames_; | |||
| sendChangeMessage (this); | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| if (availableDeviceTypes[i]->getTypeName() == currentDeviceType) | |||
| return availableDeviceTypes[i]; | |||
| return availableDeviceTypes[0]; | |||
| } | |||
| void AudioDeviceManager::addDeviceNamesToComboBox (ComboBox& combo) const | |||
| const String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| int n = 0; | |||
| jassert (&newSetup != ¤tSetup); // this will have no effect | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| if (newSetup == currentSetup && currentAudioDevice != 0) | |||
| return String::empty; | |||
| if (! (newSetup == currentSetup)) | |||
| sendChangeMessage (this); | |||
| stopDevice(); | |||
| String error; | |||
| AudioIODeviceType* type = getCurrentDeviceTypeObject(); | |||
| if (type == 0 || (newSetup.inputDeviceName.isEmpty() | |||
| && newSetup.outputDeviceName.isEmpty())) | |||
| { | |||
| AudioIODeviceType* const type = availableDeviceTypes[i]; | |||
| deleteCurrentDevice(); | |||
| if (treatAsChosenDevice) | |||
| updateXml(); | |||
| if (availableDeviceTypes.size() > 1) | |||
| combo.addSectionHeading (type->getTypeName() + T(" devices:")); | |||
| return String::empty; | |||
| } | |||
| const StringArray names (type->getDeviceNames (useInputNames)); | |||
| if (currentSetup.inputDeviceName != newSetup.inputDeviceName | |||
| || currentSetup.outputDeviceName != newSetup.outputDeviceName | |||
| || currentAudioDevice == 0) | |||
| { | |||
| deleteCurrentDevice(); | |||
| scanDevicesIfNeeded(); | |||
| for (int j = 0; j < names.size(); ++j) | |||
| combo.addItem (names[j], ++n); | |||
| if (newSetup.outputDeviceName.isNotEmpty() | |||
| && ! type->getDeviceNames (false).contains (newSetup.outputDeviceName)) | |||
| { | |||
| return "No such device: " + newSetup.outputDeviceName; | |||
| } | |||
| combo.addSeparator(); | |||
| if (newSetup.inputDeviceName.isNotEmpty() | |||
| && ! type->getDeviceNames (true).contains (newSetup.inputDeviceName)) | |||
| { | |||
| return "No such device: " + newSetup.outputDeviceName; | |||
| } | |||
| currentAudioDevice = type->createDevice (newSetup.outputDeviceName, | |||
| newSetup.inputDeviceName); | |||
| if (currentAudioDevice == 0) | |||
| error = "Can't open device"; | |||
| else | |||
| error = currentAudioDevice->getLastError(); | |||
| if (error.isNotEmpty()) | |||
| { | |||
| deleteCurrentDevice(); | |||
| return error; | |||
| } | |||
| inputChannels.clear(); | |||
| inputChannels.setRange (0, numInputChansNeeded, true); | |||
| outputChannels.clear(); | |||
| outputChannels.setRange (0, numOutputChansNeeded, true); | |||
| } | |||
| else | |||
| { | |||
| if (! newSetup.useDefaultInputChannels) | |||
| inputChannels = newSetup.inputChannels; | |||
| if (! newSetup.useDefaultOutputChannels) | |||
| outputChannels = newSetup.outputChannels; | |||
| } | |||
| combo.addItem (TRANS("<< no audio device >>"), -1); | |||
| currentSetup = newSetup; | |||
| currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate); | |||
| error = currentAudioDevice->open (inputChannels, | |||
| outputChannels, | |||
| currentSetup.sampleRate, | |||
| currentSetup.bufferSize); | |||
| if (error.isEmpty()) | |||
| { | |||
| currentDeviceType = currentAudioDevice->getTypeName(); | |||
| currentAudioDevice->start (&callbackHandler); | |||
| currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); | |||
| currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); | |||
| currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType) | |||
| *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup; | |||
| if (treatAsChosenDevice) | |||
| updateXml(); | |||
| } | |||
| else | |||
| { | |||
| deleteCurrentDevice(); | |||
| } | |||
| return error; | |||
| } | |||
| const String AudioDeviceManager::getCurrentAudioDeviceName() const | |||
| double AudioDeviceManager::chooseBestSampleRate (double rate) const | |||
| { | |||
| if (currentAudioDevice != 0) | |||
| return currentAudioDevice->getName(); | |||
| jassert (currentAudioDevice != 0); | |||
| if (rate > 0) | |||
| { | |||
| bool ok = false; | |||
| for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) | |||
| { | |||
| const double sr = currentAudioDevice->getSampleRate (i); | |||
| if (sr == rate) | |||
| ok = true; | |||
| } | |||
| if (! ok) | |||
| rate = 0; | |||
| } | |||
| if (rate == 0) | |||
| { | |||
| double lowestAbove44 = 0.0; | |||
| for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) | |||
| { | |||
| const double sr = currentAudioDevice->getSampleRate (i); | |||
| if (sr >= 44100.0 && (lowestAbove44 == 0 || sr < lowestAbove44)) | |||
| lowestAbove44 = sr; | |||
| } | |||
| if (lowestAbove44 == 0.0) | |||
| rate = currentAudioDevice->getSampleRate (0); | |||
| else | |||
| rate = lowestAbove44; | |||
| } | |||
| return String::empty; | |||
| return rate; | |||
| } | |||
| const String AudioDeviceManager::setAudioDevice (const String& deviceNameToUse, | |||
| int blockSizeToUse, | |||
| double sampleRateToUse, | |||
| const BitArray* inChans, | |||
| const BitArray* outChans, | |||
| const bool treatAsChosenDevice) | |||
| /*const String AudioDeviceManager::setAudioDevices (const String& outputDeviceName, | |||
| const String& inputDeviceName, | |||
| int blockSizeToUse, | |||
| double sampleRateToUse, | |||
| const BitArray* inChans, | |||
| const BitArray* outChans, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| stopDevice(); | |||
| String error; | |||
| if (deviceNameToUse.isNotEmpty()) | |||
| if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
| { | |||
| const StringArray devNames (getAvailableAudioDeviceNames()); | |||
| const StringArray outputNames (getAvailableAudioOutputDeviceNames()); | |||
| int outputIndex = outputNames.indexOf (outputDeviceName); | |||
| if (outputIndex < 0 && outputDeviceName.isNotEmpty()) | |||
| { | |||
| deleteDevices(); | |||
| error << "No such device: " << outputDeviceName; | |||
| return error; | |||
| } | |||
| int index = devNames.indexOf (deviceNameToUse, true); | |||
| const StringArray inputNames (getAvailableAudioInputDeviceNames()); | |||
| int inputIndex = inputNames.indexOf (inputDeviceName); | |||
| if (inputIndex < 0 && inputDeviceName.isNotEmpty()) | |||
| { | |||
| deleteDevices(); | |||
| error << "No such device: " << inputDeviceName; | |||
| return error; | |||
| } | |||
| if (index >= 0) | |||
| if (currentAudioInputDevice == 0 | |||
| || currentAudioInputDevice->getLastError().isNotEmpty() | |||
| || ! inputDeviceName.equalsIgnoreCase (currentAudioInputDevice->getName()) | |||
| || currentAudioOutputDevice == 0 | |||
| || currentAudioOutputDevice->getLastError().isNotEmpty() | |||
| || ! outputDeviceName.equalsIgnoreCase (currentAudioOutputDevice->getName())) | |||
| { | |||
| if (currentAudioDevice == 0 | |||
| || currentAudioDevice->getLastError().isNotEmpty() | |||
| || ! deviceNameToUse.equalsIgnoreCase (currentAudioDevice->getName())) | |||
| // change of device.. | |||
| deleteDevices(); | |||
| int n = 0; | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| { | |||
| // change of device.. | |||
| deleteAndZero (currentAudioDevice); | |||
| AudioIODeviceType* const type = availableDeviceTypes[i]; | |||
| const StringArray names (type->getDeviceNames (true)); | |||
| if (inputIndex >= n && inputIndex < n + names.size()) | |||
| { | |||
| currentAudioInputDevice = type->createDevice (inputDeviceName); | |||
| if (currentAudioInputDevice == 0) | |||
| error = "Can't open device: " + inputDeviceName; | |||
| else | |||
| error = currentAudioInputDevice->getLastError(); | |||
| if (error.isNotEmpty()) | |||
| { | |||
| deleteDevices(); | |||
| return error; | |||
| } | |||
| break; | |||
| } | |||
| int n = 0; | |||
| n += names.size(); | |||
| } | |||
| //xxx | |||
| if (true)//inputDeviceName != outputDeviceName) | |||
| { | |||
| // Using different input and output devices.. | |||
| n = 0; | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| { | |||
| AudioIODeviceType* const type = availableDeviceTypes[i]; | |||
| const StringArray names (type->getDeviceNames (useInputNames)); | |||
| const StringArray names (type->getDeviceNames (false)); | |||
| if (index >= n && index < n + names.size()) | |||
| if (outputIndex >= n && outputIndex < n + names.size()) | |||
| { | |||
| currentAudioDevice = type->createDevice (deviceNameToUse); | |||
| currentAudioOutputDevice = type->createDevice (outputDeviceName); | |||
| if (currentAudioOutputDevice == 0) | |||
| error = "Can't open device: " + outputDeviceName; | |||
| else | |||
| error = currentAudioOutputDevice->getLastError(); | |||
| if (error.isNotEmpty()) | |||
| { | |||
| deleteDevices(); | |||
| return error; | |||
| } | |||
| break; | |||
| } | |||
| n += names.size(); | |||
| } | |||
| error = currentAudioDevice->getLastError(); | |||
| if (error.isNotEmpty()) | |||
| if (currentAudioInputDevice != 0 && currentAudioOutputDevice != 0) | |||
| { | |||
| deleteAndZero (currentAudioDevice); | |||
| return error; | |||
| //xxx enable this optim | |||
| } | |||
| inputChannels.clear(); | |||
| inputChannels.setRange (0, numInputChansNeeded, true); | |||
| outputChannels.clear(); | |||
| outputChannels.setRange (0, numOutputChansNeeded, true); | |||
| ConglomeratingAudioIODevice* const combiner = new ConglomeratingAudioIODevice(); | |||
| combiner->addDevice (currentAudioInputDevice, false, true); | |||
| combiner->addDevice (currentAudioOutputDevice, true, false); | |||
| currentAudioDevice = combiner; | |||
| } | |||
| else | |||
| { | |||
| // Using a single device for in + out.. | |||
| currentAudioOutputDevice = currentAudioInputDevice; | |||
| currentAudioDevice = currentAudioInputDevice; | |||
| } | |||
| if (inChans != 0) | |||
| inputChannels = *inChans; | |||
| inputChannels.clear(); | |||
| inputChannels.setRange (0, numInputChansNeeded, true); | |||
| outputChannels.clear(); | |||
| outputChannels.setRange (0, numOutputChansNeeded, true); | |||
| } | |||
| if (outChans != 0) | |||
| outputChannels = *outChans; | |||
| if (inChans != 0) | |||
| inputChannels = *inChans; | |||
| error = restartDevice (blockSizeToUse, | |||
| sampleRateToUse, | |||
| inputChannels, | |||
| outputChannels); | |||
| if (outChans != 0) | |||
| outputChannels = *outChans; | |||
| if (error.isNotEmpty()) | |||
| { | |||
| deleteAndZero (currentAudioDevice); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| deleteAndZero (currentAudioDevice); | |||
| error << "No such device: " << deviceNameToUse; | |||
| } | |||
| error = restartDevice (blockSizeToUse, | |||
| sampleRateToUse, | |||
| inputChannels, | |||
| outputChannels); | |||
| if (error.isNotEmpty()) | |||
| deleteDevices(); | |||
| } | |||
| else | |||
| { | |||
| deleteAndZero (currentAudioDevice); | |||
| deleteDevices(); | |||
| } | |||
| if (treatAsChosenDevice && error.isEmpty()) | |||
| @@ -342,7 +681,7 @@ const String AudioDeviceManager::restartDevice (int blockSizeToUse, | |||
| } | |||
| const String error (currentAudioDevice->open (inChans, outChans, | |||
| sampleRateToUse, blockSizeToUse)); | |||
| sampleRateToUse, blockSizeToUse)); | |||
| if (error.isEmpty()) | |||
| currentAudioDevice->start (&callbackHandler); | |||
| @@ -350,7 +689,7 @@ const String AudioDeviceManager::restartDevice (int blockSizeToUse, | |||
| sendChangeMessage (this); | |||
| return error; | |||
| } | |||
| */ | |||
| void AudioDeviceManager::stopDevice() | |||
| { | |||
| if (currentAudioDevice != 0) | |||
| @@ -359,25 +698,16 @@ void AudioDeviceManager::stopDevice() | |||
| void AudioDeviceManager::closeAudioDevice() | |||
| { | |||
| if (currentAudioDevice != 0) | |||
| { | |||
| lastRunningDevice = currentAudioDevice->getName(); | |||
| lastRunningBlockSize = currentAudioDevice->getCurrentBufferSizeSamples(); | |||
| lastRunningSampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| lastRunningIns = inputChannels; | |||
| lastRunningOuts = outputChannels; | |||
| stopDevice(); | |||
| setAudioDevice (String::empty, 0, 0, 0, 0, false); | |||
| } | |||
| stopDevice(); | |||
| deleteAndZero (currentAudioDevice); | |||
| } | |||
| void AudioDeviceManager::restartLastAudioDevice() | |||
| { | |||
| if (currentAudioDevice == 0) | |||
| { | |||
| if (lastRunningDevice.isEmpty()) | |||
| if (currentSetup.inputDeviceName.isEmpty() | |||
| && currentSetup.outputDeviceName.isEmpty()) | |||
| { | |||
| // This method will only reload the last device that was running | |||
| // before closeAudioDevice() was called - you need to actually open | |||
| @@ -386,50 +716,19 @@ void AudioDeviceManager::restartLastAudioDevice() | |||
| return; | |||
| } | |||
| setAudioDevice (lastRunningDevice, | |||
| lastRunningBlockSize, | |||
| lastRunningSampleRate, | |||
| &lastRunningIns, | |||
| &lastRunningOuts, | |||
| false); | |||
| } | |||
| } | |||
| void AudioDeviceManager::setInputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| if (currentAudioDevice != 0 | |||
| && newEnabledChannels != inputChannels) | |||
| { | |||
| setAudioDevice (currentAudioDevice->getName(), | |||
| currentAudioDevice->getCurrentBufferSizeSamples(), | |||
| currentAudioDevice->getCurrentSampleRate(), | |||
| &newEnabledChannels, 0, | |||
| treatAsChosenDevice); | |||
| } | |||
| } | |||
| void AudioDeviceManager::setOutputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| if (currentAudioDevice != 0 | |||
| && newEnabledChannels != outputChannels) | |||
| { | |||
| setAudioDevice (currentAudioDevice->getName(), | |||
| currentAudioDevice->getCurrentBufferSizeSamples(), | |||
| currentAudioDevice->getCurrentSampleRate(), | |||
| 0, &newEnabledChannels, | |||
| treatAsChosenDevice); | |||
| AudioDeviceSetup s (currentSetup); | |||
| setAudioDeviceSetup (s, false); | |||
| } | |||
| } | |||
| void AudioDeviceManager::updateXml() | |||
| { | |||
| delete lastExplicitSettings; | |||
| lastExplicitSettings = new XmlElement (T("DEVICESETUP")); | |||
| lastExplicitSettings->setAttribute (T("audioDeviceName"), getCurrentAudioDeviceName()); | |||
| lastExplicitSettings->setAttribute (T("deviceType"), currentDeviceType); | |||
| lastExplicitSettings->setAttribute (T("audioOutputDeviceName"), currentSetup.outputDeviceName); | |||
| lastExplicitSettings->setAttribute (T("audioInputDeviceName"), currentSetup.inputDeviceName); | |||
| if (currentAudioDevice != 0) | |||
| { | |||
| @@ -438,8 +737,11 @@ void AudioDeviceManager::updateXml() | |||
| if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples()) | |||
| lastExplicitSettings->setAttribute (T("audioDeviceBufferSize"), currentAudioDevice->getCurrentBufferSizeSamples()); | |||
| lastExplicitSettings->setAttribute (T("audioDeviceInChans"), inputChannels.toString (2)); | |||
| lastExplicitSettings->setAttribute (T("audioDeviceOutChans"), outputChannels.toString (2)); | |||
| if (! currentSetup.useDefaultInputChannels) | |||
| lastExplicitSettings->setAttribute (T("audioDeviceInChans"), currentSetup.inputChannels.toString (2)); | |||
| if (! currentSetup.useDefaultOutputChannels) | |||
| lastExplicitSettings->setAttribute (T("audioDeviceOutChans"), currentSetup.outputChannels.toString (2)); | |||
| } | |||
| for (int i = 0; i < enabledMidiInputs.size(); ++i) | |||
| @@ -497,21 +799,43 @@ void AudioDeviceManager::setAudioCallback (AudioIODeviceCallback* newCallback) | |||
| } | |||
| void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData, | |||
| int totalNumInputChannels, | |||
| int numInputChannels, | |||
| float** outputChannelData, | |||
| int totalNumOutputChannels, | |||
| int numOutputChannels, | |||
| int numSamples) | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| if (inputLevelMeasurementEnabled) | |||
| { | |||
| for (int j = 0; j < numSamples; ++j) | |||
| { | |||
| float s = 0; | |||
| for (int i = 0; i < numInputChannels; ++i) | |||
| s += fabsf (inputChannelData[i][j]); | |||
| s /= numInputChannels; | |||
| const double decayFactor = 0.99992; | |||
| if (s > inputLevel) | |||
| inputLevel = s; | |||
| else if (inputLevel > 0.001f) | |||
| inputLevel *= decayFactor; | |||
| else | |||
| inputLevel = 0; | |||
| } | |||
| } | |||
| if (currentCallback != 0) | |||
| { | |||
| const double callbackStartTime = Time::getMillisecondCounterHiRes(); | |||
| currentCallback->audioDeviceIOCallback (inputChannelData, | |||
| totalNumInputChannels, | |||
| numInputChannels, | |||
| outputChannelData, | |||
| totalNumOutputChannels, | |||
| numOutputChannels, | |||
| numSamples); | |||
| const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime; | |||
| @@ -520,9 +844,25 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < totalNumOutputChannels; ++i) | |||
| if (outputChannelData [i] != 0) | |||
| zeromem (outputChannelData[i], sizeof (float) * numSamples); | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| zeromem (outputChannelData[i], sizeof (float) * numSamples); | |||
| } | |||
| if (testSound != 0) | |||
| { | |||
| const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); | |||
| const float* const src = testSound->getSampleData (0, testSoundPosition); | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| for (int j = 0; j < numSamps; ++j) | |||
| outputChannelData [i][j] += src[j]; | |||
| testSoundPosition += numSamps; | |||
| if (testSoundPosition >= testSound->getNumSamples()) | |||
| { | |||
| delete testSound; | |||
| testSound = 0; | |||
| } | |||
| } | |||
| } | |||
| @@ -605,7 +945,7 @@ bool AudioDeviceManager::isMidiInputEnabled (const String& name) const | |||
| void AudioDeviceManager::addMidiInputCallback (const String& name, | |||
| MidiInputCallback* callback) | |||
| { | |||
| removeMidiInputCallback (callback); | |||
| removeMidiInputCallback (name, callback); | |||
| if (name.isEmpty()) | |||
| { | |||
| @@ -632,13 +972,24 @@ void AudioDeviceManager::addMidiInputCallback (const String& name, | |||
| } | |||
| } | |||
| void AudioDeviceManager::removeMidiInputCallback (MidiInputCallback* callback) | |||
| void AudioDeviceManager::removeMidiInputCallback (const String& name, | |||
| MidiInputCallback* /*callback*/) | |||
| { | |||
| const ScopedLock sl (midiCallbackLock); | |||
| const int index = midiCallbacks.indexOf (callback); | |||
| midiCallbacks.remove (index); | |||
| midiCallbackDevices.remove (index); | |||
| for (int i = midiCallbacks.size(); --i >= 0;) | |||
| { | |||
| String devName; | |||
| if (midiCallbackDevices.getUnchecked(i) != 0) | |||
| devName = midiCallbackDevices.getUnchecked(i)->getName(); | |||
| if (devName == name) | |||
| { | |||
| midiCallbacks.remove (i); | |||
| midiCallbackDevices.remove (i); | |||
| } | |||
| } | |||
| } | |||
| void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, | |||
| @@ -678,12 +1029,12 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||
| //============================================================================== | |||
| void AudioDeviceManager::CallbackHandler::audioDeviceIOCallback (const float** inputChannelData, | |||
| int totalNumInputChannels, | |||
| int numInputChannels, | |||
| float** outputChannelData, | |||
| int totalNumOutputChannels, | |||
| int numOutputChannels, | |||
| int numSamples) | |||
| { | |||
| owner->audioDeviceIOCallbackInt (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples); | |||
| owner->audioDeviceIOCallbackInt (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); | |||
| } | |||
| void AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart (AudioIODevice* device) | |||
| @@ -701,5 +1052,56 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* | |||
| owner->handleIncomingMidiMessageInt (source, message); | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::playTestSound() | |||
| { | |||
| audioCallbackLock.enter(); | |||
| AudioSampleBuffer* oldSound = testSound; | |||
| testSound = 0; | |||
| audioCallbackLock.exit(); | |||
| delete oldSound; | |||
| testSoundPosition = 0; | |||
| if (currentAudioDevice != 0) | |||
| { | |||
| const double sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| const int soundLength = (int) sampleRate; | |||
| AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); | |||
| float* samples = newSound->getSampleData (0); | |||
| const double frequency = MidiMessage::getMidiNoteInHertz (80); | |||
| const float amplitude = 0.5f; | |||
| const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); | |||
| for (int i = 0; i < soundLength; ++i) | |||
| samples[i] = amplitude * (float) sin (i * phasePerSample); | |||
| newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
| newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
| const ScopedLock sl (audioCallbackLock); | |||
| testSound = newSound; | |||
| } | |||
| } | |||
| void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement) | |||
| { | |||
| if (inputLevelMeasurementEnabled != enableMeasurement) | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| inputLevelMeasurementEnabled = enableMeasurement; | |||
| inputLevel = 0; | |||
| } | |||
| } | |||
| double AudioDeviceManager::getCurrentInputLevel() const | |||
| { | |||
| jassert (inputLevelMeasurementEnabled); // you need to call enableInputLevelMeasurement() before using this! | |||
| return inputLevel; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -37,6 +37,7 @@ | |||
| #include "juce_MidiOutput.h" | |||
| #include "../../../juce_core/text/juce_XmlElement.h" | |||
| #include "../../gui/components/controls/juce_ComboBox.h" | |||
| #include "../dsp/juce_AudioSampleBuffer.h" | |||
| //============================================================================== | |||
| @@ -130,54 +131,42 @@ public: | |||
| */ | |||
| XmlElement* createStateXml() const; | |||
| //============================================================================== | |||
| /** Returns a list of the audio devices that can be used. | |||
| On Windows, this will include both DSound and ASIO devices if they are available. On | |||
| the Mac, it'll be a list of the CoreAudio devices. | |||
| These names are used by setAudioDevice() when changing devices. | |||
| /** | |||
| */ | |||
| const StringArray getAvailableAudioDeviceNames() const; | |||
| /** Rescans the list of known audio devices, in case it's changed. */ | |||
| void refreshDeviceList() const; | |||
| /** Sets a flag to indicate that when listing audio device names, it should treat them | |||
| as inputs rather than outputs. | |||
| This only really applies to DirectSound, where input and output devices are | |||
| separate. On ASIO and CoreAudio this makes no difference. | |||
| struct AudioDeviceSetup | |||
| { | |||
| AudioDeviceSetup(); | |||
| bool operator== (const AudioDeviceSetup& other) const; | |||
| /** | |||
| */ | |||
| String outputDeviceName; | |||
| /** | |||
| */ | |||
| String inputDeviceName; | |||
| /** | |||
| */ | |||
| double sampleRate; | |||
| /** | |||
| */ | |||
| int bufferSize; | |||
| /** | |||
| */ | |||
| BitArray inputChannels; | |||
| bool useDefaultInputChannels; | |||
| /** | |||
| */ | |||
| BitArray outputChannels; | |||
| bool useDefaultOutputChannels; | |||
| }; | |||
| @see getAvailableAudioDeviceNames | |||
| /** | |||
| */ | |||
| void setInputDeviceNamesUsed (const bool useInputNames); | |||
| /** Just adds the list of device names to a combo box. | |||
| void getAudioDeviceSetup (AudioDeviceSetup& setup); | |||
| The only reason this is in this class is so that it can divide DSound | |||
| and ASIO devices into labelled sections, which makes it look much neater. | |||
| */ | |||
| void addDeviceNamesToComboBox (ComboBox& combo) const; | |||
| /** Changes the audio device that should be used. | |||
| If deviceName is empty or not a valid name returned by getAvailableAudioDeviceNames(), | |||
| it will disable the current device. | |||
| @param deviceName the name of the device you want to use (or an empty string to | |||
| deselect the current device) | |||
| @param blockSizeToUse the samples-per-block you want to use, or 0 to use a default | |||
| value | |||
| @param sampleRateToUse the target sample-rate, or 0 to use a default that the device | |||
| is capable of | |||
| @param inputChans which of the audio device's input channels to open - pass 0 to | |||
| open as many of the the first ones as are needed for the number | |||
| of input channels that the app has requested | |||
| @param outputChans which of the audio device's input channels to open - pass 0 to | |||
| open as many of the the first ones as are needed for the number | |||
| of output channels that the app has requested | |||
| /** | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| @@ -185,15 +174,18 @@ public: | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @returns an error message if anything went wrong, or an empty string if it worked ok. | |||
| */ | |||
| const String setAudioDevice (const String& deviceName, | |||
| int blockSizeToUse, | |||
| double sampleRateToUse, | |||
| const BitArray* inputChans, | |||
| const BitArray* outputChans, | |||
| const bool treatAsChosenDevice); | |||
| const String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, | |||
| const bool treatAsChosenDevice); | |||
| /** Returns the currently-active audio device. */ | |||
| AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } | |||
| const String getCurrentAudioDeviceType() const throw() { return currentDeviceType; } | |||
| void setCurrentAudioDeviceType (const String& type, | |||
| const bool treatAsChosenDevice); | |||
| /** Closes the currently-open device. | |||
| @@ -212,59 +204,7 @@ public: | |||
| */ | |||
| void restartLastAudioDevice(); | |||
| /** Returns the name of the currently selected audio device. | |||
| This will be an empty string if none is active. | |||
| */ | |||
| const String getCurrentAudioDeviceName() const; | |||
| /** Returns the currently-active audio device. */ | |||
| AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; } | |||
| /** Returns the set of audio input channels currently being used. | |||
| To select different channels, use setInputChannels(), or call setAudioDevice() to | |||
| reopen the device with a different set of channels. | |||
| */ | |||
| const BitArray getInputChannels() const throw() { return inputChannels; } | |||
| /** Changes the active set of input channels. | |||
| @param newEnabledChannels the set of channels to enable | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| will be returned. If it's false, then the device is treated as a | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @see getInputChannels | |||
| */ | |||
| void setInputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice); | |||
| /** Returns the set of audio output channels currently being used. | |||
| To select different channels, use setOutputChannels(), or call setAudioDevice() to | |||
| reopen the device with a different set of channels. | |||
| */ | |||
| const BitArray getOutputChannels() const throw() { return outputChannels; } | |||
| /** Changes the active set of output channels. | |||
| @param newEnabledChannels the set of channels to enable | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| will be returned. If it's false, then the device is treated as a | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @see getOutputChannels | |||
| */ | |||
| void setOutputChannels (const BitArray& newEnabledChannels, | |||
| const bool treatAsChosenDevice); | |||
| //============================================================================== | |||
| /** Gives the manager an audio callback to use. | |||
| The manager will redirect callbacks from whatever audio device is currently | |||
| @@ -274,6 +214,7 @@ public: | |||
| */ | |||
| void setAudioCallback (AudioIODeviceCallback* newCallback); | |||
| //============================================================================== | |||
| /** Returns the average proportion of available CPU being spent inside the audio callbacks. | |||
| Returns a value between 0 and 1.0 | |||
| @@ -323,7 +264,8 @@ public: | |||
| /** Removes a listener that was previously registered with addMidiInputCallback(). | |||
| */ | |||
| void removeMidiInputCallback (MidiInputCallback* callback); | |||
| void removeMidiInputCallback (const String& midiInputDeviceName, | |||
| MidiInputCallback* callback); | |||
| //============================================================================== | |||
| /** Sets a midi output device to use as the default. | |||
| @@ -355,6 +297,51 @@ public: | |||
| */ | |||
| MidiOutput* getDefaultMidiOutput() const throw() { return defaultMidiOutput; } | |||
| /** | |||
| */ | |||
| const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes() const throw() { return availableDeviceTypes; } | |||
| //============================================================================== | |||
| /** Creates a list of available types. | |||
| This will add a set of new AudioIODeviceType objects to the specified list, to | |||
| represent each available types of device. | |||
| You can override this if your app needs to do something specific, like avoid | |||
| using DirectSound devices, etc. | |||
| */ | |||
| virtual void createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& types); | |||
| //============================================================================== | |||
| /** Plays a beep through the current audio device. | |||
| This is here to allow the audio setup UI panels to easily include a "test" | |||
| button so that the user can check where the audio is coming from. | |||
| */ | |||
| void playTestSound(); | |||
| /** Turns on level-measuring. | |||
| When enabled, the device manager will measure the peak input level | |||
| across all channels, and you can get this level by calling getCurrentInputLevel(). | |||
| This is mainly intended for audio setup UI panels to use to create a mic | |||
| level display, so that the user can check that they've selected the right | |||
| device. | |||
| A simple filter is used to make the level decay smoothly, but this is | |||
| only intended for giving rough feedback, and not for any kind of accurate | |||
| measurement. | |||
| */ | |||
| void enableInputLevelMeasurement (const bool enableMeasurement); | |||
| /** Returns the current input level. | |||
| To use this, you must first enable it by calling enableInputLevelMeasurement(). | |||
| See enableInputLevelMeasurement() for more info. | |||
| */ | |||
| double getCurrentInputLevel() const; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -362,14 +349,20 @@ public: | |||
| private: | |||
| //============================================================================== | |||
| OwnedArray <AudioIODeviceType> availableDeviceTypes; | |||
| OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs; | |||
| AudioDeviceSetup currentSetup; | |||
| AudioIODevice* currentAudioDevice; | |||
| AudioIODeviceCallback* currentCallback; | |||
| int numInputChansNeeded, numOutputChansNeeded; | |||
| String currentDeviceType; | |||
| BitArray inputChannels, outputChannels; | |||
| XmlElement* lastExplicitSettings; | |||
| mutable bool listNeedsScanning; | |||
| bool useInputNames; | |||
| bool useInputNames, inputLevelMeasurementEnabled; | |||
| double inputLevel; | |||
| AudioSampleBuffer* testSound; | |||
| int testSoundPosition; | |||
| StringArray midiInsFromXml; | |||
| OwnedArray <MidiInput> enabledMidiInputs; | |||
| @@ -402,11 +395,6 @@ private: | |||
| }; | |||
| CallbackHandler callbackHandler; | |||
| String lastRunningDevice; | |||
| int lastRunningBlockSize; | |||
| double lastRunningSampleRate; | |||
| BitArray lastRunningIns, lastRunningOuts; | |||
| friend class CallbackHandler; | |||
| void audioDeviceIOCallbackInt (const float** inputChannelData, | |||
| @@ -425,6 +413,15 @@ private: | |||
| void updateXml(); | |||
| void createDeviceTypesIfNeeded(); | |||
| void scanDevicesIfNeeded(); | |||
| void deleteCurrentDevice(); | |||
| double chooseBestSampleRate (double preferred) const; | |||
| AudioIODeviceType* getCurrentDeviceTypeObject() const; | |||
| void insertDefaultDeviceNames (AudioDeviceSetup& setup) const; | |||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | |||
| AudioDeviceManager (const AudioDeviceManager&); | |||
| const AudioDeviceManager& operator= (const AudioDeviceManager&); | |||
| }; | |||
| @@ -307,6 +307,7 @@ public: | |||
| */ | |||
| virtual int getInputLatencyInSamples() = 0; | |||
| //============================================================================== | |||
| /** True if this device can show a pop-up control panel for editing its settings. | |||
| @@ -46,33 +46,5 @@ AudioIODeviceType::~AudioIODeviceType() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| extern AudioIODeviceType* juce_createDefaultAudioIODeviceType(); | |||
| #if JUCE_WIN32 && JUCE_ASIO | |||
| extern AudioIODeviceType* juce_createASIOAudioIODeviceType(); | |||
| #endif | |||
| #if JUCE_WIN32 && JUCE_WDM_AUDIO | |||
| extern AudioIODeviceType* juce_createWDMAudioIODeviceType(); | |||
| #endif | |||
| //============================================================================== | |||
| void AudioIODeviceType::createDeviceTypes (OwnedArray <AudioIODeviceType>& list) | |||
| { | |||
| AudioIODeviceType* const defaultDeviceType = juce_createDefaultAudioIODeviceType(); | |||
| if (defaultDeviceType != 0) | |||
| list.add (defaultDeviceType); | |||
| #if JUCE_WIN32 && JUCE_ASIO | |||
| list.add (juce_createASIOAudioIODeviceType()); | |||
| #endif | |||
| #if JUCE_WIN32 && JUCE_WDM_AUDIO | |||
| list.add (juce_createWDMAudioIODeviceType()); | |||
| #endif | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -33,7 +33,8 @@ | |||
| #define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ | |||
| #include "juce_AudioIODevice.h" | |||
| class AudioDeviceManager; | |||
| class Component; | |||
| //============================================================================== | |||
| /** | |||
| @@ -71,20 +72,6 @@ | |||
| class JUCE_API AudioIODeviceType | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a list of available types. | |||
| This will add a set of new AudioIODeviceType objects to the specified list, to | |||
| represent each available types of device. | |||
| The objects that are created should be managed by the caller (the OwnedArray | |||
| will delete them when the array is itself deleted). | |||
| When created, the objects are uninitialised, so you should call scanForDevices() | |||
| on each one before getting its list of devices. | |||
| */ | |||
| static void createDeviceTypes (OwnedArray <AudioIODeviceType>& list); | |||
| //============================================================================== | |||
| /** Returns the name of this type of driver that this object manages. | |||
| @@ -104,35 +91,47 @@ public: | |||
| The scanForDevices() method must have been called to create this list. | |||
| @param preferInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| @param wantInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| */ | |||
| virtual const StringArray getDeviceNames (const bool preferInputNames = false) const = 0; | |||
| virtual const StringArray getDeviceNames (const bool wantInputNames = false) const = 0; | |||
| /** Returns the name of the default device. | |||
| This will be one of the names from the getDeviceNames() list. | |||
| @param preferInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| @param numInputChannelsNeeded the number of input channels the user is expecting to need - this | |||
| may be used to help decide which device would be most suitable | |||
| @param numOutputChannelsNeeded the number of output channels the user is expecting to need - this | |||
| may be used to help decide which device would be most suitable | |||
| @param forInput if true, this means that a default input device should be | |||
| returned; if false, it should return the default output | |||
| */ | |||
| virtual const String getDefaultDeviceName (const bool preferInputNames, | |||
| const int numInputChannelsNeeded, | |||
| const int numOutputChannelsNeeded) const = 0; | |||
| virtual int getDefaultDeviceIndex (const bool forInput) const = 0; | |||
| /** Returns the index of a given device in the list of device names. | |||
| If asInput is true, it shows the index in the inputs list, otherwise it | |||
| looks for it in the outputs list. | |||
| */ | |||
| virtual int getIndexOfDevice (AudioIODevice* device, const bool asInput) const = 0; | |||
| /** Returns true if two different devices can be used for the input and output. | |||
| */ | |||
| virtual bool hasSeparateInputsAndOutputs() const = 0; | |||
| /** Creates one of the devices of this type. | |||
| The deviceName must be one of the strings returned by getDeviceNames(), and | |||
| scanForDevices() must have been called before this method is used. | |||
| */ | |||
| virtual AudioIODevice* createDevice (const String& deviceName) = 0; | |||
| virtual AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) = 0; | |||
| //============================================================================== | |||
| struct DeviceSetupDetails | |||
| { | |||
| AudioDeviceManager* manager; | |||
| int minNumInputChannels, maxNumInputChannels; | |||
| int minNumOutputChannels, maxNumOutputChannels; | |||
| bool useStereoPairs; | |||
| }; | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| @@ -35,7 +35,7 @@ | |||
| #include "../controls/juce_ComboBox.h" | |||
| #include "../controls/juce_ListBox.h" | |||
| #include "../../../audio/devices/juce_AudioDeviceManager.h" | |||
| class AudioDeviceSelectorComponentListBox; | |||
| class MidiInputSelectorComponentListBox; | |||
| //============================================================================== | |||
| /** | |||
| @@ -89,24 +89,24 @@ public: | |||
| /** @internal */ | |||
| void changeListenerCallback (void*); | |||
| //============================================================================== | |||
| /** Called by the device-specific displays to create a little level meter that | |||
| just displays the current total input levels from the given device manager. | |||
| */ | |||
| static Component* createSimpleLevelMeterComponent (AudioDeviceManager* managerToDisplay); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| AudioDeviceManager& deviceManager; | |||
| ComboBox* audioDeviceDropDown; | |||
| ComboBox* deviceTypeDropDown; | |||
| Label* deviceTypeDropDownLabel; | |||
| Component* audioDeviceSettingsComp; | |||
| String audioDeviceSettingsCompType; | |||
| const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels; | |||
| ComboBox* sampleRateDropDown; | |||
| AudioDeviceSelectorComponentListBox* inputChansBox; | |||
| Label* inputsLabel; | |||
| AudioDeviceSelectorComponentListBox* outputChansBox; | |||
| Label* outputsLabel; | |||
| Label* sampleRateLabel; | |||
| ComboBox* bufferSizeDropDown; | |||
| Label* bufferSizeLabel; | |||
| Button* launchUIButton; | |||
| AudioDeviceSelectorComponentListBox* midiInputsList; | |||
| MidiInputSelectorComponentListBox* midiInputsList; | |||
| Label* midiInputsLabel; | |||
| ComboBox* midiOutputSelector; | |||
| Label* midiOutputLabel; | |||