victor@1: /* robert@372: ____ _____ _ _ robert@372: | __ )| ____| | / \ robert@372: | _ \| _| | | / _ \ robert@372: | |_) | |___| |___ / ___ \ robert@372: |____/|_____|_____/_/ \_\.io robert@372: victor@1: */ victor@1: robert@372: /* robert@372: * robert@372: * Andrew McPherson and Victor Zappi robert@372: * Queen Mary, University of London robert@372: */ robert@372: robert@372: /** robert@372: \example 4_audio_samples robert@372: robert@372: Playback WAV files robert@372: ------------------ robert@372: robert@372: This sketch shows how to playback audio samples from a buffer. robert@372: robert@372: An audio file is loaded into a buffer `SampleData` as `gSampleData`. This is robert@372: accessed with a read pointer that is incremented at audio rate within the render robert@372: function: `out += gSampleData.samples[gReadPtr++]`. robert@372: robert@372: Note that the read pointer is stopped from incrementing past the length of the robert@372: `gSampleData`. This is achieved by comparing the read pointer value against the robert@372: sample length which we can access as follows: `gSampleData.sampleLen`. robert@372: robert@372: The sample is triggered by keyboard input: (a) starts sample playback, (s) robert@372: stops sample playback. The triggering is treated as a lower priority task than robert@372: the audio. You can see this at the bottom of the render function: robert@372: `Bela_scheduleAuxiliaryTask(gTriggerSamplesTask)`; robert@372: */ victor@1: giuliomoro@301: #include victor@1: #include victor@1: #include "SampleData.h" victor@1: victor@1: SampleData gSampleData; // User defined structure to get complex data from main victor@1: int gReadPtr; // Position of last read sample from file victor@1: victor@1: // Task for handling the update of the frequencies using the matrix victor@1: AuxiliaryTask gTriggerSamplesTask; victor@1: victor@1: bool initialise_trigger(); victor@1: void trigger_samples(); victor@1: andrewm@56: // setup() is called once before the audio rendering starts. victor@1: // Use it to perform any initialisation and allocation which is dependent victor@1: // on the period size or sample rate. victor@1: // victor@1: // userData holds an opaque pointer to a data structure that was passed victor@1: // in from the call to initAudio(). victor@1: // victor@1: // Return true on success; returning false halts the program. victor@1: giuliomoro@301: bool setup(BelaContext *context, void *userData) victor@1: { victor@1: victor@1: // Retrieve a parameter passed in from the initAudio() call victor@1: gSampleData = *(SampleData *)userData; victor@1: victor@1: gReadPtr = -1; victor@1: victor@1: // Initialise auxiliary tasks victor@1: if(!initialise_trigger()) victor@1: return false; victor@1: victor@1: return true; victor@1: } victor@1: victor@1: // render() is called regularly at the highest priority by the audio engine. victor@1: // Input and output are given from the audio hardware and the other victor@1: // ADCs and DACs (if available). If only audio is available, numMatrixFrames victor@1: // will be 0. victor@1: giuliomoro@301: void render(BelaContext *context, void *userData) victor@1: { andrewm@56: for(unsigned int n = 0; n < context->audioFrames; n++) { victor@1: float out = 0; victor@1: victor@1: // If triggered... victor@1: if(gReadPtr != -1) victor@1: out += gSampleData.samples[gReadPtr++]; // ...read each sample... victor@1: victor@1: if(gReadPtr >= gSampleData.sampleLen) victor@1: gReadPtr = -1; victor@1: andrewm@56: for(unsigned int channel = 0; channel < context->audioChannels; channel++) andrewm@52: context->audioOut[n * context->audioChannels + channel] = out; // ...and put it in both left and right channel victor@1: } victor@1: victor@1: // Request that the lower-priority task run at next opportunity giuliomoro@301: Bela_scheduleAuxiliaryTask(gTriggerSamplesTask); victor@1: } victor@1: victor@1: // Initialise the auxiliary task victor@1: // and print info victor@1: victor@1: bool initialise_trigger() victor@1: { andrewm@303: if((gTriggerSamplesTask = Bela_createAuxiliaryTask(&trigger_samples, 50, "bela-trigger-samples")) == 0) victor@1: return false; victor@1: victor@1: rt_printf("Press 'a' to trigger sample, 's' to stop\n"); victor@1: rt_printf("Press 'q' to quit\n"); victor@1: victor@1: return true; victor@1: } victor@1: victor@1: // This is a lower-priority call to periodically read keyboard input victor@1: // and trigger samples. By placing it at a lower priority, victor@1: // it has minimal effect on the audio performance but it will take longer to victor@1: // complete if the system is under heavy audio load. victor@1: victor@1: void trigger_samples() victor@1: { victor@1: // This is not a real-time task! victor@1: // Cos getchar is a system call, not handled by Xenomai. victor@1: // This task will be automatically down graded. victor@1: victor@1: char keyStroke = '.'; victor@1: victor@1: keyStroke = getchar(); victor@1: while(getchar()!='\n'); // to read the first stroke victor@1: victor@1: switch (keyStroke) victor@1: { victor@1: case 'a': victor@1: gReadPtr = 0; victor@1: break; victor@1: case 's': victor@1: gReadPtr = -1; victor@1: break; victor@1: case 'q': victor@1: gShouldStop = true; victor@1: break; victor@1: default: victor@1: break; victor@1: } victor@1: } victor@1: victor@1: victor@1: andrewm@56: // cleanup() is called once at the end, after the audio has stopped. andrewm@56: // Release any resources that were allocated in setup(). victor@1: giuliomoro@301: void cleanup(BelaContext *context, void *userData) victor@1: { victor@1: delete[] gSampleData.samples; victor@1: }