// Main audio processing callback. // NOTE: Called on a separate thread from main() thread. ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) { // Buffer size (in samples): long buffSize = drv.preferredSize; // Assume the buffer size is an even multiple of 32-bytes: assert((buffSize % sizeof(vec4_d64)) == 0); for (long i = 0; i < buffSize; ++i) { assert(index == 0 || index == 1); // Process 8 channels of 32-bit samples per iteration: for (long n = 0; n < inputChannels / 8; ++n) { const long ci = n * 8; const long co = drv.inputBuffers + ci; // Stripe input samples into a vector: const vec8_i32 inpSamples = _mm256_setr_epi32( ((long *)drv.bufferInfos[ci + 0].buffers[index])[i], ((long *)drv.bufferInfos[ci + 1].buffers[index])[i], ((long *)drv.bufferInfos[ci + 2].buffers[index])[i], ((long *)drv.bufferInfos[ci + 3].buffers[index])[i], ((long *)drv.bufferInfos[ci + 4].buffers[index])[i], ((long *)drv.bufferInfos[ci + 5].buffers[index])[i], ((long *)drv.bufferInfos[ci + 6].buffers[index])[i], ((long *)drv.bufferInfos[ci + 7].buffers[index])[i] ); // Process audio effects: vec8_i32 outSamples; processEffects(inpSamples, outSamples, n * 2); // Copy outputs to output channel buffers: const long *outputs32 = (const long *)&outSamples; ((long *)drv.bufferInfos[co + 0].buffers[index])[i] = outputs32[0]; ((long *)drv.bufferInfos[co + 1].buffers[index])[i] = outputs32[1]; ((long *)drv.bufferInfos[co + 2].buffers[index])[i] = outputs32[2]; ((long *)drv.bufferInfos[co + 3].buffers[index])[i] = outputs32[3]; ((long *)drv.bufferInfos[co + 4].buffers[index])[i] = outputs32[4]; ((long *)drv.bufferInfos[co + 5].buffers[index])[i] = outputs32[5]; ((long *)drv.bufferInfos[co + 6].buffers[index])[i] = outputs32[6]; ((long *)drv.bufferInfos[co + 7].buffers[index])[i] = outputs32[7]; } } if (drv.postOutput) ASIOOutputReady(); return 0L; }
//---------------------------------------------------------------------------------- long init_asio_static_data (DriverInfo *asioDriverInfo) { // collect the informational data of the driver // get the number of available channels if(ASIOGetChannels(&asioDriverInfo->inputChannels, &asioDriverInfo->outputChannels) == ASE_OK) { printf ("ASIOGetChannels (inputs: %d, outputs: %d);\n", asioDriverInfo->inputChannels, asioDriverInfo->outputChannels); // get the usable buffer sizes if(ASIOGetBufferSize(&asioDriverInfo->minSize, &asioDriverInfo->maxSize, &asioDriverInfo->preferredSize, &asioDriverInfo->granularity) == ASE_OK) { printf ("ASIOGetBufferSize (min: %d, max: %d, preferred: %d, granularity: %d);\n", asioDriverInfo->minSize, asioDriverInfo->maxSize, asioDriverInfo->preferredSize, asioDriverInfo->granularity); // get the currently selected sample rate if(ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK) { printf ("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate); if (asioDriverInfo->sampleRate <= 0.0 || asioDriverInfo->sampleRate > 96000.0) { // Driver does not store it's internal sample rate, so set it to a know one. // Usually you should check beforehand, that the selected sample rate is valid // with ASIOCanSampleRate(). if(ASIOSetSampleRate(44100.0) == ASE_OK) { if(ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK) printf ("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate); else return -6; } else return -5; } // check wether the driver requires the ASIOOutputReady() optimization // (can be used by the driver to reduce output latency by one block) if(ASIOOutputReady() == ASE_OK) asioDriverInfo->postOutput = true; else asioDriverInfo->postOutput = false; printf ("ASIOOutputReady(); - %s\n", asioDriverInfo->postOutput ? "Supported" : "Not supported"); return 0; } return -3; } return -2; } return -1; }
// from ASIOv2 ASIOTime* bufferSwitchTimeInfo(ASIOTime* asioTime, long bufferIndex, ASIOBool directProcess) { JNIEnv *env = NULL; jint res = jvm->AttachCurrentThreadAsDaemon((void **) &env, NULL); if (res == JNI_OK && env != NULL) { env->CallVoidMethod( jAsioDriver, fireBufferSwitchMid, (jlong) ASIO64toLong(asioTime->timeInfo.systemTime), (jlong) ASIO64toLong(asioTime->timeInfo.samplePosition), (jint) bufferIndex); } ASIOOutputReady(); return asioTime; }
SndASIO::SndASIO(int channels, int mode, char* driver, int numbuffs, SndObj** inputs, int vecsize, float sr) : SndIO(channels,16,inputs,vecsize, sr){ int i; m_numbuffs = numbuffs; m_mode = mode; m_running = false; m_driver = driver; m_ocurrentbuffer = m_icurrentbuffer = 1; m_icount = m_ocount = 0; memset(&m_driverinfo, 0, sizeof(ASIODriverInfo)); m_asiocallbacks.bufferSwitch = &bufferSwitch; m_asiocallbacks.sampleRateDidChange = &sampleRateChanged; m_asiocallbacks.asioMessage = &asioMessages; m_asiocallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; // Allocate the memory for BufferInfos if(!(bufferinfos = new ASIOBufferInfo[(m_channels+2)*2])){ m_error = 21; return; } if(!asioDrivers) asioDrivers = new AsioDrivers; if(asioDrivers->loadDriver(m_driver)){ if(ASIOInit(&m_driverinfo) == ASE_OK){ if(ASIOCanSampleRate(m_sr) == ASE_OK) ASIOSetSampleRate(m_sr); else ASIOGetSampleRate((double *)&m_sr); // set buffer size long dump1, dump2, dump3; ASIOGetBufferSize(&dump1, &dump2, &buffsize, &dump3); // get number of channels ASIOGetChannels(&ichannels, &ochannels); if(ichannels < m_channels){ m_channels = (short) ichannels; m_samples = m_vecsize*m_channels; } else ichannels = m_channels; if(ochannels < m_channels){ m_channels = (short) ochannels; m_samples = m_vecsize*m_channels; } else ochannels = m_channels; if(m_mode == SND_OUTPUT) ichannels = 0; if(m_mode == SND_INPUT) ochannels = 0; // Set the channel infos if(!(m_channelinfos = new ASIOChannelInfo[m_channels*2])){ m_error = 22; return; } if((m_mode == SND_IO) || (m_mode == SND_OUTPUT)){ outsndbuff = new float*[m_numbuffs]; for(i = 0; i< m_numbuffs; i++){ if(!(outsndbuff[i] = new float[buffsize*m_channels])){ m_error =14; return; } } for(i = 0; i < m_channels; i++){ bufferinfos[i].isInput = ASIOFalse; bufferinfos[i].channelNum = i; bufferinfos[i].buffers[0] = bufferinfos[i].buffers[1] = 0; m_channelinfos[i].channel = bufferinfos[i].channelNum; m_channelinfos[i].isInput = bufferinfos[i].isInput; ASIOGetChannelInfo(&m_channelinfos[i]); switch(m_channelinfos[i].type){ case ASIOSTInt16LSB: encoding = SHORTSAM; m_bits = 16; break; case ASIOSTInt24LSB: encoding = S24LE; m_bits = 24; break; case ASIOSTInt32LSB: encoding = LONGSAM; m_bits = 32; break; default: encoding = SHORTSAM; break; } } } if((m_mode == SND_IO) || (m_mode == SND_INPUT)){ insndbuff = new float*[m_numbuffs]; for(i = 0; i< m_numbuffs; i++){ if(!(insndbuff[i] = new float[buffsize*m_channels])){ m_error =14; return; } } for(i = 0; i < m_channels; i++){ bufferinfos[i+ochannels].isInput = ASIOTrue; bufferinfos[i+ochannels].channelNum = i; bufferinfos[i+ochannels].buffers[0] = bufferinfos[i+ochannels].buffers[1] = 0; m_channelinfos[i+ochannels].channel = bufferinfos[i+ochannels].channelNum; m_channelinfos[i+ochannels].isInput = bufferinfos[i+ochannels].isInput; ASIOGetChannelInfo(&m_channelinfos[i+ochannels]); switch(m_channelinfos[i+ochannels].type){ case ASIOSTInt16LSB: encoding = SHORTSAM; m_bits = 16; break; case ASIOSTInt24LSB: encoding = S24LE; m_bits = 24; break; case ASIOSTInt32LSB: encoding = LONGSAM; m_bits = 32; break; default: encoding = SHORTSAM; break; } } } if(!(ASIOCreateBuffers(bufferinfos, ichannels+ochannels, buffsize, &m_asiocallbacks)== ASE_OK)){ m_error = 25; return; } if(ASIOOutputReady() == ASE_OK) optimise = true; else optimise = false; // printf("channels: %d\n", m_channels); m_outsndbuff = outsndbuff; m_insndbuff = insndbuff; m_encoding = encoding; m_bufferinfos = bufferinfos; m_ichannels = ichannels; m_ochannels = ochannels; m_buffsize = buffsize; currentbuffer = 0; m_called_read = false; buffs = m_numbuffs; } else { // could not initialise m_error = 24; return; } } else { // if driver could not be loaded m_error = 23; return; } #ifdef DEBUG cout << m_bits; #endif }
ASIOTime* bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow){ short* sigshort; long* siglong; _24Bit* sig24; int n,j; // we do the channel interleaving here for(int i = 0; i < ichannels+ochannels; i++){ if(bufferinfos[i].isInput == ASIOFalse){ // while(ocurrentbuffer == currentbuffer) { // Sleep(1); // } switch(encoding){ case SHORTSAM: sigshort = (short *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ochannels; n+=ochannels, j++){ sigshort[j] = (short) outsndbuff[currentbuffer][n]; } break; case S24LE: union { char c[4]; long l;} tmp; sig24 = (_24Bit *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ochannels; n+=ochannels, j++){ tmp.l = (long) outsndbuff[currentbuffer][n]; sig24[j].s[0] = tmp.c[1]; sig24[j].s[1] = tmp.c[2]; sig24[j].s[2] = tmp.c[3]; } break; case LONGSAM: siglong = (long *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ochannels; n+=ochannels, j++) siglong[j] = (long) outsndbuff[currentbuffer][n]; break; } } else{ switch(encoding){ case SHORTSAM: sigshort = (short *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ichannels; n+=ichannels, j++) insndbuff[currentbuffer][n] = (float) sigshort[j]; break; case S24LE: char tmp[4]; sig24 = (_24Bit *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ichannels; n+=ichannels, j++) { tmp[1] = sig24[j].s[0]; tmp[2] = sig24[j].s[1]; tmp[3] = sig24[j].s[2]; tmp[0] = 0; insndbuff[currentbuffer][n] = (float)(*(long *)tmp); } break; case LONGSAM: siglong = (long *)bufferinfos[i].buffers[index]; for(n = bufferinfos[i].channelNum, j = 0; n < buffsize*ichannels; n+=ichannels, j++) insndbuff[currentbuffer][n] = (float) siglong[j]; break; } } } if(optimise)ASIOOutputReady(); memset(outsndbuff[currentbuffer], 0, sizeof(float)*buffsize*ochannels); currentbuffer= (currentbuffer+1)%buffs; return timeInfo; }
// Main: int main() { int retval = 0; bool inited = false, buffersCreated = false, started = false; char *error = NULL; drv.sampleRate = 44100.0; // Initialize FX parameters: fx.f0_gain.init(); fx.f1_compressor.init(); // Set our own inputs: for (int i = 0; i < icr; ++i) { fx.f0_gain.input.gain[i] = _mm256_set1_pd(0); // dB fx.f1_compressor.input.threshold[i] = _mm256_set1_pd(-30); // dBFS fx.f1_compressor.input.attack[i] = _mm256_set1_pd(1.0); // msec fx.f1_compressor.input.release[i] = _mm256_set1_pd(80); // msec fx.f1_compressor.input.ratio[i] = _mm256_set1_pd(0.25); // N:1 fx.f1_compressor.input.gain[i] = _mm256_set1_pd(6); // dB } // Calculate input-dependent values: fx.f0_gain.recalc(); fx.f1_compressor.recalc(); // FX parameters are all set. #ifdef NOT_LIVE // Test mode: #if 0 const auto t0 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(-1.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(-1.0)); printvec_dB(t0); printf("\n\n"); const auto p0 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(-1.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(1.0)); printvec_dB(t0); printf("\n\n"); const auto t1 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(0.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(-1.0)); printvec_dB(t1); printf("\n\n"); const auto p1 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(0.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(1.0)); printvec_dB(t1); printf("\n\n"); goto done; #endif vec8_i32 in, out; long long c = 0LL; for (int i = 0; i < 20; ++i) { for (int n = 0; n < 48; ++n, ++c) { double s = sin(2.0 * 3.14159265358979323846 * (double)c / drv.sampleRate); int si = (int)(s * INT_MAX / 2); in = _mm256_set1_epi32(si); processEffects(in, out, 0); } #if 1 printf("samp: "); printvec_samp(in); printf("\n"); printf("input: "); for (int n = 0; n < icr; ++n) { printvec_dB(fx.fi_monitor.levels[n]); if (n < icr - 1) printf(" "); } printf("\n"); printf("gain: "); for (int n = 0; n < icr; ++n) { printvec_dB(fx.f0_output.levels[n]); if (n < icr - 1) printf(" "); } printf("\n"); printf("comp: "); for (int n = 0; n < icr; ++n) { printvec_dB(fx.fo_monitor.levels[n]); if (n < icr - 1) printf(" "); } printf("\n"); printf("samp: "); printvec_samp(out); printf("\n\n"); #endif } #else // ASIO live engine mode: if (!loadAsioDriver("UA-1000")) { error = "load failed."; goto err; } if (ASIOInit(&drv.driver) != ASE_OK) goto err; inited = true; if (ASIOGetChannels(&drv.inputChannels, &drv.outputChannels) != ASE_OK) goto err; printf("in: %d, out %d\n", drv.inputChannels, drv.outputChannels); if (ASIOGetBufferSize(&drv.minSize, &drv.maxSize, &drv.preferredSize, &drv.granularity) != ASE_OK) goto err; printf("min buf size: %d, preferred: %d, max buf size: %d\n", drv.minSize, drv.preferredSize, drv.maxSize); if (ASIOGetSampleRate(&drv.sampleRate) != ASE_OK) goto err; printf("rate: %f\n\n", drv.sampleRate); if (ASIOOutputReady() == ASE_OK) drv.postOutput = true; else drv.postOutput = false; // fill the bufferInfos from the start without a gap ASIOBufferInfo *info = drv.bufferInfos; // prepare inputs (Though this is not necessarily required, no opened inputs will work, too if (drv.inputChannels > kMaxInputChannels) drv.inputBuffers = kMaxInputChannels; else drv.inputBuffers = drv.inputChannels; for (int i = 0; i < drv.inputBuffers; i++, info++) { info->isInput = ASIOTrue; info->channelNum = i; info->buffers[0] = info->buffers[1] = 0; } // prepare outputs if (drv.outputChannels > kMaxOutputChannels) drv.outputBuffers = kMaxOutputChannels; else drv.outputBuffers = drv.outputChannels; for (int i = 0; i < drv.outputBuffers; i++, info++) { info->isInput = ASIOFalse; info->channelNum = i; info->buffers[0] = info->buffers[1] = 0; } asioCallbacks.asioMessage = asioMessage; asioCallbacks.bufferSwitch = bufferSwitch; asioCallbacks.bufferSwitchTimeInfo = bufferSwitchTimeInfo; // Create the buffers: if (ASIOCreateBuffers(drv.bufferInfos, drv.inputBuffers + drv.outputBuffers, drv.preferredSize, &asioCallbacks) != ASE_OK) goto err; else buffersCreated = true; // now get all the buffer details, sample word length, name, word clock group and activation for (int i = 0; i < drv.inputBuffers + drv.outputBuffers; i++) { drv.channelInfos[i].channel = drv.bufferInfos[i].channelNum; drv.channelInfos[i].isInput = drv.bufferInfos[i].isInput; if (ASIOGetChannelInfo(&drv.channelInfos[i]) != ASE_OK) goto err; //printf("%s[%2d].type = %d\n", drv.channelInfos[i].isInput ? "in " : "out", drv.channelInfos[i].channel, drv.channelInfos[i].type); if (drv.channelInfos[i].type != ASIOSTInt32LSB) { error = "Application assumes sample types of ASIOSTInt32LSB!"; goto err; } } // get the input and output latencies // Latencies often are only valid after ASIOCreateBuffers() // (input latency is the age of the first sample in the currently returned audio block) // (output latency is the time the first sample in the currently returned audio block requires to get to the output) if (ASIOGetLatencies(&drv.inputLatency, &drv.outputLatency) != ASE_OK) goto err; printf ("latencies: input: %d, output: %d\n", drv.inputLatency, drv.outputLatency); // Start the engine: if (ASIOStart() != ASE_OK) goto err; else started = true; printf("Engine started.\n\n"); const int total_time = 30; for (int i = 0; i < total_time; ++i) { printf("Engine running %2d. \r", total_time - i); Sleep(1000); } #endif goto done; err: if (error == NULL) error = drv.driver.errorMessage; if (error != NULL) fprintf(stderr, "%s\r\n", error); retval = -1; done: if (started) ASIOStop(); if (buffersCreated) ASIODisposeBuffers(); if (inited) ASIOExit(); return retval; }
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) { // the actual processing callback. // Beware that this is normally in a seperate thread, hence be sure that you take care // about thread synchronization. This is omitted here for simplicity. static long processedSamples = 0; // store the timeInfo for later use asioDriverInfo.tInfo = *timeInfo; // get the time stamp of the buffer, not necessary if no // synchronization to other media is required if (timeInfo->timeInfo.flags & kSystemTimeValid) asioDriverInfo.nanoSeconds = ASIO64toNanoSeconds(timeInfo->timeInfo.systemTime); else asioDriverInfo.nanoSeconds = 0; if (timeInfo->timeInfo.flags & kSamplePositionValid) asioDriverInfo.samples = ASIO64toNanoSeconds(timeInfo->timeInfo.samplePosition); else asioDriverInfo.samples = 0; if (timeInfo->timeCode.flags & kTcValid) asioDriverInfo.tcSamples = ASIO64toNanoSeconds(timeInfo->timeCode.timeCodeSamples); else asioDriverInfo.tcSamples = 0; // get the system reference time asioDriverInfo.sysRefTime = get_sys_reference_time(); #if WINDOWS && _DEBUG // a few debug messages for the Windows device driver developer // tells you the time when driver got its interrupt and the delay until the app receives // the event notification. static double last_samples = 0; char tmp[128]; sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples)); OutputDebugString (tmp); last_samples = asioDriverInfo.samples; #endif // buffer size in samples long buffSize = asioDriverInfo.preferredSize; // perform the processing for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++) { if (asioDriverInfo.bufferInfos[i].isInput == true) { in_counter++; std::unique_ptr<ah::GenericBuffer> gbuff = ah::NewGenericBufferFromASIO_Sample_T( asioDriverInfo.bufferInfos[i].buffers[index],asioDriverInfo.channelInfos[i].type); int inbuf_ind; int n = buffSize/gbuff->SampleSize(); sum=0; for (inbuf_ind = 0 ; inbuf_ind < n ; inbuf_ind++ ) { int this_elem = *gbuff->GetElement(inbuf_ind); sum += this_elem*this_elem; } //fprintf(stdout,"%d",sum); } else { out_counter++; // OK do processing for the outputs only switch (asioDriverInfo.channelInfos[i].type) { case ASIOSTInt16LSB: memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2); break; case ASIOSTInt24LSB: // used for 20 bits as well memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3); break; case ASIOSTInt32LSB: memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8); break; // these are used for 32 bit data buffer, with different alignment of the data inside // 32 bit PCI bus systems can more easily used with these case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; case ASIOSTInt16MSB: memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2); break; case ASIOSTInt24MSB: // used for 20 bits as well memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3); break; case ASIOSTInt32MSB: memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8); break; // these are used for 32 bit data buffer, with different alignment of the data inside // 32 bit PCI bus systems can more easily used with these case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4); break; } } } // finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place if (asioDriverInfo.postOutput) ASIOOutputReady(); if (processedSamples >= asioDriverInfo.sampleRate * TEST_RUN_TIME) // roughly measured asioDriverInfo.stopped = true; else processedSamples += buffSize; return 0L; }