annotate CollidoscopeApp/src/PGranularNode.cpp @ 18:f1ff1a81be20 tip

Changed licenses names. Fixed one comment and usage text in CollidoscopeApp.cpp.
author Fiore Martin <f.martin@qmul.ac.uk>
date Thu, 25 Aug 2016 12:07:50 +0200
parents 4dad0b810f18
children
rev   line source
f@5 1 /*
f@5 2
f@5 3 Copyright (C) 2016 Queen Mary University of London
f@5 4 Author: Fiore Martin
f@5 5
f@5 6 This file is part of Collidoscope.
f@5 7
f@5 8 Collidoscope is free software: you can redistribute it and/or modify
f@5 9 it under the terms of the GNU General Public License as published by
f@5 10 the Free Software Foundation, either version 3 of the License, or
f@5 11 (at your option) any later version.
f@5 12
f@5 13 This program is distributed in the hope that it will be useful,
f@5 14 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@5 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@5 16 GNU General Public License for more details.
f@5 17
f@5 18 You should have received a copy of the GNU General Public License
f@5 19 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@5 20 */
f@5 21
f@0 22 #include "PGranularNode.h"
f@0 23
f@0 24 #include "cinder/audio/Context.h"
f@0 25
f@0 26 #include "cinder/Rand.h"
f@0 27
f@0 28 // generate random numbers from 0 to max
f@0 29 // it's passed to PGranular to randomize the phase offset at grain creation
f@0 30 struct RandomGenerator
f@0 31 {
f@0 32
f@0 33 RandomGenerator( size_t max ) : mMax( max )
f@0 34 {}
f@0 35
f@0 36 size_t operator()() const {
f@0 37 return ci::Rand::randUint( mMax );
f@0 38 }
f@0 39
f@0 40 size_t mMax;
f@0 41 };
f@0 42 // FIXME maybe use only one random gen
f@0 43
f@0 44 PGranularNode::PGranularNode( ci::audio::Buffer *grainBuffer, CursorTriggerMsgRingBuffer &triggerRingBuffer ) :
f@0 45 Node( Format().channels( 1 ) ),
f@0 46 mGrainBuffer(grainBuffer),
f@0 47 mSelectionStart( 0 ),
f@0 48 mSelectionSize( 0 ),
f@0 49 mGrainDurationCoeff( 1 ),
f@0 50 mTriggerRingBuffer( triggerRingBuffer ),
f@0 51 mNoteMsgRingBufferPack( 128 )
f@0 52 {
f@0 53 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 54 mMidiNotes[i] = kNoMidiNote;
f@0 55
f@0 56 }
f@0 57 }
f@0 58
f@0 59
f@0 60 PGranularNode::~PGranularNode()
f@0 61 {
f@0 62 }
f@0 63
f@0 64 void PGranularNode::initialize()
f@0 65 {
f@0 66 mTempBuffer = std::make_shared< ci::audio::Buffer >( getFramesPerBlock() );
f@0 67
f@16 68 mRandomOffset.reset( new RandomGenerator( getSampleRate() / 100 ) ); // divided by 100 corresponds to multiplied by 0.01 in the time domain
f@0 69
f@0 70 /* create the PGranular object for looping */
f@0 71 mPGranularLoop.reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, -1 ) );
f@0 72
f@0 73 /* create the PGranular object for notes */
f@0 74 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 75 mPGranularNotes[i].reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, i ) );
f@0 76 }
f@0 77
f@0 78 }
f@0 79
f@0 80 void PGranularNode::process (ci::audio::Buffer *buffer )
f@0 81 {
f@0 82 // only update PGranular if the atomic value has changed from the previous time
f@0 83 const boost::optional<size_t> selectionSize = mSelectionSize.get();
f@0 84 if ( selectionSize ){
f@0 85 mPGranularLoop->setSelectionSize( *selectionSize );
f@0 86 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 87 mPGranularNotes[i]->setSelectionSize( *selectionSize );
f@0 88 }
f@0 89 }
f@0 90
f@0 91 const boost::optional<size_t> selectionStart = mSelectionStart.get();
f@0 92 if ( selectionStart ){
f@0 93 mPGranularLoop->setSelectionStart( *selectionStart );
f@0 94 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 95 mPGranularNotes[i]->setSelectionStart( *selectionStart );
f@0 96 }
f@0 97 }
f@0 98
f@0 99 const boost::optional<double> grainDurationCoeff = mGrainDurationCoeff.get();
f@0 100 if ( grainDurationCoeff ){
f@0 101 mPGranularLoop->setGrainsDurationCoeff( *grainDurationCoeff );
f@0 102 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 103 mPGranularNotes[i]->setGrainsDurationCoeff( *grainDurationCoeff );
f@0 104 }
f@0 105 }
f@0 106
f@0 107 // check messages to start/stop notes or loop
f@0 108 size_t availableRead = mNoteMsgRingBufferPack.getBuffer().getAvailableRead();
f@0 109 mNoteMsgRingBufferPack.getBuffer().read( mNoteMsgRingBufferPack.getExchangeArray(), availableRead );
f@0 110 for ( size_t i = 0; i < availableRead; i++ ){
f@0 111 handleNoteMsg( mNoteMsgRingBufferPack.getExchangeArray()[i] );
f@0 112 }
f@0 113
f@0 114 // process loop if not idle
f@0 115 if ( !mPGranularLoop->isIdle() ){
f@0 116 /* buffer is one channel only so I can use getData */
f@0 117 mPGranularLoop->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
f@0 118 }
f@0 119
f@0 120 // process notes if not idle
f@0 121 for ( size_t i = 0; i < kMaxVoices; i++ ){
f@0 122 if ( mPGranularNotes[i]->isIdle() )
f@0 123 continue;
f@0 124
f@0 125 mPGranularNotes[i]->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
f@0 126
f@0 127 if ( mPGranularNotes[i]->isIdle() ){
f@0 128 // this note became idle so update mMidiNotes
f@0 129 mMidiNotes[i] = kNoMidiNote;
f@0 130 }
f@0 131
f@0 132 }
f@0 133 }
f@0 134
f@16 135 // Called back when new PGranular is triggered or turned off. Sends notification message to graphic thread.
f@0 136 void PGranularNode::operator()( char msgType, int ID ) {
f@0 137
f@0 138 switch ( msgType ){
f@0 139 case 't': { // trigger
f@0 140 CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_UPDATE, ID ); // put ID
f@0 141 mTriggerRingBuffer.write( &msg, 1 );
f@0 142 };
f@0 143 break;
f@0 144
f@0 145 case 'e': // end envelope
f@0 146 CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_END, ID ); // put ID
f@0 147 mTriggerRingBuffer.write( &msg, 1 );
f@0 148 break;
f@0 149 }
f@0 150
f@0 151
f@0 152 }
f@0 153
f@0 154 void PGranularNode::handleNoteMsg( const NoteMsg &msg )
f@0 155 {
f@0 156 switch ( msg.cmd ){
f@0 157 case Command::NOTE_ON: {
f@0 158 bool synthFound = false;
f@0 159
f@0 160 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 161 // note was already on, so re-attack
f@0 162 if ( mMidiNotes[i] == msg.midiNote ){
f@0 163 mPGranularNotes[i]->noteOn( msg.rate );
f@0 164 synthFound = true;
f@0 165 break;
f@0 166 }
f@0 167 }
f@0 168
f@0 169 if ( !synthFound ){
f@16 170 // then look for a free voice
f@0 171 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 172
f@0 173 if ( mMidiNotes[i] == kNoMidiNote ){
f@0 174 mPGranularNotes[i]->noteOn( msg.rate );
f@0 175 mMidiNotes[i] = msg.midiNote;
f@0 176 synthFound = true;
f@0 177 break;
f@0 178 }
f@0 179 }
f@0 180 }
f@0 181 };
f@0 182 break;
f@0 183
f@0 184 case Command::NOTE_OFF: {
f@0 185 for ( int i = 0; i < kMaxVoices; i++ ){
f@0 186 if ( !mPGranularNotes[i]->isIdle() && mMidiNotes[i] == msg.midiNote ){
f@0 187 mPGranularNotes[i]->noteOff();
f@0 188 break;
f@0 189 }
f@0 190 }
f@0 191 };
f@0 192 break;
f@0 193
f@0 194 case Command::LOOP_ON: {
f@0 195 mPGranularLoop->noteOn( 1.0 );
f@0 196 };
f@0 197 break;
f@0 198
f@0 199 case Command::LOOP_OFF: {
f@0 200 mPGranularLoop->noteOff();
f@0 201 };
f@0 202 break;
f@0 203 default:
f@0 204 break;
f@0 205 }
f@0 206 }