Mercurial > hg > opencollidoscope
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CollidoscopeApp/src/CollidoscopeApp.cpp Thu Jun 30 14:50:06 2016 +0200 @@ -0,0 +1,454 @@ +#include "cinder/app/App.h" +#include "cinder/app/RendererGl.h" +#include "cinder/gl/gl.h" +#include "cinder/Exception.h" + + +#include "Config.h" +#include "Wave.h" +#include "DrawInfo.h" +#include "Log.h" +#include "AudioEngine.h" +#include "Oscilloscope.h" +#include "Messages.h" +#include "MIDI.h" + +using namespace ci; +using namespace ci::app; + +using namespace std; + + +class CollidoscopeApp : public App { + public: + + void setup() override; + void setupGraphics(); + + void receiveCommands(); + + void keyDown( KeyEvent event ) override; + void update() override; + void draw() override; + void resize() override; + + Config mConfig; + collidoscope::MIDI mMIDI; + AudioEngine mAudioEngine; + + array< shared_ptr< Wave >, NUM_WAVES > mWaves; + array< shared_ptr< DrawInfo >, NUM_WAVES > mDrawInfos; + array< shared_ptr< Oscilloscope >, NUM_WAVES > mOscilloscopes; + // buffers to read the wave messages as a new wave gets recorded + array< RecordWaveMsg*, NUM_WAVES> mRecordWaveMessageBuffers; + array< vector< CursorTriggerMsg >, NUM_WAVES > mCursorTriggerMessagesBuffers; + + double mSecondsPerChunk; + + ~CollidoscopeApp(); + +}; + + +void CollidoscopeApp::setup() +{ + hideCursor(); + /* setup is logged: setup steps and errors */ + + /*try { + mConfig.loadFromFile( "./collidoscope_config.xml" ); + } + catch ( const Exception &e ){ + logError( string("Exception loading config from file:") + e.what() ); + }*/ + + // setup buffers to read messages from audio thread + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + mRecordWaveMessageBuffers[i] = new RecordWaveMsg[mConfig.getNumChunks()]; + mCursorTriggerMessagesBuffers[i].reserve( mConfig.getCursorTriggerMessageBufSize() ); + } + + mAudioEngine.setup( mConfig ); + + setupGraphics(); + + mSecondsPerChunk = mConfig.getWaveLen() / mConfig.getNumChunks(); + + try { + mMIDI.setup( mConfig ); + } + catch ( const collidoscope::MIDIException &e ){ + logError( string( "Exception opening MIDI input device: " ) + e.getMessage() ); + } + +} + +void CollidoscopeApp::setupGraphics() +{ + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + + mDrawInfos[i] = make_shared< DrawInfo >( i ); + mWaves[i] = make_shared< Wave >(mConfig.getNumChunks(), mConfig.getWaveSelectionColor(i) ); + mOscilloscopes[i] = make_shared< Oscilloscope >( mAudioEngine.getAudioOutputBuffer( i ).getNumFrames() / mConfig.getOscilloscopeNumPointsDivider() ); + + } +} + +void CollidoscopeApp::keyDown( KeyEvent event ) +{ + char c = event.getChar(); + + switch (c){ + case 'r' : + mAudioEngine.record( 0 ); + mAudioEngine.record( 1 ); + break; + + case 'w': { + mWaves[0]->getSelection().setSize(mWaves[0]->getSelection().getSize() + 1); + + size_t numSelectionChunks = mWaves[0]->getSelection().getSize(); + // how many samples in one selection ? + size_t selectionSize = numSelectionChunks * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); + + mAudioEngine.setSelectionSize(0, selectionSize); + }; + break; + + case 'e': { + mWaves[1]->getSelection().setSize(mWaves[1]->getSelection().getSize() + 1); + + size_t numSelectionChunks = mWaves[1]->getSelection().getSize(); + // how many samples in one selection ? + size_t selectionSize = numSelectionChunks * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); + + mAudioEngine.setSelectionSize(1, selectionSize); + }; + break; + + case 's': { + + mWaves[0]->getSelection().setSize( mWaves[0]->getSelection().getSize() - 1 ); + + size_t selectionSize = mWaves[0]->getSelection().getSize() *(mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); + mAudioEngine.setSelectionSize( 0, selectionSize ); + }; + break; + + case 'd': { + + for( size_t waveIdx = 0; waveIdx < NUM_WAVES; waveIdx++){ + size_t selectionStart = mWaves[waveIdx]->getSelection().getStart(); + mWaves[waveIdx]->getSelection().setStart( selectionStart + 1 ); + + selectionStart = mWaves[waveIdx]->getSelection().getStart(); + mAudioEngine.setSelectionStart( waveIdx, selectionStart * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); + } + }; + + break; + + case 'a': { + size_t selectionStart = mWaves[0]->getSelection().getStart(); + + if ( selectionStart == 0 ) + return; + + mWaves[0]->getSelection().setStart( selectionStart - 1 ); + + selectionStart = mWaves[0]->getSelection().getStart(); + + mAudioEngine.setSelectionStart( 0, selectionStart * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); + }; + break; + + + case 'p': + + mWaves[0]->setCursorPos( 4, mWaves[0]->getSelection().getStart(), *mDrawInfos[0] ) ; + break; + + case 'f': + setFullScreen( !isFullScreen() ); + break; + + case ' ': { + static bool isOn = false; + isOn = !isOn; + if ( isOn ){ + mAudioEngine.loopOn( 0 ); + mAudioEngine.loopOn( 1 ); + } + else{ + mAudioEngine.loopOff( 0 ); + mAudioEngine.loopOff( 1 ); + } + }; + break; + + case 'm' : + mAudioEngine.setGrainDurationCoeff(0, 8); + break; + + case 'n': { + mAudioEngine.setGrainDurationCoeff( 0, 1 ); + }; + break; + + case '9': { + int c = mWaves[0]->getSelection().getParticleSpread(); + if ( c == 1 ) + return; + else + c -= 1; + + mAudioEngine.setGrainDurationCoeff( 0, c ); + mWaves[0]->getSelection().setParticleSpread( float( c ) ); + mAudioEngine.setGrainDurationCoeff( 1, c ); + mWaves[1]->getSelection().setParticleSpread( float( c ) ); + }; break; + + case '0': { + int c = mWaves[0]->getSelection().getParticleSpread(); + if ( c == 8 ) + return; + else + c += 1; + + mAudioEngine.setGrainDurationCoeff( 0, c ); + mWaves[0]->getSelection().setParticleSpread( float( c ) ); + }; break; + + } + +} + +void CollidoscopeApp::update() +{ + // check incoming commands + receiveCommands(); + + // check new wave chunks from recorder buffer + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + size_t availableRead = mAudioEngine.getRecordWaveAvailable( i ); + mAudioEngine.readRecordWave( i, mRecordWaveMessageBuffers[i], availableRead ); + + for ( size_t msgIndex = 0; msgIndex < availableRead; msgIndex++ ){ + const RecordWaveMsg & msg = mRecordWaveMessageBuffers[i][msgIndex]; + + if ( msg.cmd == Command::WAVE_CHUNK ){ + mWaves[i]->setChunk( msg.index, msg.arg1, msg.arg2 ); + } + else if ( msg.cmd == Command::WAVE_START ){ + mWaves[i]->reset( true ); // reset only chunks but leave selection + } + + } + } + + // check if new cursors have been triggered + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + + mAudioEngine.checkCursorTriggers( i, mCursorTriggerMessagesBuffers[i] ); + for ( auto & trigger : mCursorTriggerMessagesBuffers[i] ){ + const int nodeID = trigger.synthID; + + switch ( trigger.cmd ){ + + case Command::TRIGGER_UPDATE: { + mWaves[i]->setCursorPos( nodeID, mWaves[i]->getSelection().getStart(), *mDrawInfos[i] ); + }; + break; + + case Command::TRIGGER_END: { + mWaves[i]->removeCursor( nodeID ); + }; + break; + + } + + } + mCursorTriggerMessagesBuffers[i].clear(); + } + + // update cursors + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + mWaves[i]->update( mSecondsPerChunk, *mDrawInfos[i] ); + } + + // update oscilloscope + + for ( size_t i = 0; i < NUM_WAVES; i++ ){ + const audio::Buffer &audioOutBuffer = mAudioEngine.getAudioOutputBuffer( i ); + // one oscilloscope sample + + for ( size_t j = 0; j < mOscilloscopes[i]->getNumPoints(); j++ ){ + mOscilloscopes[i]->setPoint( j, audioOutBuffer.getData()[j], *mDrawInfos[i] ); + } + } + + + +} + +void CollidoscopeApp::draw() +{ + gl::clear( Color( 0, 0, 0 ) ); + + for ( int i = 0; i < NUM_WAVES; i++ ){ + if ( i == 1 ){ + /* for the upper wave flip the x over the center of the screen which is + the composition of rotate on the y-axis and translate by -screenwidth*/ + gl::pushModelMatrix(); + gl::rotate( float(M_PI), ci::vec3( 0, 1, 0 ) ); + gl::translate( float( -getWindowWidth() ), 0.0f ); + mOscilloscopes[i]->draw(); + mWaves[i]->draw( *mDrawInfos[i] ); + gl::popModelMatrix(); + } + else{ + + mOscilloscopes[i]->draw(); + mWaves[i]->draw( *mDrawInfos[i] ); + } + } +} + +void CollidoscopeApp::resize() +{ + App::resize(); + + for ( int i = 0; i < NUM_WAVES; i++ ){ + // reset the drawing information with the new windows size and same shrink factor + mDrawInfos[i]->reset( getWindow()->getBounds(), 3.0f / 5.0f ); + + /* reset the oscilloscope points to zero */ + for ( int j = 0; j < mOscilloscopes[i]->getNumPoints(); j++ ){ + mOscilloscopes[i]->setPoint(j, 0.0f, *mDrawInfos[i] ); + } + } +} + + + +void CollidoscopeApp::receiveCommands() +{ + // check new midi messages + static std::vector<collidoscope::MIDIMessage> midiMessages; + mMIDI.checkMessages( midiMessages ); + + + for ( auto &m : midiMessages ){ + + const size_t waveIdx = mConfig.getWaveForMIDIChannel( m.getChannel() ); + if ( waveIdx >= NUM_WAVES ) + continue; + + if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eNoteOn ){ + int midiNote = m.getData_1(); + mAudioEngine.noteOn( waveIdx, midiNote ); + } + else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eNoteOff ){ + int midiNote = m.getData_1(); + mAudioEngine.noteOff( waveIdx, midiNote ); + } + else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::ePitchBend ){ + const uint16_t MSB = m.getData_2() << 7; + uint16_t value = m.getData_1(); // LSB + + value |= MSB; + + + // value ranges from 0 to 1050. check boundaries in case sensor gives bad values + if ( value > 149 ){ // FIXME pareametrizer + continue; + } + + size_t startChunk = value; + + const size_t selectionSizeBeforeStartUpdate = mWaves[waveIdx]->getSelection().getSize(); + mWaves[waveIdx]->getSelection().setStart( startChunk ); + + mAudioEngine.setSelectionStart( waveIdx, startChunk * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); + + const size_t newSelectionSize = mWaves[waveIdx]->getSelection().getSize(); + if ( selectionSizeBeforeStartUpdate != newSelectionSize ){ + mAudioEngine.setSelectionSize( waveIdx, newSelectionSize * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()) ); + } + + + } + else if ( m.getVoice() == collidoscope::MIDIMessage::Voice::eControlChange ){ + + switch ( m.getData_1() ){ //controller number + case 1: { // selection size + const size_t midiVal = m.getData_2(); + size_t numSelectionChunks = ci::lmap<size_t>( midiVal, 0, 127, 1, mConfig.getMaxSelectionNumChunks() ); + + mWaves[waveIdx]->getSelection().setSize( numSelectionChunks ); + + // how many samples in one selection ? + size_t selectionSize = mWaves[waveIdx]->getSelection().getSize() * (mConfig.getWaveLen() * mAudioEngine.getSampleRate() / mConfig.getNumChunks()); + mAudioEngine.setSelectionSize( waveIdx, selectionSize ); + + }; + break; + + case 4: { // loop on off + unsigned char midiVal = m.getData_2(); + + if ( midiVal > 0 ) + mAudioEngine.loopOn( waveIdx ); + else + mAudioEngine.loopOff( waveIdx ); + }; + break; + + case 5: // trigger record + mAudioEngine.record( waveIdx ); + break; + + case 2: { // duration + const double midiVal = m.getData_2(); // 0-127 + const double coeff = ci::lmap<double>( midiVal, 0.0, 127, 1.0, mConfig.getMaxGrainDurationCoeff() ); + mAudioEngine.setGrainDurationCoeff( waveIdx, coeff ); + mWaves[waveIdx]->getSelection().setParticleSpread( float( coeff ) ); + }; + break; + + case 7: { // filter + const double midiVal = m.getData_2(); // 0-127 + const double minCutoff = mConfig.getMinFilterCutoffFreq(); + const double maxCutoff = mConfig.getMaxFilterCutoffFreq(); + const double cutoff = pow( maxCutoff / 200., midiVal / 127.0 ) * minCutoff; + mAudioEngine.setFilterCutoff( waveIdx, cutoff ); + const float alpha = ci::lmap<double>( midiVal, 0.0f, 127.0f, 0.f, 1.f ); + mWaves[waveIdx]->setselectionAlpha( alpha ); + }; + break; + + + + } + } + } + + midiMessages.clear(); +} + + + +CollidoscopeApp::~CollidoscopeApp() +{ + for ( int chan = 0; chan < NUM_WAVES; chan++ ){ + /* delete the array for wave messages from audio thread */ + delete[] mRecordWaveMessageBuffers[chan]; + } +} + +CINDER_APP( CollidoscopeApp, RendererGl, [] ( App::Settings *settings) { + settings->setWindowSize( 1920, 1080 ); + settings->setMultiTouchEnabled( false ); + settings->disableFrameRate(); + +} )