To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / CollidoscopeApp / src / CollidoscopeApp.cpp @ 5:75b744078d66

History | View | Annotate | Download (14.5 KB)

1 5:75b744078d66 f
/*
2

3
 Copyright (C) 2016  Queen Mary University of London
4
 Author: Fiore Martin
5

6
 This file is part of Collidoscope.
7

8
 Collidoscope is free software: you can redistribute it and/or modify
9
 it under the terms of the GNU General Public License as published by
10
 the Free Software Foundation, either version 3 of the License, or
11
 (at your option) any later version.
12

13
 This program is distributed in the hope that it will be useful,
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 GNU General Public License for more details.
17

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