Mercurial > hg > opencollidoscope
comparison CollidoscopeApp/src/AudioEngine.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 | dd889fff8423 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:02467299402e |
---|---|
1 #include "AudioEngine.h" | |
2 #include "cinder/app/App.h" | |
3 //FIXME remove App.h include | |
4 #include "Log.h" | |
5 | |
6 using namespace ci::audio; | |
7 | |
8 double chromaticRatios[] = { | |
9 1, | |
10 1.0594630943591, | |
11 1.1224620483089, | |
12 1.1892071150019, | |
13 1.2599210498937, | |
14 1.3348398541685, | |
15 1.4142135623711, | |
16 1.4983070768743, | |
17 1.5874010519653, | |
18 1.6817928305039, | |
19 1.7817974362766, | |
20 1.8877486253586 | |
21 }; | |
22 | |
23 inline double calculateMidiNoteRatio( int midiNote ) | |
24 { | |
25 int distanceFromCenter = midiNote - 60; // 60 is the central midi note | |
26 | |
27 if ( distanceFromCenter < 0 ){ | |
28 int diffAmount = -distanceFromCenter; | |
29 int octaves = diffAmount / 12; | |
30 int intervals = diffAmount % 12; | |
31 | |
32 return std::pow( 0.5, octaves ) / chromaticRatios[intervals]; | |
33 } | |
34 else{ | |
35 int octaves = distanceFromCenter / 12; | |
36 int intervals = distanceFromCenter % 12; | |
37 | |
38 return std::pow( 2, octaves ) * chromaticRatios[intervals]; | |
39 } | |
40 } | |
41 | |
42 | |
43 AudioEngine::AudioEngine() | |
44 {} | |
45 | |
46 AudioEngine::~AudioEngine() | |
47 {} | |
48 | |
49 void AudioEngine::setup(const Config& config) | |
50 { | |
51 | |
52 for ( int i = 0; i < NUM_WAVES; i++ ){ | |
53 mCursorTriggerRingBufferPacks[i].reset( new RingBufferPack<CursorTriggerMsg>( 512 ) ); // FIXME | |
54 } | |
55 | |
56 /* audio context */ | |
57 auto ctx = Context::master(); | |
58 | |
59 /* audio inpu device */ | |
60 auto inputDeviceNode = ctx->createInputDeviceNode( Device::getDefaultInput() ); | |
61 | |
62 | |
63 /* route the audio input, which is two channels, to one wave graph for each channel */ | |
64 for ( int chan = 0; chan < NUM_WAVES; chan++ ){ | |
65 | |
66 /* one channel router */ | |
67 mInputRouterNodes[chan] = ctx->makeNode( new ChannelRouterNode( Node::Format().channels( 1 ) ) ); | |
68 | |
69 /* buffer recorders */ | |
70 mBufferRecorderNodes[chan] = ctx->makeNode( new BufferToWaveRecorderNode( config.getNumChunks(), config.getWaveLen() ) ); | |
71 /* this prevents the node from recording before record is pressed */ | |
72 mBufferRecorderNodes[chan]->setAutoEnabled( false ); | |
73 | |
74 // route the input part of the audio graph. Two channels input goes into | |
75 // one channel route and to one channel buffer recorder | |
76 inputDeviceNode >> mInputRouterNodes[chan]->route( chan, 0, 1 ) >> mBufferRecorderNodes[chan]; | |
77 | |
78 | |
79 // create PGranular loops passing the buffer of the RecorderNode as argument to the contructor | |
80 // use -1 as ID | |
81 mPGranularNodes[chan] = ctx->makeNode( new PGranularNode( mBufferRecorderNodes[chan]->getRecorderBuffer(), mCursorTriggerRingBufferPacks[chan]->getBuffer() ) ); | |
82 | |
83 // create filter nodes | |
84 mLowPassFilterNodes[chan] = ctx->makeNode( new FilterLowPassNode( MonitorNode::Format().channels( 1 ) ) ); | |
85 mLowPassFilterNodes[chan]->setCutoffFreq( config.getMaxFilterCutoffFreq() ); | |
86 mLowPassFilterNodes[chan]->setQ( 0.707f ); | |
87 // create monitor nodes for oscilloscopes | |
88 mOutputMonitorNodes[chan] = ctx->makeNode( new MonitorNode( MonitorNode::Format().channels( 1 ) ) ); | |
89 | |
90 // all output goes to the filter | |
91 mPGranularNodes[chan] >> mLowPassFilterNodes[chan]; | |
92 | |
93 mOutputRouterNodes[chan] = ctx->makeNode( new ChannelRouterNode( Node::Format().channels( 2 ) ) ); | |
94 | |
95 // filter goes to output | |
96 mLowPassFilterNodes[chan] >> mOutputRouterNodes[chan]->route( 0, chan, 1 ) >> ctx->getOutput(); | |
97 | |
98 // what goes to output goes to scope | |
99 mLowPassFilterNodes[chan] >> mOutputMonitorNodes[chan]; | |
100 | |
101 } | |
102 | |
103 ctx->getOutput()->enableClipDetection( false ); | |
104 /* enable the whole audio graph */ | |
105 inputDeviceNode->enable(); | |
106 ctx->enable(); | |
107 } | |
108 | |
109 size_t AudioEngine::getSampleRate() | |
110 { | |
111 return Context::master()->getSampleRate(); | |
112 } | |
113 | |
114 void AudioEngine::loopOn( size_t waveIdx ) | |
115 { | |
116 NoteMsg msg = makeNoteMsg( Command::LOOP_ON, 1, 1.0 ); | |
117 mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 ); | |
118 } | |
119 | |
120 void AudioEngine::loopOff( size_t waveIdx ) | |
121 { | |
122 NoteMsg msg = makeNoteMsg( Command::LOOP_OFF, 0, 0.0 ); | |
123 mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 ); | |
124 } | |
125 | |
126 void AudioEngine::record( size_t waveIdx ) | |
127 { | |
128 mBufferRecorderNodes[waveIdx]->start(); | |
129 } | |
130 | |
131 void AudioEngine::noteOn( size_t waveIdx, int midiNote ) | |
132 { | |
133 | |
134 double midiAsRate = calculateMidiNoteRatio(midiNote); | |
135 NoteMsg msg = makeNoteMsg( Command::NOTE_ON, midiNote, midiAsRate ); | |
136 | |
137 mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 ); | |
138 } | |
139 | |
140 void AudioEngine::noteOff( size_t waveIdx, int midiNote ) | |
141 { | |
142 NoteMsg msg = makeNoteMsg( Command::NOTE_OFF, midiNote, 0.0 ); | |
143 mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 ); | |
144 } | |
145 | |
146 | |
147 | |
148 void AudioEngine::setSelectionSize( size_t waveIdx, size_t size ) | |
149 { | |
150 mPGranularNodes[waveIdx]->setSelectionSize( size ); | |
151 } | |
152 | |
153 void AudioEngine::setSelectionStart( size_t waveIdx, size_t start ) | |
154 { | |
155 mPGranularNodes[waveIdx]->setSelectionStart( start ); | |
156 } | |
157 | |
158 void AudioEngine::setGrainDurationCoeff( size_t waveIdx, double coeff ) | |
159 { | |
160 mPGranularNodes[waveIdx]->setGrainsDurationCoeff( coeff ); | |
161 } | |
162 | |
163 void AudioEngine::setFilterCutoff( size_t waveIdx, double cutoff ) | |
164 { | |
165 mLowPassFilterNodes[waveIdx]->setCutoffFreq( cutoff ); | |
166 } | |
167 | |
168 // ------------------------------------------------------ | |
169 // ----- methods for communication with main thread ----- | |
170 // ------------------------------------------------------ | |
171 | |
172 size_t AudioEngine::getRecordWaveAvailable( size_t waveIdx ) | |
173 { | |
174 return mBufferRecorderNodes[waveIdx]->getRingBuffer().getAvailableRead(); | |
175 } | |
176 | |
177 bool AudioEngine::readRecordWave( size_t waveIdx, RecordWaveMsg* buffer, size_t count ) | |
178 { | |
179 return mBufferRecorderNodes[waveIdx]->getRingBuffer().read( buffer, count ); | |
180 } | |
181 | |
182 void AudioEngine::checkCursorTriggers( size_t waveIdx, std::vector<CursorTriggerMsg>& cursorTriggers ) | |
183 { | |
184 ci::audio::dsp::RingBufferT<CursorTriggerMsg> &ringBuffer = mCursorTriggerRingBufferPacks[waveIdx]->getBuffer(); | |
185 CursorTriggerMsg* ringBufferReadArray = mCursorTriggerRingBufferPacks[waveIdx]->getExchangeArray(); | |
186 | |
187 size_t availableRead = ringBuffer.getAvailableRead(); | |
188 bool successfulRead = ringBuffer.read( ringBufferReadArray, availableRead ); | |
189 | |
190 if ( successfulRead ){ | |
191 for ( size_t i = 0; i < availableRead; i++ ){ | |
192 cursorTriggers.push_back( ringBufferReadArray[i] ); | |
193 } | |
194 } | |
195 } | |
196 | |
197 const ci::audio::Buffer& AudioEngine::getAudioOutputBuffer( size_t waveIdx ) const | |
198 { | |
199 return mOutputMonitorNodes[waveIdx]->getBuffer(); | |
200 } | |
201 |