annotate CollidoscopeApp/src/PGranularNode.cpp @ 3:7fb593d53361

added comments
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 12 Jul 2016 18:29:38 +0200
parents 02467299402e
children 75b744078d66
rev   line source
f@0 1 #include "PGranularNode.h"
f@0 2
f@0 3 #include "cinder/audio/Context.h"
f@0 4
f@0 5 #include "cinder/Rand.h"
f@0 6
f@0 7 // generate random numbers from 0 to max
f@0 8 // it's passed to PGranular to randomize the phase offset at grain creation
f@0 9 struct RandomGenerator
f@0 10 {
f@0 11
f@0 12 RandomGenerator( size_t max ) : mMax( max )
f@0 13 {}
f@0 14
f@0 15 size_t operator()() const {
f@0 16 return ci::Rand::randUint( mMax );
f@0 17 }
f@0 18
f@0 19 size_t mMax;
f@0 20 };
f@0 21 // FIXME maybe use only one random gen
f@0 22
f@0 23 PGranularNode::PGranularNode( ci::audio::Buffer *grainBuffer, CursorTriggerMsgRingBuffer &triggerRingBuffer ) :
f@0 24 Node( Format().channels( 1 ) ),
f@0 25 mGrainBuffer(grainBuffer),
f@0 26 mSelectionStart( 0 ),
f@0 27 mSelectionSize( 0 ),
f@0 28 mGrainDurationCoeff( 1 ),
f@0 29 mTriggerRingBuffer( triggerRingBuffer ),
f@0 30 mNoteMsgRingBufferPack( 128 )
f@0 31 {
f@0 32 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 33 mMidiNotes[i] = kNoMidiNote;
f@0 34
f@0 35 }
f@0 36 }
f@0 37
f@0 38
f@0 39 PGranularNode::~PGranularNode()
f@0 40 {
f@0 41 }
f@0 42
f@0 43 void PGranularNode::initialize()
f@0 44 {
f@0 45 mTempBuffer = std::make_shared< ci::audio::Buffer >( getFramesPerBlock() );
f@0 46
f@0 47 mRandomOffset.reset( new RandomGenerator( getSampleRate() / 100 ) ); // divided by 100 corresponds to times 0.01 in the time domain
f@0 48
f@0 49 /* create the PGranular object for looping */
f@0 50 mPGranularLoop.reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, -1 ) );
f@0 51
f@0 52 /* create the PGranular object for notes */
f@0 53 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 54 mPGranularNotes[i].reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, i ) );
f@0 55 }
f@0 56
f@0 57 }
f@0 58
f@0 59 void PGranularNode::process (ci::audio::Buffer *buffer )
f@0 60 {
f@0 61 // only update PGranular if the atomic value has changed from the previous time
f@0 62 const boost::optional<size_t> selectionSize = mSelectionSize.get();
f@0 63 if ( selectionSize ){
f@0 64 mPGranularLoop->setSelectionSize( *selectionSize );
f@0 65 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 66 mPGranularNotes[i]->setSelectionSize( *selectionSize );
f@0 67 }
f@0 68 }
f@0 69
f@0 70 const boost::optional<size_t> selectionStart = mSelectionStart.get();
f@0 71 if ( selectionStart ){
f@0 72 mPGranularLoop->setSelectionStart( *selectionStart );
f@0 73 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 74 mPGranularNotes[i]->setSelectionStart( *selectionStart );
f@0 75 }
f@0 76 }
f@0 77
f@0 78 const boost::optional<double> grainDurationCoeff = mGrainDurationCoeff.get();
f@0 79 if ( grainDurationCoeff ){
f@0 80 mPGranularLoop->setGrainsDurationCoeff( *grainDurationCoeff );
f@0 81 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 82 mPGranularNotes[i]->setGrainsDurationCoeff( *grainDurationCoeff );
f@0 83 }
f@0 84 }
f@0 85
f@0 86 // check messages to start/stop notes or loop
f@0 87 size_t availableRead = mNoteMsgRingBufferPack.getBuffer().getAvailableRead();
f@0 88 mNoteMsgRingBufferPack.getBuffer().read( mNoteMsgRingBufferPack.getExchangeArray(), availableRead );
f@0 89 for ( size_t i = 0; i < availableRead; i++ ){
f@0 90 handleNoteMsg( mNoteMsgRingBufferPack.getExchangeArray()[i] );
f@0 91 }
f@0 92
f@0 93 // process loop if not idle
f@0 94 if ( !mPGranularLoop->isIdle() ){
f@0 95 /* buffer is one channel only so I can use getData */
f@0 96 mPGranularLoop->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
f@0 97 }
f@0 98
f@0 99 // process notes if not idle
f@0 100 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 101 if ( mPGranularNotes[i]->isIdle() )
f@0 102 continue;
f@0 103
f@0 104 mPGranularNotes[i]->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
f@0 105
f@0 106 if ( mPGranularNotes[i]->isIdle() ){
f@0 107 // this note became idle so update mMidiNotes
f@0 108 mMidiNotes[i] = kNoMidiNote;
f@0 109 }
f@0 110
f@0 111 }
f@0 112 }
f@0 113
f@3 114 // Called back when new grnular is triggered of turned off. Sends notification message to graphic thread.
f@0 115 void PGranularNode::operator()( char msgType, int ID ) {
f@0 116
f@0 117 switch ( msgType ){
f@0 118 case 't': { // trigger
f@0 119 CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_UPDATE, ID ); // put ID
f@0 120 mTriggerRingBuffer.write( &msg, 1 );
f@0 121 };
f@0 122 break;
f@0 123
f@0 124 case 'e': // end envelope
f@0 125 CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_END, ID ); // put ID
f@0 126 mTriggerRingBuffer.write( &msg, 1 );
f@0 127 break;
f@0 128 }
f@0 129
f@0 130
f@0 131 }
f@0 132
f@0 133 void PGranularNode::handleNoteMsg( const NoteMsg &msg )
f@0 134 {
f@0 135 switch ( msg.cmd ){
f@0 136 case Command::NOTE_ON: {
f@0 137 bool synthFound = false;
f@0 138
f@0 139 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 140 // note was already on, so re-attack
f@0 141 if ( mMidiNotes[i] == msg.midiNote ){
f@0 142 mPGranularNotes[i]->noteOn( msg.rate );
f@0 143 synthFound = true;
f@0 144 break;
f@0 145 }
f@0 146 }
f@0 147
f@0 148 if ( !synthFound ){
f@0 149 // then look for a free synth
f@0 150 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 151
f@0 152 if ( mMidiNotes[i] == kNoMidiNote ){
f@0 153 mPGranularNotes[i]->noteOn( msg.rate );
f@0 154 mMidiNotes[i] = msg.midiNote;
f@0 155 synthFound = true;
f@0 156 break;
f@0 157 }
f@0 158 }
f@0 159 }
f@0 160 };
f@0 161 break;
f@0 162
f@0 163 case Command::NOTE_OFF: {
f@0 164 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 165 if ( !mPGranularNotes[i]->isIdle() && mMidiNotes[i] == msg.midiNote ){
f@0 166 mPGranularNotes[i]->noteOff();
f@0 167 break;
f@0 168 }
f@0 169 }
f@0 170 };
f@0 171 break;
f@0 172
f@0 173 case Command::LOOP_ON: {
f@0 174 mPGranularLoop->noteOn( 1.0 );
f@0 175 };
f@0 176 break;
f@0 177
f@0 178 case Command::LOOP_OFF: {
f@0 179 mPGranularLoop->noteOff();
f@0 180 };
f@0 181 break;
f@0 182 default:
f@0 183 break;
f@0 184 }
f@0 185 }