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