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 }