To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / CollidoscopeApp / src / AudioEngine.cpp @ 16:4dad0b810f18

History | View | Annotate | Download (7.55 KB)

1 5:75b744078d66 f
/*
2

3
 Copyright (C) 2016  Queen Mary University of London
4
 Author: Fiore Martin
5

6
 This file is part of Collidoscope.
7

8
 Collidoscope is free software: you can redistribute it and/or modify
9
 it under the terms of the GNU General Public License as published by
10
 the Free Software Foundation, either version 3 of the License, or
11
 (at your option) any later version.
12

13
 This program is distributed in the hope that it will be useful,
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 GNU General Public License for more details.
17

18
 You should have received a copy of the GNU General Public License
19
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20

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