To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / CollidoscopeApp / src / AudioEngine.cpp @ 5:75b744078d66
History | View | Annotate | Download (7.48 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 | #include "cinder/app/App.h" |
||
| 25 | //FIXME remove App.h include
|
||
| 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 | /* audio inpu device */
|
||
| 91 | 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 | // route the input part of the audio graph. Two channels input goes into
|
||
| 106 | // one channel route and to one channel buffer recorder
|
||
| 107 | 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 | // use -1 as ID
|
||
| 112 | 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 | // what goes to output goes to scope
|
||
| 130 | 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 | } |