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 @ 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
} )