/* Portaudio Karplus-Strong demo. Version 0.01, may 19, 2005. Latest version available at http://kmt.hku.nl/~pieter/EDU/c/pa_karplus-strong.c. Copyright (c) 2005 - Pieter Suurmond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is requested to send the modifications to the original developer so that they can be incorporated into the canonical version. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include "portaudio.h" /* To compile this example, link against the portaudio library and make sure the compiler can find this headerfile. */ typedef struct { /*-------------------- EXCITATION TIMER: -----------------------*/ short pluck; /* Set by main, decremented by callback. */ /*-------------------- LINEAR KARPLUS STRONG SYSTEM: --------------------*/ short* delayline; /* Memory must be allocated prior to use, its' size */ short mask; /* must be power of 2 and mask must be the size - 1. */ short L; /* Actual delay line length (0 < L <= mask + 1). */ short n; /* Write-index (discrete time) in the large buffer. */ short lp_fixed; /* 16 bits */ long lp_tuned; /* 17 bits */ short c0; /* Combined tuning and feedback coefficients: (C*G), */ short c1; /* (1-C)*G. */ /*-------------------- OUTPUT PANNING TO STEREO-BUS: --------------------*/ short amp_left; short amp_right; } KSTRONG; typedef struct { short num_kstrongs; long x; /* Common input for a little feedback. */ long dc; /* Measured DC-offset in the output channels. */ KSTRONG* kstrongs; } ks_data; static int cleanup_paks(ks_data* ptr) /* 'Destructor'. */ { int e = 0; if (ptr->kstrongs) { short i; for (i = 0; (i < ptr->num_kstrongs) && ptr->kstrongs[i].delayline; i++) free(ptr->kstrongs[i].delayline); /* At first NULL we MUST stop. */ free(ptr->kstrongs); /* Release the large array of structs. */ ptr->kstrongs = NULL; /* Notify caller about cleanup. */ } else e = 1; return e; } static int setup_paks(ks_data* ptr, short num, double sr) /* 'Constructor'. */ { int i; if (num < 1) return 1; ptr->num_kstrongs = num; ptr->dc = 0L; ptr->x = 0L; ptr->kstrongs = malloc(num * sizeof(KSTRONG)); if (!ptr->kstrongs) return 2; for (i = 0; i < num; i++) { double Hz, len, C, G, P; short maxlen, k; ptr->kstrongs[i].pluck = 0; /* Clear excitation-timers. */ /* First we determine the desired FUNDAMENTAL FREQUENCY (in Hertz) of the kstrong. */ Hz = 55.0 * pow(2.0, (double)i / 12.0); /* Equally tempered, concert pitch=440 Hz. */ /* Since we know the samplerate, we can calculate the desired closed-loop-length. */ len = sr / Hz; /* But, since we've already got half a sample delay from the fixed lowpass filter, */ len -= 0.5; /* we can already subtract a half a sample. */ ptr->kstrongs[i].L = (short)len; /* Determine coarse delay-line length L (int).*/ printf("%d: L=%d\n", i, ptr->kstrongs[i].L); C = len - (double)ptr->kstrongs[i].L;/* Leaves us C samples to add (i.e. correct). */ /* 0 <= C < 1. 0 < G < 1. */ /* Lower feedback levels for longer delay lines. Shortest delay about 0.4999999... */ G = 0.5 - (0.01 * (double)(num-i) / (double)num); /* Determine fine-tuning/feedback parameters. */ ptr->kstrongs[i].c1 = (short)(0.5 + (32768.0 * C * G)); ptr->kstrongs[i].c0 = (short)(0.5 + (32768.0 * (1.0-C) * G)); if (ptr->kstrongs[i].c0 + ptr->kstrongs[i].c1 >= 16384) printf("FEEDBACK TOO HIGH!!!\n"); /* printf(" G=%.6f\n", G); printf(" c1=%d\n", ptr->kstrongs[i].c1); printf(" c0=%d\n", ptr->kstrongs[i].c0); printf(" sum=%d\n", ptr->kstrongs[i].c0 + ptr->kstrongs[i].c1); */ maxlen = 1; /* Find smallest power of 2 greater than needed */ while (maxlen <= ptr->kstrongs[i].L) /* length so we can use bitmasks for wrapping. */ maxlen <<= 1; /* If we need a delay of 128 samples, we must allocate 256! */ ptr->kstrongs[i].delayline = malloc(maxlen * sizeof(short)); /* Allocate memory for large delay. */ if (!ptr->kstrongs[i].delayline) { cleanup_paks(ptr); return 4; } for (k=0; kkstrongs[i].delayline[k] = 0; ptr->kstrongs[i].mask = maxlen - 1; /* Always use bitmask for ALL reading and writing! */ /* kstrongs[i]->n = 0; Really not necessary to initialise write-index n. */ ptr->kstrongs[i].lp_fixed = 0; /* Also clear these small intermediate signal buffers. */ ptr->kstrongs[i].lp_tuned = 0; /*---------------------- DETERMINE PANNING: ----------*/ P = (double)(i+2) / (double)(num+3); /* 0...1. */ ptr->kstrongs[i].amp_left = (short)(0.5 + (16384.0 * sqrt(1.0 - P))); ptr->kstrongs[i].amp_right = (short)(0.5 + (16384.0 * sqrt(P))); /* printf(" al=%d\n", ptr->kstrongs[i].amp_left); printf(" ar=%d\n", ptr->kstrongs[i].amp_right); printf(" sum=%d\n", ptr->kstrongs[i].amp_left + ptr->kstrongs[i].amp_right); */ } return 0; } /* This routine will be called by the PortAudio engine when audio is needed. It may called at interrupt level on some machines so don't do anything that could mess up the system like calling malloc() or free(). */ static int ksCallback( const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData ) { ks_data* data = (ks_data*)userData; short* out = (short*)outputBuffer; int i; (void) inputBuffer; /* Prevent unused variable warnings. */ for (i=0; ikstrongs; /* We always run ALL strings! */ for (n = data->num_kstrongs; n--;)/* Sure there is at least 1 string. */ { long b, y, a = data->x; /* Small signal from other strings. */ /*------------------------------- EXCITATION: --------------*/ if (s->pluck) { s->pluck--; a += (rand() & 32767L) - 16384L; } /*------------------------------- Karplus-Strong: ----------------*/ /* Simple tunable Karplus-Strong system: c0 = C*G 3 additions | 2 multiplications v ------------ ---------> * ----- | | | | | -1 v | -1 v x[n] --> + --> clip ----> z --> + ----> z --> * --> + ------> y[n] ^ a[n] b[n] ^ | | | | | c1 = (1-C)*G | | -L | ----------------- z <------------------------------ */ a += s->delayline[(s->n - s->L) & s->mask]; /* Add output from L samples ago. */ if (a > 32767L) a = 32767L; /* Clip back to 16 bits */ else if (a < -32768L) a = -32768L; /* Lowpass filter with 2 (fixed 1:1) coeffs. */ b = (a + s->lp_fixed); s->lp_fixed = a; /* Remember for next sample. */ /* Tunable lowpass filter. */ y = ((s->c0 * b) + /* 16*16 -> 31 bits (signed). */ (s->c1 * s->lp_tuned)) >> 15; s->lp_tuned = b; /* Remember for next sample. */ s->delayline[s->n++ & s->mask] = y; /* Write in buf, increment n. */ /*------------------------------ TO STEREO OUTPUT BUS: -----------*/ accu[0] += (s->amp_left * y) >> 14; /* Mult by 16384 at most. */ accu[1] += (s->amp_right * y) >> 14; s++; } if (accu[0] > 32767L) accu[0] = 32767L; /* Clip again. */ else if (accu[0] < -32768L) accu[0] = -32768L; if (accu[1] > 32767L) accu[1] = 32767L; else if (accu[1] < -32768L) accu[1] = -32768L; *out++ = (short)accu[0]; *out++ = (short)accu[1]; /* Let other strings resonate a bit. */ accu[0] += accu[1]; /* Make it mono again for filtering. */ data->dc = ((4095 * data->dc) + accu[0]) >> 12; /* 1 pole. */ data->x = (accu[0] - data->dc) >> 13; /* Feedback. */ } return 0; } int main(void) { const short kunst_der_fuge[] = /* {,} */ { /* Only the on-set of notes. */ 0,62,192,69,192,65,192,62,192,61,192,62,96,64,96,65,192,65,48,67, 48,65,48,64,48,62,0,69,96,64,96,65,0,74,96,67,96,69,0,72, 96,57,48,59,48,60,0,69,48,57,48,65,96,65,0,68,48,59,48,64, 96,69,48,65,48,64,0,71,48,62,48,64,0,72,96,66,96,67,0,72, 48,74,48,72,48,70,48,50,0,67,0,69,48,62,48,65,0,74,96,57, 0,64,96,73,96,53,0,62,0,74,48,69,48,72,48,64,48,50,0,65, 48,69,48,70,48,62,48,49,0,67,0,70,48,64,48,69,48,67,48,50, 0,65,48,64,48,52,0,62,48,61,48,53,0,62,0,69,48,72,48,67, 48,71,48,53,0,72,48,55,48,53,0,60,48,52,48,50,0,57,0,65, 0,72,48,62,48,72,48,64,48,62,0,65,48,69,48,71,48,56,48,60, 0,64,0,71,96,45,0,69,48,47,0,68,48,48,0,57,0,69,48,45, 48,53,0,62,48,60,48,53,0,56,0,62,0,71,48,47,0,65,48,52, 0,64,48,62,48,57,0,60,0,72,48,53,48,52,0,59,0,74,48,50, 48,52,0,60,0,67,48,55,0,70,48,54,0,69,48,57,48,55,0,60, 48,62,0,70,48,60,0,69,48,58,0,67,48,50,0,57,0,67,48,64, 48,65,48,62,48,57,0,70,48,53,48,52,0,55,48,50,48,52,0,60, 0,70,48,67,48,53,0,69,48,52,48,54,0,60,0,74,48,57,48,55, 0,59,48,54,48,56,0,64,0,74,48,71,48,57,0,72,48,56,48,57, 0,64,0,77,48,61,48,58,0,62,48,57,48,59,0,67,0,77,48,74, 48,60,0,76,48,59,48,61,0,67,0,69,48,64,48,62,0,65,0,74, 48,56,48,57,0,64,0,74,48,71,48,72,48,69,48,57,0,62,0,65, 48,53,48,55,0,70,48,64,48,55,0,61,0,69,48,52,48,53,0,62, 48,57,48,59,0,62,48,50,48,52,0,67,48,61,48,50,0,62,0,62, 0,67,48,64,48,57,0,65,48,74,48,50,0,64,0,69,48,47,48,49, 96,50,0,57,0,64,0,65,48,62,48,69,48,48,48,46,0,58,0,62, 96,67,96,45,0,61,0,69,48,52,48,57,48,43,48,41,0,62,48,43, 0,58,48,41,0,57,0,64,48,40,0,55,48,38,0,57,0,65,48,41, 48,40,0,59,48,38,48,45,0,60,0,65,48,67,48,65,48,41,0,64, 48,46,0,60,0,62,48,57,48,58,0,67,48,43,48,50,0,57,48,64, 48,62,0,65,48,53,48,57,0,62,0,64,48,59,48,60,0,69,48,45, 48,40,0,59,48,66,48,64,0,67,96,64,0,66,0,69,48,57,48,62, 48,74,48,52,0,68,0,76,48,62,48,60,48,59,48,57,0,60,0,69, 0,72,48,62,48,64,48,57,48,53,0,60,0,69,48,64,48,57,0,62, 48,60,48,53,0,59,0,62,0,68,48,53,48,52,0,65,48,50,48,48, 0,60,0,64,0,69,48,50,48,48,0,56,0,62,0,71,48,47,48,45, 0,57,0,60,0,72,48,55,48,54,0,69,48,52,48,50,0,54,0,72, 48,74,48,55,0,62,0,72,48,70,48,49,0,55,0,64,0,69,48,55, 48,53,0,69,48,52,48,45,0,53,48,52,0,67,48,50,0,65,0,69, 48,49,0,64,48,42,0,50,0,62,0,69,48,72,48,70,48,69,48,43, 0,70,48,69,48,45,0,67,48,66,48,46,0,62,0,67,48,62,48,67, 48,70,48,46,0,64,48,48,0,67,48,46,0,60,48,45,0,66,48,43, 0,60,0,67,48,57,48,58,48,64,48,43,0,57,0,61,48,40,48,41, 48,69,48,46,0,57,0,62,48,53,48,55,48,65,48,46,0,60,0,64, 48,43,48,45,48,72,48,50,0,60,0,65,48,57,48,59,48,69,48,50, 0,64,0,67,48,47,48,48,48,76,48,53,0,64,0,69,48,61,48,62, 48,72,48,53,0,67,0,71,48,50,48,52,48,79,48,45,0,52,0,67, 0,73,48,69,48,70,48,55,48,53,0,57,0,69,0,74,48,52,48,50, 0,76,48,49,48,50,0,53,0,69,0,76,48,73,48,74,48,41,48,46, 0,50,0,69,48,65,48,67,0,74,48,76,48,45,0,49,0,69,0,77, 48,76,48,79,48,70,48,50,0,69,48,77,48,52,0,67,0,76,48,74, 48,45,0,53,0,67,0,73,48,69,48,65,0,74,48,46,0,62,48,45, 0,53,0,69,48,43,0,55,0,71,48,45,0,53,0,72,48,52,0,67, 48,46,0,50,0,66,0,72,48,62,48,67,0,70,48,43,48,50,0,69, 48,64,48,57,0,65,48,53,48,50,0,58,0,65,48,62,48,52,0,67, 48,55,48,60,48,64,48,53,0,69,48,57,48,53,0,62,0,69,48,65, 48,55,0,70,48,50,48,52,0,60,48,67,48,53,0,69,48,49,48,50, 0,58,0,69,48,65,48,52,0,67,48,47,48,49,0,57,48,45,48,50, 0,65,48,62,48,50,0,52,0,69,48,48,48,47,0,57,48,45,48,47, 0,74,48,56,48,54,48,56,48,45,0,57,0,74,0,76,48,73,48,71, 48,55,0,73,48,53,0,74,0,81,48,52,0,69,48,50,0,76,48,49, 48,50,0,76,0,77,48,69,48,53,0,74,48,50,48,58,0,74,48,65, 48,55,0,70,48,52,48,57,0,70,0,73,48,64,48,69,48,65,48,59, 0,74,48,67,48,61,0,65,0,76,48,64,48,62,0,62,0,77,48,69, 48,59,0,74,48,55,0,71,48,60,0,67,0,77,48,64,0,79,48,57, 0,72,0,77,48,53,0,76,48,58,0,72,0,74,48,62,48,67,0,70, 48,76,48,69,0,73,48,64,48,65,0,74,48,77,48,65,0,67,0,70, 48,65,48,64,48,62,0,65,48,61,0,64,0,70,48,70,48,69,48,67, 48,62,0,64,0,65,48,60,48,58,0,62,48,57,0,61,48,55,0,62, 0,70,48,57,48,58,0,67,48,60,48,50,0,62,0,67,0,69,48,57, 48,62,48,65,48,57,0,64,48,59,48,61,0,76,48,64,48,53,0,61, 0,69,0,76,48,64,0,73,48,57,0,74,48,61,0,76,48,50,0,62, 0,77,48,74,48,58,0,67,0,79,48,55,0,70,48,49,0,52,0,69, 0,79,48,76,48,81,48,79,48,50,0,53,0,69,0,77,48,67,0,76, 48,52,0,55,0,65,0,74,48,64,0,73,48,53,0,57,0,62,0,74, 48,72,48,70,48,69,48,53,0,59,0,67,48,55,48,53,0,61,0,64, 48,52,48,50,0,57,0,62,0,67,48,64,48,65,48,62,48,50,0,69, 48,51,48,50,0,54,0,63,48,48,48,46,0,55,0,62,0,69,48,57, 0,66,48,58,0,67,48,70,48,46,0,72,48,48,48,46,0,60,0,66, 48,45,48,43,0,62,0,67,0,72,48,66,0,69,48,67,0,70,48,74, 48,75,48,55,48,43,0,70,0,74,48,53,48,45,0,52,0,70,0,73, 48,69,48,77,0,81,48,74,48,71,48,77,48,76,0,79,48,73,48,45, 0,69,0,79,48,73,48,53,0,74,0,77,48,50,0,69,48,55,0,70, 48,74,48,76,48,49,0,67,48,45,0,53,0,69,0,76,48,73,48,74, 48,50,0,65,48,57,0,64,48,71,48,45,0,72,48,67,48,46,0,50, 0,66,0,72,48,62,48,62,0,67,0,70,48,43,48,50,0,69,48,64, 48,57,0,65,48,53,0,69,48,50,0,58,0,65,0,74,48,46,0,74, 0,77,48,48,0,72,0,76,48,79,48,53,0,58,0,77,48,56,0,76, 48,57,0,72,0,74,48,72,48,53,0,62,0,70,0,74,48,50,0,69, 0,77,48,52,0,68,48,76,48,57,0,62,0,69,0,77,48,59,0,74, 48,60,0,76,48,64,48,57,0,65,0,69,0,76,48,53,0,74,48,55, 0,67,0,72,48,71,48,60,0,65,0,72,48,62,48,63,0,67,48,72, 0,81,48,60,0,62,0,69,0,79,48,57,0,78,48,58,0,67,0,79, 48,82,48,64,0,73,384,57,0,65,0,69,0,74,384,56,0,65,0,71, 0,74,192,57,0,65,0,69,0,74,96,64,48,62,48,64,0,74,48,71, 48,45,0,69,0,73,48,67,48,50,0,57,0,66,0,74,48,67,48,69, 48,72,48,62,0,70,48,67,48,72,48,69,48,50,0,58,0,62,0,72, 48,66,48,67,48,70,48,55,0,63,48,67,48,69,48,72,48,50,0,54, 0,62,0,72,48,70,48,57,0,72,48,75,48,55,0,58,48,74,48,57, 0,60,0,78,48,81,48,50,0,58,0,62,0,81,48,79,24,81,24,55, 0,82,48,55,0,73,48,50,0,55,0,58,0,74,48,62,0,70,48,60, 0,69,48,58,0,67,48,50,0,57,0,67,0,74,48,66,48,50,0,57, 0,64,0,74,48,67,48,66,-1,-1 }; PaStream* stream; PaStreamParameters outParams; PaError err; ks_data ks; int e; const double samplerate = 44100.0; const int frames_per_buffer = 512; /* 0: machine decides for itself. */ const short *music = kunst_der_fuge; short n; printf("PortAudio Karplus Strong demo.\n"); e = setup_paks(&ks, 45, samplerate); /* 45 strings. */ if (e) { printf("setup_paks() = %d\n", e); return 1; } err = Pa_Initialize(); if (err != paNoError) goto done; outParams.device = Pa_GetDefaultOutputDevice(); /* Default output device. */ outParams.channelCount = 2; /* Stereo output */ outParams.sampleFormat = paInt16; /* 16 bit signed int. */ outParams.suggestedLatency = Pa_GetDeviceInfo(outParams.device)->defaultHighOutputLatency; outParams.hostApiSpecificStreamInfo = NULL; err = Pa_OpenStream( &stream, NULL, /* No input. */ &outParams, /* As above. */ samplerate, frames_per_buffer, paClipOff, /* we won't output out of range samples. */ ksCallback, &ks ); if (err != paNoError) goto done; err = Pa_StartStream(stream); if (err != paNoError) goto done; Pa_Sleep(100); while (*music >= 0) { Pa_Sleep(*music++ * 6); /* Tempo: *5 */ n = (*music++)-38; if ((n >= 0) && (n < ks.num_kstrongs)) ks.kstrongs[n].pluck = (ks.kstrongs[n].L * 3) >> 1; else printf("String %d is not there (skipping)!\n", n); } Pa_Sleep(5000); err = Pa_CloseStream(stream); done: Pa_Terminate(); e = cleanup_paks(&ks); if (e) printf("cleanup_paks() = %d\n", e); if (err) { fprintf( stderr, "An error occured while using the portaudio stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); } else printf("PortAudio KS Finished.\n"); return err; }