| @@ -323,6 +323,7 @@ public: | |||||
| minSize (0), maxSize (0), | minSize (0), maxSize (0), | ||||
| preferredSize (0), | preferredSize (0), | ||||
| granularity (0), | granularity (0), | ||||
| numClockSources (0), | |||||
| currentBlockSizeSamples (0), | currentBlockSizeSamples (0), | ||||
| currentBitDepth (16), | currentBitDepth (16), | ||||
| currentSampleRate (0), | currentSampleRate (0), | ||||
| @@ -376,12 +377,10 @@ public: | |||||
| if (newRates.size() == 0) | 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 ("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); | newRates.add ((int) cr); | ||||
| } | } | ||||
| @@ -431,46 +430,11 @@ public: | |||||
| isStarted = false; | isStarted = false; | ||||
| bufferIndex = -1; | bufferIndex = -1; | ||||
| minSize = 0; | |||||
| maxSize = 0; | |||||
| granularity = 0; | |||||
| long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); | long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); | ||||
| jassert (err == ASE_OK); | 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); | int sampleRate = roundToInt (sr); | ||||
| currentSampleRate = sampleRate; | currentSampleRate = sampleRate; | ||||
| @@ -489,71 +453,13 @@ public: | |||||
| if (sampleRate == 0) | if (sampleRate == 0) | ||||
| sampleRate = 44100; | 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; | error = String::empty; | ||||
| err = 0; | |||||
| buffersCreated = false; | 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) | if (needToReset) | ||||
| { | { | ||||
| @@ -652,12 +558,7 @@ public: | |||||
| outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples); | 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); | asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); | ||||
| deviceIsOpen = true; | deviceIsOpen = true; | ||||
| @@ -866,6 +767,8 @@ private: | |||||
| Array<int> sampleRates, bufferSizes; | Array<int> sampleRates, bufferSizes; | ||||
| long inputLatency, outputLatency; | long inputLatency, outputLatency; | ||||
| long minSize, maxSize, preferredSize, granularity; | long minSize, maxSize, preferredSize, granularity; | ||||
| ASIOClockSource clocks[32]; | |||||
| int numClockSources; | |||||
| int volatile currentBlockSizeSamples; | int volatile currentBlockSizeSamples; | ||||
| int volatile currentBitDepth; | 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, | int resetBuffers (const BigInteger& inputChannels, | ||||
| const BigInteger& outputChannels) | const BigInteger& outputChannels) | ||||
| { | { | ||||
| @@ -996,6 +943,157 @@ private: | |||||
| bufferSizes.sort (comparator); | 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() | void removeCurrentDriver() | ||||
| { | { | ||||
| if (asioObject != nullptr) | if (asioObject != nullptr) | ||||
| @@ -1115,16 +1213,15 @@ private: | |||||
| { | { | ||||
| addBufferSizes (minSize, maxSize, preferredSize, granularity); | 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); | err = asioObject->setSampleRate (44100.0); | ||||
| JUCE_ASIO_LOG_ERROR ("setting sample rate", err); | JUCE_ASIO_LOG_ERROR ("setting sample rate", err); | ||||
| asioObject->getSampleRate (¤tRate); | |||||
| currentRate = getSampleRate(); | |||||
| } | } | ||||
| currentSampleRate = currentRate; | currentSampleRate = currentRate; | ||||
| @@ -1135,85 +1232,11 @@ private: | |||||
| updateSampleRates(); | 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.. | // start and stop because cubase does it.. | ||||
| asioObject->getLatencies (&inputLatency, &outputLatency); | |||||
| err = asioObject->start(); | err = asioObject->start(); | ||||
| // ignore an error here, as it might start later after setting other stuff up | // ignore an error here, as it might start later after setting other stuff up | ||||
| JUCE_ASIO_LOG_ERROR ("start", err); | JUCE_ASIO_LOG_ERROR ("start", err); | ||||