Mercurial > hg > beaglert
comparison core/default_libpd_render.cpp @ 467:03a2cd5f151b prerelease
Libpd headers moved to include/, rm useless basic_libpd example, fixed Makefile to actually build default_libpd_render
author | Giulio Moro <giuliomoro@yahoo.it> |
---|---|
date | Mon, 20 Jun 2016 16:57:35 +0100 |
parents | examples/basic_libpd/render.cpp@9dc5a0ccad25 |
children | 5a936f8e9447 |
comparison
equal
deleted
inserted
replaced
466:1d585c5fa663 | 467:03a2cd5f151b |
---|---|
1 /* | |
2 * render.cpp | |
3 * | |
4 * Created on: Oct 24, 2014 | |
5 * Author: parallels | |
6 */ | |
7 | |
8 #include <Bela.h> | |
9 #include <DigitalChannelManager.h> | |
10 #include <cmath> | |
11 #include <I2c_Codec.h> | |
12 #include <PRU.h> | |
13 #include <stdio.h> | |
14 #include <libpd/z_libpd.h> | |
15 #include <libpd/s_stuff.h> | |
16 #include <UdpServer.h> | |
17 #include <Midi.h> | |
18 //extern t_sample* sys_soundin; | |
19 //extern t_sample* sys_soundout; | |
20 // if you are 100% sure of what value was used to compile libpd/puredata, then | |
21 // you could #define this instead of getting it at runtime. It has proved to give some 0.3% | |
22 // performance boost when it is 8 (thanks to vectorize optimizations I guess). | |
23 int gBufLength; | |
24 | |
25 float* gInBuf; | |
26 float* gOutBuf; | |
27 | |
28 void pdnoteon(int ch, int pitch, int vel) { | |
29 printf("noteon: %d %d %d\n", ch, pitch, vel); | |
30 } | |
31 | |
32 void Bela_printHook(const char *recv){ | |
33 rt_printf("%s", recv); | |
34 } | |
35 | |
36 void libpdReadFilesLoop(){ | |
37 while(!gShouldStop){ | |
38 // check for modified sockets/file descriptors | |
39 // (libpd would normally do this every block WITHIN the audio thread) | |
40 // not sure if this is thread-safe at the moment | |
41 libpd_sys_microsleep(0); | |
42 usleep(1000); | |
43 } | |
44 } | |
45 | |
46 #define PARSE_MIDI | |
47 static AuxiliaryTask libpdReadFilesTask; | |
48 static Midi midi; | |
49 static DigitalChannelManager dcm; | |
50 //UdpServer udpServer; | |
51 | |
52 void sendDigitalMessage(bool state, unsigned int delay, void* receiverName){ | |
53 libpd_float((char*)receiverName, (float)state); | |
54 // rt_printf("%s: %d\n", (char*)receiverName, state); | |
55 } | |
56 | |
57 #define LIBPD_DIGITAL_OFFSET 11 // digitals are preceded by 2 audio and 8 analogs (even if using a different number of analogs) | |
58 | |
59 void Bela_messageHook(const char *source, const char *symbol, int argc, t_atom *argv){ | |
60 if(strcmp(source, "bela_setDigital") == 0){ | |
61 // symbol is the direction, argv[0] is the channel, argv[1] (optional) | |
62 // is signal("sig" or "~") or message("message", default) rate | |
63 bool isMessageRate = true; // defaults to message rate | |
64 bool direction = 0; // initialize it just to avoid the compiler's warning | |
65 bool disable = false; | |
66 if(strcmp(symbol, "in") == 0){ | |
67 direction = INPUT; | |
68 } else if(strcmp(symbol, "out") == 0){ | |
69 direction = OUTPUT; | |
70 } else if(strcmp(symbol, "disable") == 0){ | |
71 disable = true; | |
72 } else { | |
73 return; | |
74 } | |
75 if(argc == 0){ | |
76 return; | |
77 } else if (libpd_is_float(&argv[0]) == false){ | |
78 return; | |
79 } | |
80 int channel = libpd_get_float(&argv[0]) - LIBPD_DIGITAL_OFFSET; | |
81 if(disable == true){ | |
82 dcm.unmanage(channel); | |
83 return; | |
84 } | |
85 if(argc >= 2){ | |
86 t_atom* a = &argv[1]; | |
87 if(libpd_is_symbol(a)){ | |
88 char *s = libpd_get_symbol(a); | |
89 if(strcmp(s, "~") == 0 || strncmp(s, "sig", 3) == 0){ | |
90 isMessageRate = false; | |
91 } | |
92 } | |
93 } | |
94 dcm.manage(channel, direction, isMessageRate); | |
95 } | |
96 } | |
97 | |
98 void Bela_floatHook(const char *source, float value){ | |
99 // let's make this as optimized as possible for built-in digital Out parsing | |
100 // the built-in digital receivers are of the form "bela_digitalOutXX" where XX is between 11 and 26 | |
101 static int prefixLength = 15; // strlen("bela_digitalOut") | |
102 if(strncmp(source, "bela_digitalOut", prefixLength)==0){ | |
103 if(source[prefixLength] != 0){ //the two ifs are used instead of if(strlen(source) >= prefixLength+2) | |
104 if(source[prefixLength + 1] != 0){ | |
105 // quickly convert the suffix to integer, assuming they are numbers, avoiding to call atoi | |
106 int receiver = ((source[prefixLength] - 48) * 10); | |
107 receiver += (source[prefixLength+1] - 48); | |
108 unsigned int channel = receiver - 11; // go back to the actual Bela digital channel number | |
109 if(channel >= 0 && channel < 16){ //16 is the hardcoded value for the number of digital channels | |
110 dcm.setValue(channel, value); | |
111 } | |
112 } | |
113 } | |
114 } | |
115 } | |
116 | |
117 char receiverNames[16][21]={ | |
118 {"bela_digitalIn11"},{"bela_digitalIn12"},{"bela_digitalIn13"},{"bela_digitalIn14"},{"bela_digitalIn15"}, | |
119 {"bela_digitalIn16"},{"bela_digitalIn17"},{"bela_digitalIn18"},{"bela_digitalIn19"},{"bela_digitalIn20"}, | |
120 {"bela_digitalIn21"},{"bela_digitalIn22"},{"bela_digitalIn23"},{"bela_digitalIn24"},{"bela_digitalIn25"}, | |
121 {"bela_digitalIn26"} | |
122 }; | |
123 | |
124 static unsigned int analogChannelsInUse; | |
125 static unsigned int gLibpdBlockSize; | |
126 static unsigned int gChannelsInUse = 26; | |
127 | |
128 bool setup(BelaContext *context, void *userData) | |
129 { | |
130 dcm.setCallback(sendDigitalMessage); | |
131 analogChannelsInUse = min(context->analogChannels, gChannelsInUse - context->audioChannels - context->digitalChannels); | |
132 if(context->digitalChannels > 0){ | |
133 for(unsigned int ch = 0; ch < context->digitalChannels; ++ch){ | |
134 dcm.setCallbackArgument(ch, receiverNames[ch]); | |
135 } | |
136 } | |
137 midi.readFrom(0); | |
138 midi.writeTo(0); | |
139 #ifdef PARSE_MIDI | |
140 midi.enableParser(true); | |
141 #else | |
142 midi.enableParser(false); | |
143 #endif /* PARSE_MIDI */ | |
144 // gChannelsInUse = min((int)(context->analogChannels+context->audioChannels), (int)gChannelsInUse); | |
145 // udpServer.bindToPort(1234); | |
146 | |
147 gLibpdBlockSize = libpd_blocksize(); | |
148 // check that we are not running with a blocksize smaller than gLibPdBlockSize | |
149 // it would still work, but the load would be executed unevenly between calls to render | |
150 if(context->audioFrames < gLibpdBlockSize){ | |
151 fprintf(stderr, "Error: minimum block size must be %d\n", gLibpdBlockSize); | |
152 return false; | |
153 } | |
154 // set hooks before calling libpd_init | |
155 libpd_set_printhook(Bela_printHook); | |
156 libpd_set_floathook(Bela_floatHook); | |
157 libpd_set_messagehook(Bela_messageHook); | |
158 libpd_set_noteonhook(pdnoteon); | |
159 //TODO: add hooks for other midi events and generate MIDI output appropriately | |
160 libpd_init(); | |
161 //TODO: ideally, we would analyse the ASCII of the patch file and find out which in/outs to use | |
162 libpd_init_audio(gChannelsInUse, gChannelsInUse, context->audioSampleRate); | |
163 gInBuf = libpd_get_sys_soundin(); | |
164 gOutBuf = libpd_get_sys_soundout(); | |
165 | |
166 libpd_start_message(1); // one entry in list | |
167 libpd_add_float(1.0f); | |
168 libpd_finish_message("pd", "dsp"); | |
169 | |
170 gBufLength = max(gLibpdBlockSize, context->audioFrames); | |
171 | |
172 | |
173 // bind your receivers here | |
174 libpd_bind("bela_digitalOut11"); | |
175 libpd_bind("bela_digitalOut12"); | |
176 libpd_bind("bela_digitalOut13"); | |
177 libpd_bind("bela_digitalOut14"); | |
178 libpd_bind("bela_digitalOut15"); | |
179 libpd_bind("bela_digitalOut16"); | |
180 libpd_bind("bela_digitalOut17"); | |
181 libpd_bind("bela_digitalOut18"); | |
182 libpd_bind("bela_digitalOut19"); | |
183 libpd_bind("bela_digitalOut20"); | |
184 libpd_bind("bela_digitalOut21"); | |
185 libpd_bind("bela_digitalOut22"); | |
186 libpd_bind("bela_digitalOut23"); | |
187 libpd_bind("bela_digitalOut24"); | |
188 libpd_bind("bela_digitalOut25"); | |
189 libpd_bind("bela_digitalOut26"); | |
190 libpd_bind("bela_setDigital"); | |
191 | |
192 char file[] = "_main.pd"; | |
193 char folder[] = "./"; | |
194 // open patch [; pd open file folder( | |
195 libpd_openfile(file, folder); | |
196 libpdReadFilesTask = Bela_createAuxiliaryTask(libpdReadFilesLoop, 60, "libpdReadFiles"); | |
197 Bela_scheduleAuxiliaryTask(libpdReadFilesTask); | |
198 | |
199 | |
200 return true; | |
201 } | |
202 | |
203 // render() is called regularly at the highest priority by the audio engine. | |
204 // Input and output are given from the audio hardware and the other | |
205 // ADCs and DACs (if available). If only audio is available, numMatrixFrames | |
206 // will be 0. | |
207 | |
208 void render(BelaContext *context, void *userData) | |
209 { | |
210 int num; | |
211 // the safest thread-safe option to handle MIDI input is to process the MIDI buffer | |
212 // from the audio thread. | |
213 #ifdef PARSE_MIDI | |
214 while((num = midi.getParser()->numAvailableMessages()) > 0){ | |
215 static MidiChannelMessage message; | |
216 message = midi.getParser()->getNextChannelMessage(); | |
217 //message.prettyPrint(); // use this to print beautified message (channel, data bytes) | |
218 switch(message.getType()){ | |
219 case kmmNoteOn: | |
220 { | |
221 int noteNumber = message.getDataByte(0); | |
222 int velocity = message.getDataByte(1); | |
223 int channel = message.getChannel(); | |
224 libpd_noteon(channel, noteNumber, velocity); | |
225 break; | |
226 } | |
227 case kmmNoteOff: | |
228 { | |
229 /* PureData does not seem to handle noteoff messages as per the MIDI specs, | |
230 * so that the noteoff velocity is ignored. Here we convert them to noteon | |
231 * with a velocity of 0. | |
232 */ | |
233 int noteNumber = message.getDataByte(0); | |
234 // int velocity = message.getDataByte(1); // would be ignored by Pd | |
235 int channel = message.getChannel(); | |
236 libpd_noteon(channel, noteNumber, 0); | |
237 break; | |
238 } | |
239 case kmmControlChange: | |
240 { | |
241 int channel = message.getChannel(); | |
242 int controller = message.getDataByte(0); | |
243 int value = message.getDataByte(1); | |
244 libpd_controlchange(channel, controller, value); | |
245 break; | |
246 } | |
247 case kmmProgramChange: | |
248 { | |
249 int channel = message.getChannel(); | |
250 int program = message.getDataByte(0); | |
251 libpd_programchange(channel, program); | |
252 break; | |
253 } | |
254 case kmmPolyphonicKeyPressure: | |
255 { | |
256 int channel = message.getChannel(); | |
257 int pitch = message.getDataByte(0); | |
258 int value = message.getDataByte(1); | |
259 libpd_polyaftertouch(channel, pitch, value); | |
260 break; | |
261 } | |
262 case kmmChannelPressure: | |
263 { | |
264 int channel = message.getChannel(); | |
265 int value = message.getDataByte(0); | |
266 libpd_aftertouch(channel, value); | |
267 break; | |
268 } | |
269 case kmmPitchBend: | |
270 { | |
271 int channel = message.getChannel(); | |
272 int value = (message.getDataByte(1) << 7)| message.getDataByte(0); | |
273 libpd_pitchbend(channel, value); | |
274 break; | |
275 } | |
276 case kmmNone: | |
277 case kmmAny: | |
278 break; | |
279 } | |
280 } | |
281 #else | |
282 int input; | |
283 while((input = midi.getInput()) >= 0){ | |
284 libpd_midibyte(0, input); | |
285 } | |
286 #endif /* PARSE_MIDI */ | |
287 | |
288 static unsigned int numberOfPdBlocksToProcess = gBufLength / gLibpdBlockSize; | |
289 | |
290 // these are reset at every audio callback. Persistence across audio callbacks | |
291 // is handled by the core code. | |
292 // setDataOut = 0; | |
293 // clearDataOut = 0; | |
294 | |
295 for(unsigned int tick = 0; tick < numberOfPdBlocksToProcess; ++tick){ | |
296 unsigned int audioFrameBase = gLibpdBlockSize * tick; | |
297 unsigned int j; | |
298 unsigned int k; | |
299 float* p0; | |
300 float* p1; | |
301 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
302 for (k = 0, p1 = p0; k < context->audioChannels; k++, p1 += gLibpdBlockSize) { | |
303 *p1 = audioRead(context, audioFrameBase + j, k); | |
304 } | |
305 } | |
306 // then analogs | |
307 // this loop resamples by ZOH, as needed, using m | |
308 if(context->analogChannels == 8 ){ //hold the value for two frames | |
309 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
310 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
311 unsigned int analogFrame = (audioFrameBase + j) / 2; | |
312 *p1 = analogRead(context, analogFrame, k); | |
313 } | |
314 } | |
315 } else if(context->analogChannels == 4){ //write every frame | |
316 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
317 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
318 unsigned int analogFrame = audioFrameBase + j; | |
319 *p1 = analogRead(context, analogFrame, k); | |
320 } | |
321 } | |
322 } else if(context->analogChannels == 2){ //drop every other frame | |
323 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
324 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
325 unsigned int analogFrame = (audioFrameBase + j) * 2; | |
326 *p1 = analogRead(context, analogFrame, k); | |
327 } | |
328 } | |
329 } | |
330 | |
331 //then digital | |
332 // note: in multiple places below we assume that the number of digitals is same as number of audio | |
333 // digital in at message-rate | |
334 dcm.processInput(&context->digital[audioFrameBase], gLibpdBlockSize); | |
335 | |
336 // digital in at signal-rate | |
337 for (j = 0, p0 = gInBuf; j < gLibpdBlockSize; j++, p0++) { | |
338 unsigned int digitalFrame = audioFrameBase + j; | |
339 for (k = 0, p1 = p0 + gLibpdBlockSize * (context->audioChannels + 8); | |
340 k < 16; ++k, p1 += gLibpdBlockSize) { | |
341 if(dcm.isSignalRate(k) && dcm.isInput(k)){ // only process input channels that are handled at signal rate | |
342 *p1 = digitalRead(context, digitalFrame, k); | |
343 } | |
344 } | |
345 } | |
346 | |
347 libpd_process_sys(); // process the block | |
348 | |
349 //digital out | |
350 // digital out at signal-rate | |
351 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | |
352 unsigned int digitalFrame = (audioFrameBase + j); | |
353 for (k = 0, p1 = p0 + gLibpdBlockSize * (context->audioChannels + 8); | |
354 k < context->digitalChannels; k++, p1 += gLibpdBlockSize) { | |
355 if(dcm.isSignalRate(k) && dcm.isOutput(k)){ // only process output channels that are handled at signal rate | |
356 digitalWriteOnce(context, digitalFrame, k, *p1 > 0.5); | |
357 } | |
358 } | |
359 } | |
360 | |
361 // digital out at message-rate | |
362 dcm.processOutput(&context->digital[audioFrameBase], gLibpdBlockSize); | |
363 | |
364 //audio | |
365 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j++, p0++) { | |
366 for (k = 0, p1 = p0; k < context->audioChannels; k++, p1 += gLibpdBlockSize) { | |
367 audioWrite(context, audioFrameBase + j, k, *p1); | |
368 } | |
369 } | |
370 | |
371 //analog | |
372 if(context->analogChannels == 8){ | |
373 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j += 2, p0 += 2) { //write every two frames | |
374 unsigned int analogFrame = (audioFrameBase + j) / 2; | |
375 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
376 analogWriteOnce(context, analogFrame, k, *p1); | |
377 } | |
378 } | |
379 } else if(context->analogChannels == 4){ //write every frame | |
380 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; ++j, ++p0) { | |
381 unsigned int analogFrame = (audioFrameBase + j); | |
382 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
383 analogWriteOnce(context, analogFrame, k, *p1); | |
384 } | |
385 } | |
386 } else if(context->analogChannels == 2){ //write every frame twice | |
387 for (j = 0, p0 = gOutBuf; j < gLibpdBlockSize; j++, p0++) { | |
388 for (k = 0, p1 = p0 + gLibpdBlockSize * context->audioChannels; k < analogChannelsInUse; k++, p1 += gLibpdBlockSize) { | |
389 int analogFrame = audioFrameBase * 2 + j * 2; | |
390 analogWriteOnce(context, analogFrame, k, *p1); | |
391 analogWriteOnce(context, analogFrame + 1, k, *p1); | |
392 } | |
393 } | |
394 } | |
395 } | |
396 } | |
397 | |
398 // cleanup() is called once at the end, after the audio has stopped. | |
399 // Release any resources that were allocated in setup(). | |
400 | |
401 void cleanup(BelaContext *context, void *userData) | |
402 { | |
403 } |