Mercurial > hg > opencollidoscope
comparison CollidoscopeApp/src/CollidoscopeApp.cpp @ 0:02467299402e
First import
CollidoscopeApp for Raspberry Pi
JackDevice
Teensy code for Collidoscope
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 30 Jun 2016 14:50:06 +0200 |
parents | |
children | 75b744078d66 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:02467299402e |
---|---|
1 #include "cinder/app/App.h" | |
2 #include "cinder/app/RendererGl.h" | |
3 #include "cinder/gl/gl.h" | |
4 #include "cinder/Exception.h" | |
5 | |
6 | |
7 #include "Config.h" | |
8 #include "Wave.h" | |
9 #include "DrawInfo.h" | |
10 #include "Log.h" | |
11 #include "AudioEngine.h" | |
12 #include "Oscilloscope.h" | |
13 #include "Messages.h" | |
14 #include "MIDI.h" | |
15 | |
16 using namespace ci; | |
17 using namespace ci::app; | |
18 | |
19 using namespace std; | |
20 | |
21 | |
22 class CollidoscopeApp : public App { | |
23 public: | |
24 | |
25 void setup() override; | |
26 void setupGraphics(); | |
27 | |
28 void receiveCommands(); | |
29 | |
30 void keyDown( KeyEvent event ) override; | |
31 void update() override; | |
32 void draw() override; | |
33 void resize() override; | |
34 | |
35 Config mConfig; | |
36 collidoscope::MIDI mMIDI; | |
37 AudioEngine mAudioEngine; | |
38 | |
39 array< shared_ptr< Wave >, NUM_WAVES > mWaves; | |
40 array< shared_ptr< DrawInfo >, NUM_WAVES > mDrawInfos; | |
41 array< shared_ptr< Oscilloscope >, NUM_WAVES > mOscilloscopes; | |
42 // buffers to read the wave messages as a new wave gets recorded | |
43 array< RecordWaveMsg*, NUM_WAVES> mRecordWaveMessageBuffers; | |
44 array< vector< CursorTriggerMsg >, NUM_WAVES > mCursorTriggerMessagesBuffers; | |
45 | |
46 double mSecondsPerChunk; | |
47 | |
48 ~CollidoscopeApp(); | |
49 | |
50 }; | |
51 | |
52 | |
53 void CollidoscopeApp::setup() | |
54 { | |
55 hideCursor(); | |
56 /* setup is logged: setup steps and errors */ | |
57 | |
58 /*try { | |
59 mConfig.loadFromFile( "./collidoscope_config.xml" ); | |
60 } | |
61 catch ( const Exception &e ){ | |
62 logError( string("Exception loading config from file:") + e.what() ); | |
63 }*/ | |
64 | |
65 // setup buffers to read messages from audio thread | |
66 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
67 mRecordWaveMessageBuffers[i] = new RecordWaveMsg[mConfig.getNumChunks()]; | |
68 mCursorTriggerMessagesBuffers[i].reserve( mConfig.getCursorTriggerMessageBufSize() ); | |
69 } | |
70 | |
71 mAudioEngine.setup( mConfig ); | |
72 | |
73 setupGraphics(); | |
74 | |
75 mSecondsPerChunk = mConfig.getWaveLen() / mConfig.getNumChunks(); | |
76 | |
77 try { | |
78 mMIDI.setup( mConfig ); | |
79 } | |
80 catch ( const collidoscope::MIDIException &e ){ | |
81 logError( string( "Exception opening MIDI input device: " ) + e.getMessage() ); | |
82 } | |
83 | |
84 } | |
85 | |
86 void CollidoscopeApp::setupGraphics() | |
87 { | |
88 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
89 | |
90 mDrawInfos[i] = make_shared< DrawInfo >( i ); | |
91 mWaves[i] = make_shared< Wave >(mConfig.getNumChunks(), mConfig.getWaveSelectionColor(i) ); | |
92 mOscilloscopes[i] = make_shared< Oscilloscope >( mAudioEngine.getAudioOutputBuffer( i ).getNumFrames() / mConfig.getOscilloscopeNumPointsDivider() ); | |
93 | |
94 } | |
95 } | |
96 | |
97 void CollidoscopeApp::keyDown( KeyEvent event ) | |
98 { | |
99 char c = event.getChar(); | |
100 | |
101 switch (c){ | |
102 case 'r' : | |
103 mAudioEngine.record( 0 ); | |
104 mAudioEngine.record( 1 ); | |
105 break; | |
106 | |
107 case 'w': { | |
108 mWaves[0]->getSelection().setSize(mWaves[0]->getSelection().getSize() + 1); | |
109 | |
110 size_t numSelectionChunks = mWaves[0]->getSelection().getSize(); | |
111 // how many samples in one selection ? | |
112 size_t selectionSize = numSelectionChunks * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); | |
113 | |
114 mAudioEngine.setSelectionSize(0, selectionSize); | |
115 }; | |
116 break; | |
117 | |
118 case 'e': { | |
119 mWaves[1]->getSelection().setSize(mWaves[1]->getSelection().getSize() + 1); | |
120 | |
121 size_t numSelectionChunks = mWaves[1]->getSelection().getSize(); | |
122 // how many samples in one selection ? | |
123 size_t selectionSize = numSelectionChunks * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); | |
124 | |
125 mAudioEngine.setSelectionSize(1, selectionSize); | |
126 }; | |
127 break; | |
128 | |
129 case 's': { | |
130 | |
131 mWaves[0]->getSelection().setSize( mWaves[0]->getSelection().getSize() - 1 ); | |
132 | |
133 size_t selectionSize = mWaves[0]->getSelection().getSize() *(mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); | |
134 mAudioEngine.setSelectionSize( 0, selectionSize ); | |
135 }; | |
136 break; | |
137 | |
138 case 'd': { | |
139 | |
140 for( size_t waveIdx = 0; waveIdx < NUM_WAVES; waveIdx++){ | |
141 size_t selectionStart = mWaves[waveIdx]->getSelection().getStart(); | |
142 mWaves[waveIdx]->getSelection().setStart( selectionStart + 1 ); | |
143 | |
144 selectionStart = mWaves[waveIdx]->getSelection().getStart(); | |
145 mAudioEngine.setSelectionStart( waveIdx, selectionStart * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); | |
146 } | |
147 }; | |
148 | |
149 break; | |
150 | |
151 case 'a': { | |
152 size_t selectionStart = mWaves[0]->getSelection().getStart(); | |
153 | |
154 if ( selectionStart == 0 ) | |
155 return; | |
156 | |
157 mWaves[0]->getSelection().setStart( selectionStart - 1 ); | |
158 | |
159 selectionStart = mWaves[0]->getSelection().getStart(); | |
160 | |
161 mAudioEngine.setSelectionStart( 0, selectionStart * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); | |
162 }; | |
163 break; | |
164 | |
165 | |
166 case 'p': | |
167 | |
168 mWaves[0]->setCursorPos( 4, mWaves[0]->getSelection().getStart(), *mDrawInfos[0] ) ; | |
169 break; | |
170 | |
171 case 'f': | |
172 setFullScreen( !isFullScreen() ); | |
173 break; | |
174 | |
175 case ' ': { | |
176 static bool isOn = false; | |
177 isOn = !isOn; | |
178 if ( isOn ){ | |
179 mAudioEngine.loopOn( 0 ); | |
180 mAudioEngine.loopOn( 1 ); | |
181 } | |
182 else{ | |
183 mAudioEngine.loopOff( 0 ); | |
184 mAudioEngine.loopOff( 1 ); | |
185 } | |
186 }; | |
187 break; | |
188 | |
189 case 'm' : | |
190 mAudioEngine.setGrainDurationCoeff(0, 8); | |
191 break; | |
192 | |
193 case 'n': { | |
194 mAudioEngine.setGrainDurationCoeff( 0, 1 ); | |
195 }; | |
196 break; | |
197 | |
198 case '9': { | |
199 int c = mWaves[0]->getSelection().getParticleSpread(); | |
200 if ( c == 1 ) | |
201 return; | |
202 else | |
203 c -= 1; | |
204 | |
205 mAudioEngine.setGrainDurationCoeff( 0, c ); | |
206 mWaves[0]->getSelection().setParticleSpread( float( c ) ); | |
207 mAudioEngine.setGrainDurationCoeff( 1, c ); | |
208 mWaves[1]->getSelection().setParticleSpread( float( c ) ); | |
209 }; break; | |
210 | |
211 case '0': { | |
212 int c = mWaves[0]->getSelection().getParticleSpread(); | |
213 if ( c == 8 ) | |
214 return; | |
215 else | |
216 c += 1; | |
217 | |
218 mAudioEngine.setGrainDurationCoeff( 0, c ); | |
219 mWaves[0]->getSelection().setParticleSpread( float( c ) ); | |
220 }; break; | |
221 | |
222 } | |
223 | |
224 } | |
225 | |
226 void CollidoscopeApp::update() | |
227 { | |
228 // check incoming commands | |
229 receiveCommands(); | |
230 | |
231 // check new wave chunks from recorder buffer | |
232 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
233 size_t availableRead = mAudioEngine.getRecordWaveAvailable( i ); | |
234 mAudioEngine.readRecordWave( i, mRecordWaveMessageBuffers[i], availableRead ); | |
235 | |
236 for ( size_t msgIndex = 0; msgIndex < availableRead; msgIndex++ ){ | |
237 const RecordWaveMsg & msg = mRecordWaveMessageBuffers[i][msgIndex]; | |
238 | |
239 if ( msg.cmd == Command::WAVE_CHUNK ){ | |
240 mWaves[i]->setChunk( msg.index, msg.arg1, msg.arg2 ); | |
241 } | |
242 else if ( msg.cmd == Command::WAVE_START ){ | |
243 mWaves[i]->reset( true ); // reset only chunks but leave selection | |
244 } | |
245 | |
246 } | |
247 } | |
248 | |
249 // check if new cursors have been triggered | |
250 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
251 | |
252 mAudioEngine.checkCursorTriggers( i, mCursorTriggerMessagesBuffers[i] ); | |
253 for ( auto & trigger : mCursorTriggerMessagesBuffers[i] ){ | |
254 const int nodeID = trigger.synthID; | |
255 | |
256 switch ( trigger.cmd ){ | |
257 | |
258 case Command::TRIGGER_UPDATE: { | |
259 mWaves[i]->setCursorPos( nodeID, mWaves[i]->getSelection().getStart(), *mDrawInfos[i] ); | |
260 }; | |
261 break; | |
262 | |
263 case Command::TRIGGER_END: { | |
264 mWaves[i]->removeCursor( nodeID ); | |
265 }; | |
266 break; | |
267 | |
268 } | |
269 | |
270 } | |
271 mCursorTriggerMessagesBuffers[i].clear(); | |
272 } | |
273 | |
274 // update cursors | |
275 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
276 mWaves[i]->update( mSecondsPerChunk, *mDrawInfos[i] ); | |
277 } | |
278 | |
279 // update oscilloscope | |
280 | |
281 for ( size_t i = 0; i < NUM_WAVES; i++ ){ | |
282 const audio::Buffer &audioOutBuffer = mAudioEngine.getAudioOutputBuffer( i ); | |
283 // one oscilloscope sample | |
284 | |
285 for ( size_t j = 0; j < mOscilloscopes[i]->getNumPoints(); j++ ){ | |
286 mOscilloscopes[i]->setPoint( j, audioOutBuffer.getData()[j], *mDrawInfos[i] ); | |
287 } | |
288 } | |
289 | |
290 | |
291 | |
292 } | |
293 | |
294 void CollidoscopeApp::draw() | |
295 { | |
296 gl::clear( Color( 0, 0, 0 ) ); | |
297 | |
298 for ( int i = 0; i < NUM_WAVES; i++ ){ | |
299 if ( i == 1 ){ | |
300 /* for the upper wave flip the x over the center of the screen which is | |
301 the composition of rotate on the y-axis and translate by -screenwidth*/ | |
302 gl::pushModelMatrix(); | |
303 gl::rotate( float(M_PI), ci::vec3( 0, 1, 0 ) ); | |
304 gl::translate( float( -getWindowWidth() ), 0.0f ); | |
305 mOscilloscopes[i]->draw(); | |
306 mWaves[i]->draw( *mDrawInfos[i] ); | |
307 gl::popModelMatrix(); | |
308 } | |
309 else{ | |
310 | |
311 mOscilloscopes[i]->draw(); | |
312 mWaves[i]->draw( *mDrawInfos[i] ); | |
313 } | |
314 } | |
315 } | |
316 | |
317 void CollidoscopeApp::resize() | |
318 { | |
319 App::resize(); | |
320 | |
321 for ( int i = 0; i < NUM_WAVES; i++ ){ | |
322 // reset the drawing information with the new windows size and same shrink factor | |
323 mDrawInfos[i]->reset( getWindow()->getBounds(), 3.0f / 5.0f ); | |
324 | |
325 /* reset the oscilloscope points to zero */ | |
326 for ( int j = 0; j < mOscilloscopes[i]->getNumPoints(); j++ ){ | |
327 mOscilloscopes[i]->setPoint(j, 0.0f, *mDrawInfos[i] ); | |
328 } | |
329 } | |
330 } | |
331 | |
332 | |
333 | |
334 void CollidoscopeApp::receiveCommands() | |
335 { | |
336 // check new midi messages | |
337 static std::vector<collidoscope::MIDIMessage> midiMessages; | |
338 mMIDI.checkMessages( midiMessages ); | |
339 | |
340 | |
341 for ( auto &m : midiMessages ){ | |
342 | |
343 const size_t waveIdx = mConfig.getWaveForMIDIChannel( m.getChannel() ); | |
344 if ( waveIdx >= NUM_WAVES ) | |
345 continue; | |
346 | |
347 if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eNoteOn ){ | |
348 int midiNote = m.getData_1(); | |
349 mAudioEngine.noteOn( waveIdx, midiNote ); | |
350 } | |
351 else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eNoteOff ){ | |
352 int midiNote = m.getData_1(); | |
353 mAudioEngine.noteOff( waveIdx, midiNote ); | |
354 } | |
355 else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::ePitchBend ){ | |
356 const uint16_t MSB = m.getData_2() << 7; | |
357 uint16_t value = m.getData_1(); // LSB | |
358 | |
359 value |= MSB; | |
360 | |
361 | |
362 // value ranges from 0 to 1050. check boundaries in case sensor gives bad values | |
363 if ( value > 149 ){ // FIXME pareametrizer | |
364 continue; | |
365 } | |
366 | |
367 size_t startChunk = value; | |
368 | |
369 const size_t selectionSizeBeforeStartUpdate = mWaves[waveIdx]->getSelection().getSize(); | |
370 mWaves[waveIdx]->getSelection().setStart( startChunk ); | |
371 | |
372 mAudioEngine.setSelectionStart( waveIdx, startChunk * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); | |
373 | |
374 const size_t newSelectionSize = mWaves[waveIdx]->getSelection().getSize(); | |
375 if ( selectionSizeBeforeStartUpdate != newSelectionSize ){ | |
376 mAudioEngine.setSelectionSize( waveIdx, newSelectionSize * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); | |
377 } | |
378 | |
379 | |
380 } | |
381 else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eControlChange ){ | |
382 | |
383 switch ( m.getData_1() ){ //controller number | |
384 case 1: { // selection size | |
385 const size_t midiVal = m.getData_2(); | |
386 size_t numSelectionChunks = ci::lmap<size_t>( midiVal, 0, 127, 1, mConfig.getMaxSelectionNumChunks() ); | |
387 | |
388 mWaves[waveIdx]->getSelection().setSize( numSelectionChunks ); | |
389 | |
390 // how many samples in one selection ? | |
391 size_t selectionSize = mWaves[waveIdx]->getSelection().getSize() * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); | |
392 mAudioEngine.setSelectionSize( waveIdx, selectionSize ); | |
393 | |
394 }; | |
395 break; | |
396 | |
397 case 4: { // loop on off | |
398 unsigned char midiVal = m.getData_2(); | |
399 | |
400 if ( midiVal > 0 ) | |
401 mAudioEngine.loopOn( waveIdx ); | |
402 else | |
403 mAudioEngine.loopOff( waveIdx ); | |
404 }; | |
405 break; | |
406 | |
407 case 5: // trigger record | |
408 mAudioEngine.record( waveIdx ); | |
409 break; | |
410 | |
411 case 2: { // duration | |
412 const double midiVal = m.getData_2(); // 0-127 | |
413 const double coeff = ci::lmap<double>( midiVal, 0.0, 127, 1.0, mConfig.getMaxGrainDurationCoeff() ); | |
414 mAudioEngine.setGrainDurationCoeff( waveIdx, coeff ); | |
415 mWaves[waveIdx]->getSelection().setParticleSpread( float( coeff ) ); | |
416 }; | |
417 break; | |
418 | |
419 case 7: { // filter | |
420 const double midiVal = m.getData_2(); // 0-127 | |
421 const double minCutoff = mConfig.getMinFilterCutoffFreq(); | |
422 const double maxCutoff = mConfig.getMaxFilterCutoffFreq(); | |
423 const double cutoff = pow( maxCutoff / 200., midiVal / 127.0 ) * minCutoff; | |
424 mAudioEngine.setFilterCutoff( waveIdx, cutoff ); | |
425 const float alpha = ci::lmap<double>( midiVal, 0.0f, 127.0f, 0.f, 1.f ); | |
426 mWaves[waveIdx]->setselectionAlpha( alpha ); | |
427 }; | |
428 break; | |
429 | |
430 | |
431 | |
432 } | |
433 } | |
434 } | |
435 | |
436 midiMessages.clear(); | |
437 } | |
438 | |
439 | |
440 | |
441 CollidoscopeApp::~CollidoscopeApp() | |
442 { | |
443 for ( int chan = 0; chan < NUM_WAVES; chan++ ){ | |
444 /* delete the array for wave messages from audio thread */ | |
445 delete[] mRecordWaveMessageBuffers[chan]; | |
446 } | |
447 } | |
448 | |
449 CINDER_APP( CollidoscopeApp, RendererGl, [] ( App::Settings *settings) { | |
450 settings->setWindowSize( 1920, 1080 ); | |
451 settings->setMultiTouchEnabled( false ); | |
452 settings->disableFrameRate(); | |
453 | |
454 } ) |