giuliomoro@181
|
1 /*
|
giuliomoro@181
|
2 * render.cpp
|
giuliomoro@181
|
3 *
|
giuliomoro@181
|
4 * Created on: Oct 24, 2014
|
giuliomoro@181
|
5 * Author: parallels
|
giuliomoro@181
|
6 */
|
giuliomoro@181
|
7
|
giuliomoro@301
|
8 #include <Bela.h>
|
giuliomoro@181
|
9 #include <Midi.h>
|
giuliomoro@181
|
10 #include <stdlib.h>
|
giuliomoro@181
|
11 #include <rtdk.h>
|
giuliomoro@181
|
12 #include <cmath>
|
giuliomoro@181
|
13
|
giuliomoro@224
|
14 float gFreq;
|
giuliomoro@224
|
15 float gPhaseIncrement = 0;
|
giuliomoro@224
|
16 bool gIsNoteOn = 0;
|
giuliomoro@224
|
17 int gVelocity = 0;
|
giuliomoro@224
|
18 float gSamplingPeriod = 0;
|
giuliomoro@224
|
19
|
giuliomoro@226
|
20 void midiMessageCallback(MidiChannelMessage message, void* arg){
|
giuliomoro@226
|
21 if(arg != NULL){
|
giuliomoro@226
|
22 rt_printf("Message from midi port %d: ", *(int*)arg);
|
giuliomoro@226
|
23 }
|
giuliomoro@224
|
24 message.prettyPrint();
|
giuliomoro@224
|
25 if(message.getType() == kmmNoteOn){
|
giuliomoro@224
|
26 gFreq = powf(2, (message.getDataByte(0)-69)/12.0f) * 440;
|
giuliomoro@224
|
27 gVelocity = message.getDataByte(1);
|
giuliomoro@224
|
28 gPhaseIncrement = 2 * M_PI * gFreq * gSamplingPeriod;
|
giuliomoro@224
|
29 gIsNoteOn = gVelocity > 0;
|
giuliomoro@224
|
30 rt_printf("v0:%f, ph: %6.5f, gVelocity: %d\n", gFreq, gPhaseIncrement, gVelocity);
|
giuliomoro@224
|
31 }
|
giuliomoro@224
|
32 }
|
giuliomoro@181
|
33 // setup() is called once before the audio rendering starts.
|
giuliomoro@181
|
34 // Use it to perform any initialisation and allocation which is dependent
|
giuliomoro@181
|
35 // on the period size or sample rate.
|
giuliomoro@181
|
36 //
|
giuliomoro@181
|
37 // userData holds an opaque pointer to a data structure that was passed
|
giuliomoro@181
|
38 // in from the call to initAudio().
|
giuliomoro@181
|
39 //
|
giuliomoro@181
|
40 // Return true on success; returning false halts the program.
|
giuliomoro@181
|
41 Midi midi;
|
giuliomoro@226
|
42 int gMidiPort0 = 0;
|
giuliomoro@301
|
43 bool setup(BelaContext *context, void *userData)
|
giuliomoro@181
|
44 {
|
giuliomoro@226
|
45 midi.readFrom(gMidiPort0);
|
giuliomoro@226
|
46 midi.writeTo(gMidiPort0);
|
giuliomoro@224
|
47 midi.enableParser(true);
|
giuliomoro@226
|
48 midi.setParserCallback(midiMessageCallback, &gMidiPort0);
|
giuliomoro@181
|
49 if(context->analogFrames == 0) {
|
giuliomoro@226
|
50 rt_printf("Error: this example needs the analog I/O to be enabled\n");
|
giuliomoro@181
|
51 return false;
|
giuliomoro@181
|
52 }
|
giuliomoro@224
|
53 gSamplingPeriod = 1/context->audioSampleRate;
|
giuliomoro@181
|
54 return true;
|
giuliomoro@181
|
55 }
|
giuliomoro@181
|
56
|
giuliomoro@181
|
57 // render() is called regularly at the highest priority by the audio engine.
|
giuliomoro@181
|
58 // Input and output are given from the audio hardware and the other
|
giuliomoro@181
|
59 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
|
giuliomoro@181
|
60 // will be 0.
|
giuliomoro@181
|
61
|
giuliomoro@181
|
62
|
giuliomoro@181
|
63 enum {kVelocity, kNoteOn, kNoteNumber};
|
giuliomoro@301
|
64 void render(BelaContext *context, void *userData)
|
giuliomoro@181
|
65 {
|
giuliomoro@224
|
66 // one way of getting the midi data is to parse them yourself
|
giuliomoro@224
|
67 // (you should set midi.enableParser(false) above):
|
giuliomoro@224
|
68 /*
|
giuliomoro@224
|
69 static midi_byte_t noteOnStatus = 0x90; //on channel 1
|
giuliomoro@181
|
70 static int noteNumber = 0;
|
giuliomoro@181
|
71 static int waitingFor = kNoteOn;
|
giuliomoro@184
|
72 static int playingNote = -1;
|
giuliomoro@197
|
73 int message;
|
giuliomoro@181
|
74 while ((message = midi.getInput()) >= 0){
|
giuliomoro@191
|
75 rt_printf("%d\n", message);
|
giuliomoro@181
|
76 switch(waitingFor){
|
giuliomoro@181
|
77 case kNoteOn:
|
giuliomoro@181
|
78 if(message == noteOnStatus){
|
giuliomoro@181
|
79 waitingFor = kNoteNumber;
|
giuliomoro@181
|
80 }
|
giuliomoro@181
|
81 break;
|
giuliomoro@181
|
82 case kNoteNumber:
|
giuliomoro@181
|
83 if((message & (1<<8)) == 0){
|
giuliomoro@181
|
84 noteNumber = message;
|
giuliomoro@181
|
85 waitingFor = kVelocity;
|
giuliomoro@181
|
86 }
|
giuliomoro@181
|
87 break;
|
giuliomoro@181
|
88 case kVelocity:
|
giuliomoro@181
|
89 if((message & (1<<8)) == 0){
|
giuliomoro@184
|
90 int _velocity = message;
|
giuliomoro@181
|
91 waitingFor = kNoteOn;
|
giuliomoro@184
|
92 // "monophonic" behaviour, with priority to the latest note on
|
giuliomoro@184
|
93 // i.e.: a note off from a previous note does not stop the current note
|
giuliomoro@184
|
94 // still you might end up having a key down and no note being played if you pressed and released another
|
giuliomoro@184
|
95 // key in the meantime
|
giuliomoro@184
|
96 if(_velocity == 0 && noteNumber == playingNote){
|
giuliomoro@181
|
97 noteOn = false;
|
giuliomoro@184
|
98 playingNote = -1;
|
giuliomoro@184
|
99 velocity = _velocity;
|
giuliomoro@184
|
100 } else if (_velocity > 0) {
|
giuliomoro@184
|
101 noteOn = true;
|
giuliomoro@184
|
102 velocity = _velocity;
|
giuliomoro@184
|
103 playingNote = noteNumber;
|
giuliomoro@184
|
104 f0 = powf(2, (playingNote-69)/12.0f) * 440;
|
giuliomoro@184
|
105 phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
|
giuliomoro@184
|
106 }
|
giuliomoro@181
|
107 rt_printf("NoteOn: %d, NoteNumber: %d, velocity: %d\n", noteOn, noteNumber, velocity);
|
giuliomoro@181
|
108 }
|
giuliomoro@181
|
109 break;
|
giuliomoro@181
|
110 }
|
giuliomoro@181
|
111 }
|
giuliomoro@199
|
112 */
|
giuliomoro@224
|
113 /*
|
giuliomoro@197
|
114 int num;
|
giuliomoro@197
|
115 //alternatively, you can use the built-in parser (only processes channel messages at the moment).
|
giuliomoro@197
|
116 while((num = midi.getParser()->numAvailableMessages()) > 0){
|
giuliomoro@197
|
117 static MidiChannelMessage message;
|
giuliomoro@197
|
118 message = midi.getParser()->getNextChannelMessage();
|
giuliomoro@197
|
119 message.prettyPrint();
|
giuliomoro@199
|
120 if(message.getType() == kmmNoteOn){
|
giuliomoro@199
|
121 f0 = powf(2, (message.getDataByte(0)-69)/12.0f) * 440;
|
giuliomoro@199
|
122 velocity = message.getDataByte(1);
|
giuliomoro@199
|
123 phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
|
giuliomoro@199
|
124 noteOn = velocity > 0;
|
giuliomoro@224
|
125 rt_printf("v0:%f, ph: %6.5f, velocity: %d\n", f0, phaseIncrement, gVelocity);
|
giuliomoro@199
|
126 }
|
giuliomoro@197
|
127 }
|
giuliomoro@224
|
128 */
|
giuliomoro@224
|
129 // the following block toggles the LED on an Owl pedal
|
giuliomoro@224
|
130 // and asks the pedal to return the status of the LED
|
giuliomoro@224
|
131 // using MIDI control changes
|
giuliomoro@191
|
132 for(unsigned int n = 0; n < context->analogFrames; n++){
|
giuliomoro@191
|
133 static int count = 0;
|
giuliomoro@191
|
134 static bool state = 0;
|
andrewm@308
|
135 analogWriteOnce(context, n, 1, state);
|
giuliomoro@191
|
136 if(count % 40000 == 0){
|
giuliomoro@191
|
137 state = !state;
|
giuliomoro@224
|
138 midi_byte_t bytes[6] = {176, 30, (char)(state*127), 176, 67, 30}; // toggle the OWL led and ask for the led status
|
giuliomoro@191
|
139 midi.writeOutput(bytes, 6);
|
giuliomoro@191
|
140 }
|
giuliomoro@199
|
141 count++;
|
giuliomoro@199
|
142 }
|
giuliomoro@199
|
143 for(unsigned int n = 0; n < context->audioFrames; n++){
|
giuliomoro@224
|
144 if(gIsNoteOn == 1){
|
giuliomoro@181
|
145 static float phase = 0;
|
giuliomoro@224
|
146 phase += gPhaseIncrement;
|
giuliomoro@181
|
147 if(phase > 2 * M_PI)
|
giuliomoro@181
|
148 phase -= 2 * M_PI;
|
giuliomoro@224
|
149 float value = sinf(phase) * gVelocity/128.0f;
|
andrewm@308
|
150 audioWrite(context, n, 0, value);
|
andrewm@308
|
151 audioWrite(context, n, 1, value);
|
giuliomoro@181
|
152 } else {
|
andrewm@308
|
153 audioWrite(context, n, 0, 0);
|
andrewm@308
|
154 audioWrite(context, n, 1, 0);
|
giuliomoro@181
|
155 }
|
giuliomoro@181
|
156 }
|
giuliomoro@181
|
157 }
|
giuliomoro@181
|
158
|
giuliomoro@181
|
159 // cleanup() is called once at the end, after the audio has stopped.
|
giuliomoro@181
|
160 // Release any resources that were allocated in setup().
|
giuliomoro@181
|
161
|
giuliomoro@301
|
162 void cleanup(BelaContext *context, void *userData)
|
giuliomoro@181
|
163 {
|
giuliomoro@181
|
164
|
giuliomoro@181
|
165 }
|