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 @ 2:dd889fff8423

History | View | Annotate | Download (13.7 KB)

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