victor@1
|
1 /*
|
robert@372
|
2 ____ _____ _ _
|
robert@372
|
3 | __ )| ____| | / \
|
robert@372
|
4 | _ \| _| | | / _ \
|
robert@372
|
5 | |_) | |___| |___ / ___ \
|
robert@372
|
6 |____/|_____|_____/_/ \_\.io
|
robert@372
|
7
|
victor@1
|
8 */
|
victor@1
|
9
|
robert@372
|
10 /*
|
robert@372
|
11 *
|
robert@372
|
12 * Andrew McPherson and Victor Zappi
|
robert@372
|
13 * Queen Mary, University of London
|
robert@372
|
14 */
|
robert@372
|
15
|
robert@372
|
16 /**
|
robert@372
|
17 \example 4_audio_samples
|
robert@372
|
18
|
robert@372
|
19 Playback WAV files
|
robert@372
|
20 ------------------
|
robert@372
|
21
|
robert@372
|
22 This sketch shows how to playback audio samples from a buffer.
|
robert@372
|
23
|
robert@372
|
24 An audio file is loaded into a buffer `SampleData` as `gSampleData`. This is
|
robert@372
|
25 accessed with a read pointer that is incremented at audio rate within the render
|
robert@372
|
26 function: `out += gSampleData.samples[gReadPtr++]`.
|
robert@372
|
27
|
robert@372
|
28 Note that the read pointer is stopped from incrementing past the length of the
|
robert@372
|
29 `gSampleData`. This is achieved by comparing the read pointer value against the
|
robert@372
|
30 sample length which we can access as follows: `gSampleData.sampleLen`.
|
robert@372
|
31
|
robert@372
|
32 The sample is triggered by keyboard input: (a) starts sample playback, (s)
|
robert@372
|
33 stops sample playback. The triggering is treated as a lower priority task than
|
robert@372
|
34 the audio. You can see this at the bottom of the render function:
|
robert@372
|
35 `Bela_scheduleAuxiliaryTask(gTriggerSamplesTask)`;
|
robert@372
|
36 */
|
victor@1
|
37
|
giuliomoro@301
|
38 #include <Bela.h>
|
victor@1
|
39 #include <cmath>
|
victor@1
|
40 #include "SampleData.h"
|
victor@1
|
41
|
victor@1
|
42 SampleData gSampleData; // User defined structure to get complex data from main
|
victor@1
|
43 int gReadPtr; // Position of last read sample from file
|
victor@1
|
44
|
victor@1
|
45 // Task for handling the update of the frequencies using the matrix
|
victor@1
|
46 AuxiliaryTask gTriggerSamplesTask;
|
victor@1
|
47
|
victor@1
|
48 bool initialise_trigger();
|
victor@1
|
49 void trigger_samples();
|
victor@1
|
50
|
andrewm@56
|
51 // setup() is called once before the audio rendering starts.
|
victor@1
|
52 // Use it to perform any initialisation and allocation which is dependent
|
victor@1
|
53 // on the period size or sample rate.
|
victor@1
|
54 //
|
victor@1
|
55 // userData holds an opaque pointer to a data structure that was passed
|
victor@1
|
56 // in from the call to initAudio().
|
victor@1
|
57 //
|
victor@1
|
58 // Return true on success; returning false halts the program.
|
victor@1
|
59
|
giuliomoro@301
|
60 bool setup(BelaContext *context, void *userData)
|
victor@1
|
61 {
|
victor@1
|
62
|
victor@1
|
63 // Retrieve a parameter passed in from the initAudio() call
|
victor@1
|
64 gSampleData = *(SampleData *)userData;
|
victor@1
|
65
|
victor@1
|
66 gReadPtr = -1;
|
victor@1
|
67
|
victor@1
|
68 // Initialise auxiliary tasks
|
victor@1
|
69 if(!initialise_trigger())
|
victor@1
|
70 return false;
|
victor@1
|
71
|
victor@1
|
72 return true;
|
victor@1
|
73 }
|
victor@1
|
74
|
victor@1
|
75 // render() is called regularly at the highest priority by the audio engine.
|
victor@1
|
76 // Input and output are given from the audio hardware and the other
|
victor@1
|
77 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
|
victor@1
|
78 // will be 0.
|
victor@1
|
79
|
giuliomoro@301
|
80 void render(BelaContext *context, void *userData)
|
victor@1
|
81 {
|
andrewm@56
|
82 for(unsigned int n = 0; n < context->audioFrames; n++) {
|
victor@1
|
83 float out = 0;
|
victor@1
|
84
|
victor@1
|
85 // If triggered...
|
victor@1
|
86 if(gReadPtr != -1)
|
victor@1
|
87 out += gSampleData.samples[gReadPtr++]; // ...read each sample...
|
victor@1
|
88
|
victor@1
|
89 if(gReadPtr >= gSampleData.sampleLen)
|
victor@1
|
90 gReadPtr = -1;
|
victor@1
|
91
|
andrewm@56
|
92 for(unsigned int channel = 0; channel < context->audioChannels; channel++)
|
andrewm@52
|
93 context->audioOut[n * context->audioChannels + channel] = out; // ...and put it in both left and right channel
|
victor@1
|
94 }
|
victor@1
|
95
|
victor@1
|
96 // Request that the lower-priority task run at next opportunity
|
giuliomoro@301
|
97 Bela_scheduleAuxiliaryTask(gTriggerSamplesTask);
|
victor@1
|
98 }
|
victor@1
|
99
|
victor@1
|
100 // Initialise the auxiliary task
|
victor@1
|
101 // and print info
|
victor@1
|
102
|
victor@1
|
103 bool initialise_trigger()
|
victor@1
|
104 {
|
andrewm@303
|
105 if((gTriggerSamplesTask = Bela_createAuxiliaryTask(&trigger_samples, 50, "bela-trigger-samples")) == 0)
|
victor@1
|
106 return false;
|
victor@1
|
107
|
victor@1
|
108 rt_printf("Press 'a' to trigger sample, 's' to stop\n");
|
victor@1
|
109 rt_printf("Press 'q' to quit\n");
|
victor@1
|
110
|
victor@1
|
111 return true;
|
victor@1
|
112 }
|
victor@1
|
113
|
victor@1
|
114 // This is a lower-priority call to periodically read keyboard input
|
victor@1
|
115 // and trigger samples. By placing it at a lower priority,
|
victor@1
|
116 // it has minimal effect on the audio performance but it will take longer to
|
victor@1
|
117 // complete if the system is under heavy audio load.
|
victor@1
|
118
|
victor@1
|
119 void trigger_samples()
|
victor@1
|
120 {
|
victor@1
|
121 // This is not a real-time task!
|
victor@1
|
122 // Cos getchar is a system call, not handled by Xenomai.
|
victor@1
|
123 // This task will be automatically down graded.
|
victor@1
|
124
|
victor@1
|
125 char keyStroke = '.';
|
victor@1
|
126
|
victor@1
|
127 keyStroke = getchar();
|
victor@1
|
128 while(getchar()!='\n'); // to read the first stroke
|
victor@1
|
129
|
victor@1
|
130 switch (keyStroke)
|
victor@1
|
131 {
|
victor@1
|
132 case 'a':
|
victor@1
|
133 gReadPtr = 0;
|
victor@1
|
134 break;
|
victor@1
|
135 case 's':
|
victor@1
|
136 gReadPtr = -1;
|
victor@1
|
137 break;
|
victor@1
|
138 case 'q':
|
victor@1
|
139 gShouldStop = true;
|
victor@1
|
140 break;
|
victor@1
|
141 default:
|
victor@1
|
142 break;
|
victor@1
|
143 }
|
victor@1
|
144 }
|
victor@1
|
145
|
victor@1
|
146
|
victor@1
|
147
|
andrewm@56
|
148 // cleanup() is called once at the end, after the audio has stopped.
|
andrewm@56
|
149 // Release any resources that were allocated in setup().
|
victor@1
|
150
|
giuliomoro@301
|
151 void cleanup(BelaContext *context, void *userData)
|
victor@1
|
152 {
|
victor@1
|
153 delete[] gSampleData.samples;
|
victor@1
|
154 }
|