robert@464: /*
robert@464:  ____  _____ _        _    
robert@464: | __ )| ____| |      / \   
robert@464: |  _ \|  _| | |     / _ \  
robert@464: | |_) | |___| |___ / ___ \ 
robert@464: |____/|_____|_____/_/   \_\
robert@464: 
robert@464: The platform for ultra-low latency audio and sensor processing
robert@464: 
robert@464: http://bela.io
robert@464: 
robert@464: A project of the Augmented Instruments Laboratory within the
robert@464: Centre for Digital Music at Queen Mary University of London.
robert@464: http://www.eecs.qmul.ac.uk/~andrewm
robert@464: 
robert@464: (c) 2016 Augmented Instruments Laboratory: Andrew McPherson,
robert@464:   Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack,
robert@464:   Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved.
robert@464: 
robert@464: The Bela software is distributed under the GNU Lesser General Public License
robert@464: (LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt
robert@464: */
robert@464: 
robert@464: 
robert@464: #include <Bela.h>
robert@464: #include <cmath>
robert@464: #include "SampleData.h"
robert@464: 
robert@464: SampleData gSampleData;	// User defined structure to get complex data from main
robert@464: int gReadPtr;			// Position of last read sample from file
robert@464: 
robert@464: // Task for handling the update of the frequencies using the matrix
robert@464: AuxiliaryTask gTriggerSamplesTask;
robert@464: 
robert@464: bool initialise_trigger();
robert@464: void trigger_samples();
robert@464: 
robert@464: bool setup(BelaContext *context, void *userData)
robert@464: {
robert@464: 
robert@464: 	// Retrieve a parameter passed in from the initAudio() call
robert@464: 	gSampleData = *(SampleData *)userData;
robert@464: 
robert@464: 	gReadPtr = -1;
robert@464: 
robert@464: 	// Initialise auxiliary tasks
robert@464: 	if(!initialise_trigger())
robert@464: 		return false;
robert@464: 
robert@464: 	return true;
robert@464: }
robert@464: 
robert@464: void render(BelaContext *context, void *userData)
robert@464: {
robert@464: 	for(unsigned int n = 0; n < context->audioFrames; n++) {
robert@464: 		float out = 0;
robert@464: 
robert@464: 		// If triggered...
robert@464: 		if(gReadPtr != -1)
robert@464: 			out += gSampleData.samples[gReadPtr++];	// ...read each sample...
robert@464: 
robert@464: 		if(gReadPtr >= gSampleData.sampleLen)
robert@464: 			gReadPtr = -1;
robert@464: 
robert@464: 		for(unsigned int channel = 0; channel < context->audioChannels; channel++)
robert@464: 			context->audioOut[n * context->audioChannels + channel] = out;	// ...and put it in both left and right channel
robert@464: 	}
robert@464: 
robert@464: 	// Request that the lower-priority task run at next opportunity
robert@464: 	Bela_scheduleAuxiliaryTask(gTriggerSamplesTask);
robert@464: }
robert@464: 
robert@464: // Initialise the auxiliary task
robert@464: // and print info
robert@464: 
robert@464: bool initialise_trigger()
robert@464: {
robert@464: 	if((gTriggerSamplesTask = Bela_createAuxiliaryTask(&trigger_samples, 50, "bela-trigger-samples")) == 0)
robert@464: 		return false;
robert@464: 
robert@464: 	rt_printf("Press 'a' to trigger sample, 's' to stop\n");
robert@464: 	rt_printf("Press 'q' to quit\n");
robert@464: 
robert@464: 	return true;
robert@464: }
robert@464: 
robert@464: // This is a lower-priority call to periodically read keyboard input
robert@464: // and trigger samples. By placing it at a lower priority,
robert@464: // it has minimal effect on the audio performance but it will take longer to
robert@464: // complete if the system is under heavy audio load.
robert@464: 
robert@464: void trigger_samples()
robert@464: {
robert@464: 	// This is not a real-time task!
robert@464: 	// Cos getchar is a system call, not handled by Xenomai.
robert@464: 	// This task will be automatically down graded.
robert@464: 
robert@464: 	char keyStroke = '.';
robert@464: 
robert@464: 	keyStroke =	getchar();
robert@464: 	while(getchar()!='\n'); // to read the first stroke
robert@464: 
robert@464: 	switch (keyStroke)
robert@464: 	{
robert@464: 		case 'a':
robert@464: 			gReadPtr = 0;
robert@464: 			break;
robert@464: 		case 's':
robert@464: 			gReadPtr = -1;
robert@464: 			break;
robert@464: 		case 'q':
robert@464: 			gShouldStop = true;
robert@464: 			break;
robert@464: 		default:
robert@464: 			break;
robert@464: 	}
robert@464: }
robert@464: 
robert@464: 
robert@464: void cleanup(BelaContext *context, void *userData)
robert@464: {
robert@464: 	delete[] gSampleData.samples;
robert@464: }
robert@464: 
robert@464: 
robert@464: /**
robert@500: \example samples/render.cpp
robert@464: 
robert@464: Playback WAV files
robert@464: ------------------
robert@464: 
robert@464: This sketch shows how to playback audio samples from a buffer.
robert@464: 
robert@464: An audio file is loaded into a buffer `SampleData` as `gSampleData`. This is 
robert@464: accessed with a read pointer that is incremented at audio rate within the render 
robert@464: function: `out += gSampleData.samples[gReadPtr++]`.
robert@464: 
robert@464: Note that the read pointer is stopped from incrementing past the length of the 
robert@464: `gSampleData`. This is achieved by comparing the read pointer value against the 
robert@464: sample length which we can access as follows: `gSampleData.sampleLen`.
robert@464: 
robert@464: The sample is triggered by keyboard input: (a) starts sample playback, (s) 
robert@464: stops sample playback. The triggering is treated as a lower priority task than 
robert@464: the audio. You can see this at the bottom of the render function: 
robert@464: `Bela_scheduleAuxiliaryTask(gTriggerSamplesTask)`;
robert@464: */