To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / CollidoscopeApp / src / CollidoscopeApp.cpp @ 0:02467299402e
History | View | Annotate | Download (13.7 KB)
| 1 | 0:02467299402e | f | #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 | } ) |