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