void ofxZenGarden::processNonInterleaved(float *input, float *output, int frameCount) { if(context==NULL || !running) return; setBlockSize(frameCount); memset(output, 0, frameCount*sizeof(float)*numOutputChannels); zg_process(context, input, output); }
int main(int argc, char * const argv[]) { const int blockSize = 64; const int numInputChannels = 2; const int numOutputChannels = 2; const float sampleRate = 22050.0f; // pass directory and filename of the patch to load PdContext *context = zg_new_context(numInputChannels, numOutputChannels, blockSize, sampleRate, callbackFunction, NULL); PdGraph *graph = zg_new_graph(context, "/Users/mhroth/workspace/ZenGarden/test/", "MessageMessageBox.pd"); if (graph == NULL) { zg_delete_context(context); return 1; } zg_attach_graph(context, graph); float *inputBuffers = (float *) calloc(numInputChannels * blockSize, sizeof(float)); float *outputBuffers = (float *) calloc(numOutputChannels * blockSize, sizeof(float)); timeval start, end; gettimeofday(&start, NULL); for (int i = 0; i < NUM_ITERATIONS; i++) { zg_process(context, inputBuffers, outputBuffers); } gettimeofday(&end, NULL); double elapsedTime = (end.tv_sec - start.tv_sec) * 1000.0; // sec to ms elapsedTime += (end.tv_usec - start.tv_usec) / 1000.0; // us to ms printf("Runtime is: %i iterations in %f milliseconds == %f iterations/second.\n", NUM_ITERATIONS, elapsedTime, ((double) NUM_ITERATIONS)*1000.0/elapsedTime); double simulatedTime = ((double) blockSize / (double) sampleRate) * (double) NUM_ITERATIONS * 1000.0; // milliseconds printf("Runs in realtime: %s (x%.3f)\n", (simulatedTime >= elapsedTime) ? "YES" : "NO", simulatedTime/elapsedTime); zg_delete_context(context); free(inputBuffers); free(outputBuffers); return 0; }
void ofxZenGarden::process(float *input, float *output, int frameCount) { if(context==NULL || !running) return; setBlockSize(frameCount); memset(output, 0, frameCount*sizeof(float)*numOutputChannels); zg_process(context, input, outputBuffer); if(numOutputChannels==1) { memcpy(output, outputBuffer, sizeof(float)*frameCount); } else if(numOutputChannels==2) { for(int i = 0; i < frameCount; i++) { output[i*2] = outputBuffer[i]; output[i*2 + 1] = outputBuffer[i + blockSize]; } } else { for(int i = 0; i < blockSize; i++) { for(int channel = 0; channel < numOutputChannels; channel++) { output[i*numOutputChannels + channel] = outputBuffer[i + channel*blockSize]; } } } }
// this function uses bit-wise manupulation in order to more quickly multiply floats by // -1 or 32768. See http://en.wikipedia.org/wiki/IEEE_754-1985 JNIEXPORT void JNICALL Java_me_rjdj_zengarden_ZenGarden_process( JNIEnv *env, jobject jobj, jshortArray jinputBuffer, jshortArray joutputBuffer, jlong nativePtr) { PureDataMobileNativeVars *pdmnv = (PureDataMobileNativeVars *) nativePtr; // set up the floating point input buffers short *cinputBuffer = (short *) env->GetPrimitiveArrayCritical(jinputBuffer, NULL); switch (pdmnv->numInputChannels) { case 1: { // if there is only one input channel (mono) // convert all shorts to floats for (int i = 0; i < pdmnv->blockSize; i++) { pdmnv->finputBuffer[i] = shortSampleToFloat(cinputBuffer[i], pdmnv); } // copy the float buffer from the left to right channel memcpy(pdmnv->finputBuffer + pdmnv->blockSize, pdmnv->finputBuffer, pdmnv->numBytesInBlock); break; } case 2: { // if there is a stereo input // uninterleave the short buffer for (int i = 0, j = 0; i < pdmnv->blockSize; i++) { for (int k = 0, z = i; k < pdmnv->numInputChannels; k++, j++, z+=pdmnv->blockSize) { pdmnv->finputBuffer[z] = shortSampleToFloat(cinputBuffer[j], pdmnv); } } break; } default: { break; } } env->ReleasePrimitiveArrayCritical(jinputBuffer, cinputBuffer, JNI_ABORT); // no need to copy back changes. release native buffer. zg_process(pdmnv->pdGraph, pdmnv->finputBuffer, pdmnv->foutputBuffer); // read back from the floating point output buffers // regarding use of lrintf, see http://www.mega-nerd.com/FPcast/ short *coutputBuffer = (short *) env->GetPrimitiveArrayCritical(joutputBuffer, NULL); for (int i = 0, j = 0; i < pdmnv->blockSize; i++) { for (int k = 0, z = i; k < pdmnv->numOutputChannels; k++, j++, z+=pdmnv->blockSize) { // clip the output (like Pd does) float f = pdmnv->foutputBuffer[z]; if (f > 1.0f) { coutputBuffer[j] = 32767; } else if (f < -1.0f) { coutputBuffer[j] = -32768; } else { coutputBuffer[j] = (short) lrintf(f * 32767.0f); } /* * WARNING: if pdmnv->foutputBuffer[z] == 1.0f, this method will result in -32768, not 32767 * and cause a click in the output (i.e., there is overflow when converting to a short) int floatAsInt = *(int *)(pdmnv->foutputBuffer+z); int exponent = floatAsInt & 0x7F800000; exponent += 0x07800000; // multiply by 32768.0f == 2^15 (add 15 to the exponent) floatAsInt = (floatAsInt & 0x807FFFFF) | exponent; coutputBuffer[j] = (short) lrintf(*(float *)&floatAsInt); */ } } env->ReleasePrimitiveArrayCritical(joutputBuffer, coutputBuffer, 0); // copy back the changes and release native buffer }
void PdSynth :: audioCB() { if (mPd == NULL) return; int from = synth.mStart; int to = synth.mEnd; PortReader input_p1(synth, 0); PortReader input_p2(synth, 1); PortReader out_p1(synth, 2); PortReader out_p2(synth, 3); PortReader amp_p1(synth, 4); luaav_audio_config cfg = luaav_audio_config_current(); //int blocksize = cfg.blocksize; // int nchnls = csound->GetNchnls(); // int ksmps = csound->GetKsmps(); // double scale = csound->Get0dBFS(); // double inv_scale = 1.0 / scale; //long inbufsize = csound->GetInputBufferSize(); //long outbufsize = csound->GetOutputBufferSize(); // sample * outbuffers[2]; // outbuffers[0] = luaav3_OutPort_data(&self->synth.output, 0); // outbuffers[1] = luaav3_OutPort_data(&self->synth.output, 1); // // sample const * inbuffers[2]; // inbuffers[0] = luaav3_InPort_data(&self->input, 0); // inbuffers[1] = luaav3_InPort_data(&self->input, 1); // if ksmps is less than blocksize, need to run multiple passes // copy buffers in: // TODO: use a Control array instead of globals float * dst1 = mInputBuffers; float * dst2 = mInputBuffers + mBlockSize; for (int i=0; i<mBlockSize; i++) { dst1[i] = (float)input_p1[i]; dst2[i] = (float)input_p2[i]; } // let PD do the work: zg_process(mPd, mInputBuffers, mOutputBuffers); // copy buffers back out: // TODO: use a Control array instead of globals float * src1 = mOutputBuffers; float * src2 = mOutputBuffers + mBlockSize; for (int i=0; i<mBlockSize; i++) { sample amp = amp_p1[i]; out_p1[i] += amp * (sample)src1[i]; out_p2[i] += amp * (sample)src2[i]; } // // // because csound is interleaved, and LuaAV isn't. // MYFLT * csin = csound->GetSpin(); // for (int i=0; i<ksmps; i++) { // const sample input1 = input_p1[i+offset]; // const sample input2 = input_p1[i+offset]; // // csin[ i*nchnls] = input1 * scale; // csin[1 + i*nchnls] = input2 * scale; // } // // /* // luaav_sample v1 = inbuffers[0][0]; // luaav_sample v2 = csin[0]; // printf("%f %f\n", v1, v2); // ok, got data! // */ // // // run one ksmps of audio processing in csound // csound->PerformKsmps(); // // //convert csound interleaved audio back into LuaAV non-interleaved audio // MYFLT * csout = csound->GetSpout(); // for (int i=0; i<ksmps; i++) { // sample& out1 = out_p1[i+offset]; // sample& out2 = out_p2[i+offset]; // const sample amp1 = amp_p1[i+offset]; // // out1 += csout[ i*nchnls] * inv_scale * amp1; // out2 += csout[1 + i*nchnls] * inv_scale * amp1; // } }