Music 3SI |
Week 3 Online Tutorial |
Introduction to Audio/Multimedia Application ProgrammingAnnouncements | Course Info | Weekly Schedule | Tutorials | Links |
|
Week 3 Online Tutorial: Stk Programming (2)1. Callback (Continued)
We begin with the code from last week.
ex7.cpp [download]
// Plays a sine wave (440[Hz]) realtime, stops when "Enter" is pressed.
#include <iostream> #include "sineWave.h" #include "RtAudio.h" // III': Callback function // Continues to run until step V int tick(char *buffer, int bufferSize, void *dataPointer) { // dataPointer: pointer handed over from the end of step II // Casted to a corresponding type SineWave *sine = (SineWave *) dataPointer; // buffer: pointer to the audio data buffer StkFloat *samples = (StkFloat *) buffer; // Every time this callback function gets called, // it handles just as many samples as "bufferSize" for ( int i=0; i<bufferSize; i++ ) *samples++ = sine->tick(); return 0; } int main (int argc, char* const argv[]) { Stk::setSampleRate( 44100.0 ); // I: Initialize input (SineWave) SineWave* sine; // SineWave (note: pointer) sine = new SineWave(); // "new": allocate a "pointee" sine->setFrequency(440.0); // "->": method for a pointer // II: Initialize output (RtAudio) RtAudio* dac; // RtAudio: realtime audio I/O // RtAudioFormat: 64-bit FP (or 32-bit FP). Check RtAudio.h RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; // RT_BUFFER_SIZE: defined in Stk.h int bufferSize = RT_BUFFER_SIZE; // Allocate a pointee and opens a stream dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 0); // tick: name of callback function // sine: pointer handed over to callback dac->setStreamCallback(&tick, (void *)sine); // III: Start stream to output dac->startStream(); // IV: Waiting for [ENTER], while III' is running char keyhit; std::cout << "\nPlaying ... press [enter] to quit.\n"; std::cin.get(keyhit); // V: Stop stream: in two steps! dac->cancelStreamCallback(); dac->closeStream(); // VI: Clean up used memory space delete dac; delete sine; // VII: The End! return 0; } Duplex mode
Setting "non-zero" values for both input and output channel of RtAudio makes it possible to have realtime input and output at the same time.
ex8.cpp [download]
// Duplex mode: bypasses realtime input through realtime output
// stops when "Enter" is pressed. #include <iostream> #include "SineWave.h" #include "RtAudio.h" // Callback function int tick(char *buffer, int bufferSize, void *) { // buffer: pointer to the audio data buffer StkFloat *samples = (StkFloat *) buffer; // Bypass: do NOTHING (just move over to the next address) for ( int i=0; i<bufferSize; i++ ) *samples++; return 0; } int main (int argc, char* const argv[]) { Stk::setSampleRate( 44100.0 ); RtAudio* dac; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; // Both input and output have one channel each dac = new RtAudio(0, 1, 0, 1, format, (int)Stk::sampleRate(), &bufferSize, 0); // NULL: no pointer to hand over to callback dac->setStreamCallback(&tick, (void *)NULL); dac->startStream(); char keyhit; std::cout << "\nPlaying ... press [enter] to quit.\n"; std::cin.get(keyhit); dac->cancelStreamCallback(); dac->closeStream(); delete dac; return 0; } 2. Effects
Now we add some effects to realtime input.
ex9.cpp [download]
// Applies chorus effect to realtime input and plays through realtime output
// stops when "Enter" is pressed. #include <iostream> #include "Chorus.h" #include "RtAudio.h" // Callback function int tick(char *buffer, int bufferSize, void *dataPointer) { // Pointer to "Chorus" from main Chorus *chorus = (Chorus *) dataPointer; StkFloat *samples = (StkFloat *) buffer; // Passes RT input through "chorus" to RT output for ( int i=0; i<bufferSize; i++ ) *samples++ = chorus->tick(*samples); return 0; } int main (int argc, char* const argv[]) { // Input arguments as chorus parameters float effectMix = atof( argv[1] ); // Wet/dry ratio (0.0~1.0) float modDepth = atof( argv[2] ); // Modulation depth (0.0~1.0) float modFrequency = atof( argv[3] ); // Modulation frequency [Hz] Stk::setSampleRate( 44100.0 ); Chorus* chorus = new Chorus(); // Chorus chorus->setEffectMix(effectMix); // Wet/dry ratio chorus->setModDepth(modDepth); // Mod. depth chorus->setModFrequency(modFrequency); // Mod. freq. RtAudio* dac; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; dac = new RtAudio(0, 1, 0, 1, format, (int)Stk::sampleRate(), &bufferSize, 0); dac->setStreamCallback(&tick, (void *)chorus); dac->startStream(); char keyhit; std::cout << "\nPlaying ... press [enter] to quit.\n"; std::cin.get(keyhit); dac->cancelStreamCallback(); dac->closeStream(); delete dac; delete chorus; return 0; }
ex10 is an example with pitch shifter.
ex10.cpp [download]3. Instruments
Instead of realtime input from ADC, we use one of the Stk instruments classes. ex11 is an example of BeeThree (B3), with text-based user interface to change its pitch.
ex11.cpp [download]
// Pitch-controllable "BeeThree" instrument. Stops when pitch is set "zero"
#include <iostream> #include "BeeThree.h" #include "RtAudio.h" // Callback function int tick(char *buffer, int bufferSize, void *dataPointer) { // Pointer to "BeeThree" from main BeeThree *beethree = (BeeThree *) dataPointer; StkFloat *samples = (StkFloat *) buffer; // Filling buffer with "tick"s of beethree for ( int i=0; i<bufferSize; i++ ) *samples++ = beethree->tick(); return 0; } int main (int argc, char* const argv[]) { Stk::setSampleRate( 44100.0 ); float frequency = 440.0; // Initial frequency value BeeThree* beethree = new BeeThree(); // BeeThree beethree->noteOn( frequency, 0.7 ); // "noteOn" RtAudio* dac; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 0); dac->setStreamCallback(&tick, (void *)beethree); dac->startStream(); // Loop to take frequency input. Enter "0" to exit. while ( frequency != 0 ) { std::cout << "\nEnter new frequency (0 to quit): "; std::cin >> frequency; beethree->setFrequency( frequency ); } dac->cancelStreamCallback(); dac->closeStream(); delete dac; delete beethree; return 0; } 4. Error (or Exception) Handling
Exceptions provide a way to react to exceptional circumstances (like runtime errors) in our program by transferring control to special functions called handlers.
ex12 does exactly the same work as ex11, but with some exception handlings. ex12.cpp [download]
// Pitch-controllable "BeeThree" instrument. Stops when pitch is set "zero"
// Now with error handling. #include <iostream> #include "BeeThree.h" #include "RtAudio.h" int tick(char *buffer, int bufferSize, void *dataPointer) { BeeThree *beethree = (BeeThree *) dataPointer; StkFloat *samples = (StkFloat *) buffer; for ( int i=0; i<bufferSize; i++ ) *samples++ = beethree->tick(); return 0; } int main (int argc, char* const argv[]) { Stk::setSampleRate( 44100.0 ); float frequency = 440.0; BeeThree* beethree = new BeeThree(); beethree->noteOn( frequency, 0.7 ); RtAudio* dac; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; try { dac = new RtAudio(0, 1, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 0); } catch (RtError& error) { error.printMessage(); exit(0); } try { dac->setStreamCallback(&tick, (void *)beethree); dac->startStream(); } catch (RtError& error) { error.printMessage(); goto cleanup; } while ( frequency != 0 ) { std::cout << "\nEnter new frequency (0 to quit): "; std::cin >> frequency; beethree->setFrequency( frequency ); } try { dac->cancelStreamCallback(); dac->closeStream(); } catch (RtError& error) { error.printMessage(); } cleanup: delete dac; delete beethree; return 0; } 5. Tips For Assignment 1Two channels: stereo
For stereo input/output, set the number of channels of RtAudio as 2.
dac = new RtAudio(0, 2, 0, 0, format, (int)Stk::sampleRate(), &bufferSize, 0);
And process one more sample in the callback loop. Here, sound level from the right channel is lower than the left channel by 20 [dB].
int tick(char *buffer, int bufferSize, void *dataPointer) {
BeeThree *beethree = (BeeThree *) dataPointer; StkFloat *samples = (StkFloat *) buffer; for ( int i=0; i<bufferSize; i++ ) StkFloat bee3 = beethree->tick(); // tick result of "beethree" *samples++ = bee3; // left *samples++ = bee3 * 0.1; // right: multiplied by 0.1 -> 20[dB] lower return 0; }
ex13 is this "stereo" version of ex12.
ex13.cpp [download]Multiple effects
To have more than one effects, pointers of those effect instances need to be sent over to the callback. This can be done by using a structure which, in fact, can contain not only the pointers to effects but also other control parameters. Compare ex14 with ex9 and ex10.
ex14.cpp [download]
// Multi effect example: RT Input --> Chorus --> PitShift --> Pan --> RT Output
// Panning value (0.0 ~ 1.0) can be adjusted during playback. // stops when entered pan value goes out of range. #include <iostream> #include "Chorus.h" #include "PitShift.h" #include "RtAudio.h" // TickData: structure to contain pointers to chorus, pitshift, and pan struct TickData { Chorus *chorus; PitShift *pitshift; StkFloat pan; }; int tick(char *buffer, int bufferSize, void *dataPointer) { TickData *data = (TickData *) dataPointer; StkFloat *samples = (StkFloat *) buffer; for ( int i=0; i<bufferSize; i++ ) { *samples++ = (1.0 - data->pan) * data->pitshift->tick( data->chorus->tick( *samples ) ); *samples++ = data->pan * data->pitshift->tick( data->chorus->tick( *samples ) ); } return 0; } int main (int argc, char* const argv[]) { float chorusMix = atof( argv[1] ); // Chorus wet/dry ratio (0.0~1.0) float modDepth = atof( argv[2] ); // Modulation depth (0.0~1.0) float modFrequency = atof( argv[3] ); // Modulation frequency [Hz] float pitshiftMix = atof( argv[4] ); // PitShift wet/dry ratio (0.0~1.0) float shift = atof( argv[5] ); // Amount of pitch shift float pan = 0.5; Stk::setSampleRate( 44100.0 ); TickData data; data.chorus = new Chorus(); // Chorus data.chorus->setEffectMix(effectMix); data.chorus->setModDepth(modDepth); data.chorus->setModFrequency(modFrequency); data.pitshift = new PitShift(); // PitShift data.pitshift->setEffectMix( pitshiftMix ); data.pitshift->setShift( shift ); RtAudio* dac; RtAudioFormat format = ( sizeof(StkFloat) == 8 ) ? RTAUDIO_FLOAT64:RTAUDIO_FLOAT32; int bufferSize = RT_BUFFER_SIZE; dac = new RtAudio(0, 2, 0, 2, format, (int)Stk::sampleRate(), &bufferSize, 0); dac->setStreamCallback(&tick, (void *)&data); dac->startStream(); while ( frequency != 0 ) { std::cout << "Enter new panning value (0.0 <= pan <= 1.0): "; std::cin >> pan; data.pan = pan; } dac->cancelStreamCallback(); dac->closeStream(); delete dac; delete data.chorus; delete data.pitshift; return 0; } |
|
Announcements | Course Info | Weekly Schedule | Tutorials | Links
Music 3SI / Spring 2006 / CCRMA, Stanford University
Woon Seung Yeo Last updated: Wed, 26 May 2006 16:00:25 -0700 |