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