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