| @@ -323,6 +323,7 @@ public: | |||
| minSize (0), maxSize (0), | |||
| preferredSize (0), | |||
| granularity (0), | |||
| numClockSources (0), | |||
| currentBlockSizeSamples (0), | |||
| currentBitDepth (16), | |||
| currentSampleRate (0), | |||
| @@ -376,12 +377,10 @@ public: | |||
| if (newRates.size() == 0) | |||
| { | |||
| double cr = 0; | |||
| const long err = asioObject->getSampleRate (&cr); | |||
| double cr = getSampleRate(); | |||
| JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr)); | |||
| JUCE_ASIO_LOG_ERROR ("getSampleRate", err); | |||
| if (err == 0) | |||
| if (cr > 0) | |||
| newRates.add ((int) cr); | |||
| } | |||
| @@ -431,46 +430,11 @@ public: | |||
| isStarted = false; | |||
| bufferIndex = -1; | |||
| minSize = 0; | |||
| maxSize = 0; | |||
| granularity = 0; | |||
| long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); | |||
| jassert (err == ASE_OK); | |||
| long newPreferredSize = 0; | |||
| if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK) | |||
| { | |||
| if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) | |||
| shouldUsePreferredSize = true; | |||
| if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) | |||
| shouldUsePreferredSize = true; | |||
| preferredSize = newPreferredSize; | |||
| } | |||
| // unfortunate workaround for certain manufacturers whose drivers crash horribly if you make | |||
| // dynamic changes to the buffer size... | |||
| shouldUsePreferredSize = shouldUsePreferredSize | |||
| || getName().containsIgnoreCase ("Digidesign"); | |||
| if (shouldUsePreferredSize) | |||
| { | |||
| JUCE_ASIO_LOG ("Using preferred size for buffer.."); | |||
| if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == ASE_OK) | |||
| { | |||
| bufferSizeSamples = preferredSize; | |||
| } | |||
| else | |||
| { | |||
| bufferSizeSamples = 1024; | |||
| JUCE_ASIO_LOG_ERROR ("getBufferSize1", err); | |||
| } | |||
| shouldUsePreferredSize = false; | |||
| } | |||
| bufferSizeSamples = readBufferSizes (bufferSizeSamples); | |||
| int sampleRate = roundToInt (sr); | |||
| currentSampleRate = sampleRate; | |||
| @@ -489,71 +453,13 @@ public: | |||
| if (sampleRate == 0) | |||
| sampleRate = 44100; | |||
| ASIOClockSource clocks[32] = { 0 }; | |||
| long numSources = numElementsInArray (clocks); | |||
| asioObject->getClockSources (clocks, &numSources); | |||
| bool isSourceSet = false; | |||
| // careful not to remove this loop because it does more than just logging! | |||
| for (int i = 0; i < numSources; ++i) | |||
| { | |||
| String s ("clock: "); | |||
| s += clocks[i].name; | |||
| if (clocks[i].isCurrentSource) | |||
| { | |||
| isSourceSet = true; | |||
| s << " (cur)"; | |||
| } | |||
| JUCE_ASIO_LOG (s); | |||
| } | |||
| if (numSources > 1 && ! isSourceSet) | |||
| { | |||
| JUCE_ASIO_LOG ("setting clock source"); | |||
| err = asioObject->setClockSource (clocks[0].index); | |||
| JUCE_ASIO_LOG_ERROR ("setClockSource1", err); | |||
| Thread::sleep (20); | |||
| } | |||
| else | |||
| { | |||
| if (numSources == 0) | |||
| JUCE_ASIO_LOG ("no clock sources!"); | |||
| } | |||
| { | |||
| double cr = 0; | |||
| err = asioObject->getSampleRate (&cr); | |||
| JUCE_ASIO_LOG_ERROR ("getSampleRate", err); | |||
| currentSampleRate = cr; | |||
| } | |||
| updateClockSources(); | |||
| currentSampleRate = getSampleRate(); | |||
| error = String::empty; | |||
| err = 0; | |||
| buffersCreated = false; | |||
| if (currentSampleRate != sampleRate) | |||
| { | |||
| JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (sampleRate)); | |||
| err = asioObject->setSampleRate (sampleRate); | |||
| if (err == ASE_NoClock && numSources > 0) | |||
| { | |||
| JUCE_ASIO_LOG ("trying to set a clock source.."); | |||
| Thread::sleep (10); | |||
| err = asioObject->setClockSource (clocks[0].index); | |||
| JUCE_ASIO_LOG_ERROR ("setClockSource2", err); | |||
| Thread::sleep (10); | |||
| err = asioObject->setSampleRate (sampleRate); | |||
| } | |||
| if (err == 0) | |||
| currentSampleRate = sampleRate; | |||
| // on fail, ignore the attempt to change rate, and run with the current one.. | |||
| } | |||
| setSampleRate (sampleRate); | |||
| if (needToReset) | |||
| { | |||
| @@ -652,12 +558,7 @@ public: | |||
| outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples); | |||
| } | |||
| inputLatency = outputLatency = 0; | |||
| if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) | |||
| JUCE_ASIO_LOG ("no latencies"); | |||
| else | |||
| JUCE_ASIO_LOG ("latencies: " + String ((int) outputLatency) + ", " + String ((int) inputLatency)); | |||
| readLatencies(); | |||
| asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||
| deviceIsOpen = true; | |||
| @@ -866,6 +767,8 @@ private: | |||
| Array<int> sampleRates, bufferSizes; | |||
| long inputLatency, outputLatency; | |||
| long minSize, maxSize, preferredSize, granularity; | |||
| ASIOClockSource clocks[32]; | |||
| int numClockSources; | |||
| int volatile currentBlockSizeSamples; | |||
| int volatile currentBitDepth; | |||
| @@ -930,6 +833,50 @@ private: | |||
| } | |||
| } | |||
| int readBufferSizes (int bufferSizeSamples) | |||
| { | |||
| minSize = 0; | |||
| maxSize = 0; | |||
| granularity = 0; | |||
| long newPreferredSize = 0; | |||
| if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK) | |||
| { | |||
| if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) | |||
| shouldUsePreferredSize = true; | |||
| if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) | |||
| shouldUsePreferredSize = true; | |||
| preferredSize = newPreferredSize; | |||
| } | |||
| // unfortunate workaround for certain drivers which crash if you make | |||
| // dynamic changes to the buffer size... | |||
| shouldUsePreferredSize = shouldUsePreferredSize || getName().containsIgnoreCase ("Digidesign"); | |||
| if (shouldUsePreferredSize) | |||
| { | |||
| JUCE_ASIO_LOG ("Using preferred size for buffer.."); | |||
| long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | |||
| if (err == ASE_OK) | |||
| { | |||
| bufferSizeSamples = (int) preferredSize; | |||
| } | |||
| else | |||
| { | |||
| bufferSizeSamples = 1024; | |||
| JUCE_ASIO_LOG_ERROR ("getBufferSize1", err); | |||
| } | |||
| shouldUsePreferredSize = false; | |||
| } | |||
| return bufferSizeSamples; | |||
| } | |||
| int resetBuffers (const BigInteger& inputChannels, | |||
| const BigInteger& outputChannels) | |||
| { | |||
| @@ -996,6 +943,157 @@ private: | |||
| bufferSizes.sort (comparator); | |||
| } | |||
| double getSampleRate() const | |||
| { | |||
| double cr = 0; | |||
| long err = asioObject->getSampleRate (&cr); | |||
| JUCE_ASIO_LOG_ERROR ("getSampleRate", err); | |||
| return cr; | |||
| } | |||
| void setSampleRate (int newRate) | |||
| { | |||
| if (currentSampleRate != newRate) | |||
| { | |||
| JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate)); | |||
| long err = asioObject->setSampleRate (newRate); | |||
| if (err == ASE_NoClock && numClockSources > 0) | |||
| { | |||
| JUCE_ASIO_LOG ("trying to set a clock source.."); | |||
| Thread::sleep (10); | |||
| err = asioObject->setClockSource (clocks[0].index); | |||
| JUCE_ASIO_LOG_ERROR ("setClockSource2", err); | |||
| Thread::sleep (10); | |||
| err = asioObject->setSampleRate (newRate); | |||
| } | |||
| if (err == 0) | |||
| currentSampleRate = newRate; | |||
| // on fail, ignore the attempt to change rate, and run with the current one.. | |||
| } | |||
| } | |||
| void updateClockSources() | |||
| { | |||
| zeromem (clocks, sizeof (clocks)); | |||
| long numSources = numElementsInArray (clocks); | |||
| asioObject->getClockSources (clocks, &numSources); | |||
| numClockSources = (int) numSources; | |||
| bool isSourceSet = false; | |||
| // careful not to remove this loop because it does more than just logging! | |||
| for (int i = 0; i < numClockSources; ++i) | |||
| { | |||
| String s ("clock: "); | |||
| s += clocks[i].name; | |||
| if (clocks[i].isCurrentSource) | |||
| { | |||
| isSourceSet = true; | |||
| s << " (cur)"; | |||
| } | |||
| JUCE_ASIO_LOG (s); | |||
| } | |||
| if (numClockSources > 1 && ! isSourceSet) | |||
| { | |||
| JUCE_ASIO_LOG ("setting clock source"); | |||
| long err = asioObject->setClockSource (clocks[0].index); | |||
| JUCE_ASIO_LOG_ERROR ("setClockSource1", err); | |||
| Thread::sleep (20); | |||
| } | |||
| else | |||
| { | |||
| if (numClockSources == 0) | |||
| JUCE_ASIO_LOG ("no clock sources!"); | |||
| } | |||
| } | |||
| void readLatencies() | |||
| { | |||
| inputLatency = outputLatency = 0; | |||
| if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) | |||
| JUCE_ASIO_LOG ("getLatencies() failed"); | |||
| else | |||
| JUCE_ASIO_LOG ("Latencies: in = " + String ((int) inputLatency) + ", out = " + String ((int) outputLatency)); | |||
| } | |||
| void createDummyBuffers (long preferredSize) | |||
| { | |||
| numActiveInputChans = 0; | |||
| numActiveOutputChans = 0; | |||
| ASIOBufferInfo* info = bufferInfos; | |||
| int numChans = 0; | |||
| for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i) | |||
| { | |||
| info->isInput = 1; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = nullptr; | |||
| ++info; | |||
| ++numChans; | |||
| } | |||
| const int outputBufferIndex = numChans; | |||
| for (int i = 0; i < jmin (2, (int) totalNumOutputChans); ++i) | |||
| { | |||
| info->isInput = 0; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = nullptr; | |||
| ++info; | |||
| ++numChans; | |||
| } | |||
| setCallbackFunctions(); | |||
| JUCE_ASIO_LOG ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); | |||
| if (preferredSize > 0) | |||
| { | |||
| long err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); | |||
| JUCE_ASIO_LOG_ERROR ("dummy buffers", err); | |||
| } | |||
| long newInps = 0, newOuts = 0; | |||
| asioObject->getChannels (&newInps, &newOuts); | |||
| if (totalNumInputChans != newInps || totalNumOutputChans != newOuts) | |||
| { | |||
| totalNumInputChans = newInps; | |||
| totalNumOutputChans = newOuts; | |||
| JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in; " + String ((int) totalNumOutputChans) + " out"); | |||
| } | |||
| updateSampleRates(); | |||
| reloadChannelNames(); | |||
| for (int i = 0; i < totalNumOutputChans; ++i) | |||
| { | |||
| ASIOChannelInfo channelInfo = { 0 }; | |||
| channelInfo.channel = i; | |||
| channelInfo.isInput = 0; | |||
| asioObject->getChannelInfo (&channelInfo); | |||
| outputFormat[i] = ASIOSampleFormat (channelInfo.type); | |||
| if (i < 2) | |||
| { | |||
| // clear the channels that are used with the dummy stuff | |||
| outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); | |||
| outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); | |||
| } | |||
| } | |||
| } | |||
| void removeCurrentDriver() | |||
| { | |||
| if (asioObject != nullptr) | |||
| @@ -1115,16 +1213,15 @@ private: | |||
| { | |||
| addBufferSizes (minSize, maxSize, preferredSize, granularity); | |||
| double currentRate = 0; | |||
| asioObject->getSampleRate (¤tRate); | |||
| double currentRate = getSampleRate(); | |||
| if (currentRate <= 0.0 || currentRate > 192001.0) | |||
| if (currentRate < 1.0 || currentRate > 192001.0) | |||
| { | |||
| JUCE_ASIO_LOG ("setting sample rate"); | |||
| JUCE_ASIO_LOG ("setting default sample rate"); | |||
| err = asioObject->setSampleRate (44100.0); | |||
| JUCE_ASIO_LOG_ERROR ("setting sample rate", err); | |||
| asioObject->getSampleRate (¤tRate); | |||
| currentRate = getSampleRate(); | |||
| } | |||
| currentSampleRate = currentRate; | |||
| @@ -1135,85 +1232,11 @@ private: | |||
| updateSampleRates(); | |||
| // ..because cubase does it at this point | |||
| inputLatency = outputLatency = 0; | |||
| if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) | |||
| JUCE_ASIO_LOG ("no latencies"); | |||
| JUCE_ASIO_LOG ("latencies: " + String ((int) inputLatency) + ", " + String ((int) outputLatency)); | |||
| // create some dummy buffers now.. because cubase does.. | |||
| numActiveInputChans = 0; | |||
| numActiveOutputChans = 0; | |||
| ASIOBufferInfo* info = bufferInfos; | |||
| int numChans = 0; | |||
| for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i) | |||
| { | |||
| info->isInput = 1; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = nullptr; | |||
| ++info; | |||
| ++numChans; | |||
| } | |||
| const int outputBufferIndex = numChans; | |||
| for (int i = 0; i < jmin (2, (int) totalNumOutputChans); ++i) | |||
| { | |||
| info->isInput = 0; | |||
| info->channelNum = i; | |||
| info->buffers[0] = info->buffers[1] = nullptr; | |||
| ++info; | |||
| ++numChans; | |||
| } | |||
| setCallbackFunctions(); | |||
| JUCE_ASIO_LOG ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); | |||
| if (preferredSize > 0) | |||
| { | |||
| err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); | |||
| JUCE_ASIO_LOG_ERROR ("dummy buffers", err); | |||
| } | |||
| long newInps = 0, newOuts = 0; | |||
| asioObject->getChannels (&newInps, &newOuts); | |||
| if (totalNumInputChans != newInps || totalNumOutputChans != newOuts) | |||
| { | |||
| totalNumInputChans = newInps; | |||
| totalNumOutputChans = newOuts; | |||
| JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in; " + String ((int) totalNumOutputChans) + " out"); | |||
| } | |||
| updateSampleRates(); | |||
| reloadChannelNames(); | |||
| for (int i = 0; i < totalNumOutputChans; ++i) | |||
| { | |||
| ASIOChannelInfo channelInfo = { 0 }; | |||
| channelInfo.channel = i; | |||
| channelInfo.isInput = 0; | |||
| asioObject->getChannelInfo (&channelInfo); | |||
| outputFormat[i] = ASIOSampleFormat (channelInfo.type); | |||
| if (i < 2) | |||
| { | |||
| // clear the channels that are used with the dummy stuff | |||
| outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); | |||
| outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); | |||
| } | |||
| } | |||
| readLatencies(); // ..doing these steps because cubase does so at this stage | |||
| createDummyBuffers (preferredSize); // in initialisation, and some devices fail if we don't. | |||
| readLatencies(); | |||
| // start and stop because cubase does it.. | |||
| asioObject->getLatencies (&inputLatency, &outputLatency); | |||
| err = asioObject->start(); | |||
| // ignore an error here, as it might start later after setting other stuff up | |||
| JUCE_ASIO_LOG_ERROR ("start", err); | |||