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 }
|
chris@543
|
70
|
chris@543
|
71 if(context->audioOutChannels <= 2 ||
|
chris@543
|
72 context->analogOutChannels <= 2){
|
chris@543
|
73 printf("Error: for this project, you need at least 2 analog and audio output channels.\n");
|
chris@543
|
74 return false;
|
chris@543
|
75 }
|
chris@543
|
76
|
robert@464
|
77 gSamplingPeriod = 1/context->audioSampleRate;
|
robert@464
|
78 return true;
|
robert@464
|
79 }
|
robert@464
|
80
|
robert@464
|
81 // render() is called regularly at the highest priority by the audio engine.
|
robert@464
|
82 // Input and output are given from the audio hardware and the other
|
robert@464
|
83 // ADCs and DACs (if available). If only audio is available, numMatrixFrames
|
robert@464
|
84 // will be 0.
|
robert@464
|
85
|
robert@464
|
86
|
robert@464
|
87 enum {kVelocity, kNoteOn, kNoteNumber};
|
robert@464
|
88 void render(BelaContext *context, void *userData)
|
robert@464
|
89 {
|
robert@464
|
90 // one way of getting the midi data is to parse them yourself
|
robert@464
|
91 // (you should set midi.enableParser(false) above):
|
robert@464
|
92 /*
|
robert@464
|
93 static midi_byte_t noteOnStatus = 0x90; //on channel 1
|
robert@464
|
94 static int noteNumber = 0;
|
robert@464
|
95 static int waitingFor = kNoteOn;
|
robert@464
|
96 static int playingNote = -1;
|
robert@464
|
97 int message;
|
robert@464
|
98 while ((message = midi.getInput()) >= 0){
|
robert@464
|
99 rt_printf("%d\n", message);
|
robert@464
|
100 switch(waitingFor){
|
robert@464
|
101 case kNoteOn:
|
robert@464
|
102 if(message == noteOnStatus){
|
robert@464
|
103 waitingFor = kNoteNumber;
|
robert@464
|
104 }
|
robert@464
|
105 break;
|
robert@464
|
106 case kNoteNumber:
|
robert@464
|
107 if((message & (1<<8)) == 0){
|
robert@464
|
108 noteNumber = message;
|
robert@464
|
109 waitingFor = kVelocity;
|
robert@464
|
110 }
|
robert@464
|
111 break;
|
robert@464
|
112 case kVelocity:
|
robert@464
|
113 if((message & (1<<8)) == 0){
|
robert@464
|
114 int _velocity = message;
|
robert@464
|
115 waitingFor = kNoteOn;
|
robert@464
|
116 // "monophonic" behaviour, with priority to the latest note on
|
robert@464
|
117 // i.e.: a note off from a previous note does not stop the current note
|
robert@464
|
118 // still you might end up having a key down and no note being played if you pressed and released another
|
robert@464
|
119 // key in the meantime
|
robert@464
|
120 if(_velocity == 0 && noteNumber == playingNote){
|
robert@464
|
121 noteOn = false;
|
robert@464
|
122 playingNote = -1;
|
robert@464
|
123 velocity = _velocity;
|
robert@464
|
124 } else if (_velocity > 0) {
|
robert@464
|
125 noteOn = true;
|
robert@464
|
126 velocity = _velocity;
|
robert@464
|
127 playingNote = noteNumber;
|
robert@464
|
128 f0 = powf(2, (playingNote-69)/12.0f) * 440;
|
robert@464
|
129 phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
|
robert@464
|
130 }
|
robert@464
|
131 rt_printf("NoteOn: %d, NoteNumber: %d, velocity: %d\n", noteOn, noteNumber, velocity);
|
robert@464
|
132 }
|
robert@464
|
133 break;
|
robert@464
|
134 }
|
robert@464
|
135 }
|
robert@464
|
136 */
|
robert@464
|
137 /*
|
robert@464
|
138 int num;
|
robert@464
|
139 //alternatively, you can use the built-in parser (only processes channel messages at the moment).
|
robert@464
|
140 while((num = midi.getParser()->numAvailableMessages()) > 0){
|
robert@464
|
141 static MidiChannelMessage message;
|
robert@464
|
142 message = midi.getParser()->getNextChannelMessage();
|
robert@464
|
143 message.prettyPrint();
|
robert@464
|
144 if(message.getType() == kmmNoteOn){
|
robert@464
|
145 f0 = powf(2, (message.getDataByte(0)-69)/12.0f) * 440;
|
robert@464
|
146 velocity = message.getDataByte(1);
|
robert@464
|
147 phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
|
robert@464
|
148 noteOn = velocity > 0;
|
robert@464
|
149 rt_printf("v0:%f, ph: %6.5f, velocity: %d\n", f0, phaseIncrement, gVelocity);
|
robert@464
|
150 }
|
robert@464
|
151 }
|
robert@464
|
152 */
|
robert@464
|
153 // the following block toggles the LED on an Owl pedal
|
robert@464
|
154 // and asks the pedal to return the status of the LED
|
robert@464
|
155 // using MIDI control changes
|
robert@464
|
156 for(unsigned int n = 0; n < context->analogFrames; n++){
|
robert@464
|
157 static int count = 0;
|
robert@464
|
158 static bool state = 0;
|
robert@464
|
159 analogWriteOnce(context, n, 1, state);
|
robert@464
|
160 if(count % 40000 == 0){
|
robert@464
|
161 state = !state;
|
robert@464
|
162 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
|
163 midi.writeOutput(bytes, 6);
|
robert@464
|
164 }
|
robert@464
|
165 count++;
|
robert@464
|
166 }
|
robert@464
|
167 for(unsigned int n = 0; n < context->audioFrames; n++){
|
robert@464
|
168 if(gIsNoteOn == 1){
|
robert@464
|
169 static float phase = 0;
|
robert@464
|
170 phase += gPhaseIncrement;
|
robert@464
|
171 if(phase > 2 * M_PI)
|
robert@464
|
172 phase -= 2 * M_PI;
|
robert@464
|
173 float value = sinf(phase) * gVelocity/128.0f;
|
robert@464
|
174 audioWrite(context, n, 0, value);
|
robert@464
|
175 audioWrite(context, n, 1, value);
|
robert@464
|
176 } else {
|
robert@464
|
177 audioWrite(context, n, 0, 0);
|
robert@464
|
178 audioWrite(context, n, 1, 0);
|
robert@464
|
179 }
|
robert@464
|
180 }
|
robert@464
|
181 }
|
robert@464
|
182
|
robert@464
|
183 // cleanup() is called once at the end, after the audio has stopped.
|
robert@464
|
184 // Release any resources that were allocated in setup().
|
robert@464
|
185
|
robert@464
|
186 void cleanup(BelaContext *context, void *userData)
|
robert@464
|
187 {
|
robert@464
|
188
|
robert@464
|
189 }
|