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 @ 3:7fb593d53361

History | View | Annotate | Download (6.75 KB)

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
/* Frequency ratios in the chromatic scale */
9
double chromaticRatios[] = { 
10
    1, 
11
    1.0594630943591, 
12
    1.1224620483089, 
13
    1.1892071150019, 
14
    1.2599210498937, 
15
    1.3348398541685, 
16
    1.4142135623711, 
17
    1.4983070768743, 
18
    1.5874010519653, 
19
    1.6817928305039, 
20
    1.7817974362766, 
21
    1.8877486253586 
22
};
23

    
24

    
25
/*
26
 * Calculates the ratio between the frequency of the midi note passed as argument and middle C note ( MIDI value = 60 ).
27
 * This is used for pitch shifting the granular synth output, according to the key pressed by the user.
28
 * The middle C is taken as reference in pitch in the pitch shifting of Collidoscope output.
29
 * That is, with the middle C the output is not pitch shifted at all and is equal in frequency to the recorder sample.
30
 *
31
 */ 
32
inline double calculateMidiNoteRatio( int midiNote )
33
{
34
    int distanceFromCenter = midiNote - 60; // 60 is the central midi note 
35

    
36
    if ( distanceFromCenter < 0 ){
37
        int diffAmount = -distanceFromCenter;
38
        int octaves = diffAmount / 12;
39
        int intervals = diffAmount % 12;
40

    
41
        return std::pow( 0.5, octaves ) / chromaticRatios[intervals];
42
    }
43
    else{
44
        int octaves = distanceFromCenter / 12;
45
        int intervals = distanceFromCenter % 12;
46

    
47
        return std::pow( 2, octaves ) * chromaticRatios[intervals];
48
    }
49
}
50

    
51

    
52
AudioEngine::AudioEngine() 
53
{}
54

    
55
AudioEngine::~AudioEngine()
56
{}
57

    
58
void AudioEngine::setup(const Config& config)
59
{
60
    
61
    for ( int i = 0; i < NUM_WAVES; i++ ){
62
        mCursorTriggerRingBufferPacks[i].reset( new RingBufferPack<CursorTriggerMsg>( 512 ) ); // FIXME 
63
    }
64

    
65
    /* audio context */
66
    auto ctx = Context::master();
67

    
68
    /* audio inpu device */
69
    auto inputDeviceNode = ctx->createInputDeviceNode( Device::getDefaultInput() );
70
 
71

    
72
    /* route the audio input, which is two channels, to one wave graph for each channel */
73
    for ( int chan = 0; chan < NUM_WAVES; chan++ ){
74

    
75
        /* one channel router */
76
        mInputRouterNodes[chan] = ctx->makeNode( new ChannelRouterNode( Node::Format().channels( 1 ) ) );
77

    
78
        /* buffer recorders */  
79
        mBufferRecorderNodes[chan] = ctx->makeNode( new BufferToWaveRecorderNode( config.getNumChunks(), config.getWaveLen() ) );
80
        /* this prevents the node from recording before record is pressed */
81
        mBufferRecorderNodes[chan]->setAutoEnabled( false );
82

    
83
        // route the input part of the audio graph. Two channels input goes into
84
        // one channel route and to one channel buffer recorder 
85
        inputDeviceNode >> mInputRouterNodes[chan]->route( chan, 0, 1 ) >> mBufferRecorderNodes[chan];
86

    
87

    
88
        // create PGranular loops passing the buffer of the RecorderNode as argument to the contructor 
89
        // use -1 as ID 
90
        mPGranularNodes[chan] = ctx->makeNode( new PGranularNode( mBufferRecorderNodes[chan]->getRecorderBuffer(), mCursorTriggerRingBufferPacks[chan]->getBuffer() ) );
91

    
92
        // create filter nodes 
93
        mLowPassFilterNodes[chan] = ctx->makeNode( new FilterLowPassNode( MonitorNode::Format().channels( 1 ) ) );
94
        mLowPassFilterNodes[chan]->setCutoffFreq( config.getMaxFilterCutoffFreq() );
95
        mLowPassFilterNodes[chan]->setQ( 0.707f );
96
        // create monitor nodes for oscilloscopes 
97
        mOutputMonitorNodes[chan] = ctx->makeNode( new MonitorNode( MonitorNode::Format().channels( 1 ) ) );
98

    
99
        // all output goes to the filter 
100
        mPGranularNodes[chan] >> mLowPassFilterNodes[chan];
101
        
102
        mOutputRouterNodes[chan] = ctx->makeNode( new ChannelRouterNode( Node::Format().channels( 2 ) ) );
103

    
104
        // filter goes to output 
105
        mLowPassFilterNodes[chan] >> mOutputRouterNodes[chan]->route( 0, chan, 1 ) >> ctx->getOutput();
106
        
107
        // what goes to output goes to scope 
108
        mLowPassFilterNodes[chan] >> mOutputMonitorNodes[chan];
109

    
110
    }
111

    
112
    ctx->getOutput()->enableClipDetection( false );
113
    /* enable the whole audio graph */
114
    inputDeviceNode->enable();
115
    ctx->enable();
116
}
117

    
118
size_t AudioEngine::getSampleRate()
119
{
120
    return Context::master()->getSampleRate();
121
}
122

    
123
void AudioEngine::loopOn( size_t waveIdx )
124
{
125
    NoteMsg msg = makeNoteMsg( Command::LOOP_ON, 1, 1.0 );
126
    mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 );
127
}
128

    
129
void AudioEngine::loopOff( size_t waveIdx )
130
{
131
    NoteMsg msg = makeNoteMsg( Command::LOOP_OFF, 0, 0.0 );
132
    mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 );
133
}
134

    
135
void AudioEngine::record( size_t waveIdx )
136
{
137
    mBufferRecorderNodes[waveIdx]->start();
138
}
139

    
140
void AudioEngine::noteOn( size_t waveIdx, int midiNote )
141
{
142
    
143
    double midiAsRate = calculateMidiNoteRatio(midiNote);
144
    NoteMsg msg = makeNoteMsg( Command::NOTE_ON, midiNote, midiAsRate );
145

    
146
    mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 );
147
}
148

    
149
void AudioEngine::noteOff( size_t waveIdx, int midiNote )
150
{
151
    NoteMsg msg = makeNoteMsg( Command::NOTE_OFF, midiNote, 0.0 );
152
    mPGranularNodes[waveIdx]->getNoteRingBuffer().write( &msg, 1 );
153
}
154

    
155

    
156

    
157
void AudioEngine::setSelectionSize( size_t waveIdx, size_t size )
158
{
159
    mPGranularNodes[waveIdx]->setSelectionSize( size );
160
}
161

    
162
void AudioEngine::setSelectionStart( size_t waveIdx, size_t start )
163
{
164
    mPGranularNodes[waveIdx]->setSelectionStart( start );
165
}
166

    
167
void AudioEngine::setGrainDurationCoeff( size_t waveIdx, double coeff )
168
{
169
    mPGranularNodes[waveIdx]->setGrainsDurationCoeff( coeff );
170
}
171

    
172
void AudioEngine::setFilterCutoff( size_t waveIdx, double cutoff )
173
{
174
    mLowPassFilterNodes[waveIdx]->setCutoffFreq( cutoff );
175
}
176

    
177
// ------------------------------------------------------
178
// ----- methods for communication with main thread -----
179
// ------------------------------------------------------
180

    
181
size_t AudioEngine::getRecordWaveAvailable( size_t waveIdx )
182
{
183
    return mBufferRecorderNodes[waveIdx]->getRingBuffer().getAvailableRead();
184
}
185

    
186
 
187
bool AudioEngine::readRecordWave( size_t waveIdx, RecordWaveMsg* buffer, size_t count )
188
{
189
    return mBufferRecorderNodes[waveIdx]->getRingBuffer().read( buffer, count );
190
}
191

    
192
void AudioEngine::checkCursorTriggers( size_t waveIdx, std::vector<CursorTriggerMsg>& cursorTriggers )
193
{
194
    ci::audio::dsp::RingBufferT<CursorTriggerMsg> &ringBuffer = mCursorTriggerRingBufferPacks[waveIdx]->getBuffer();
195
    CursorTriggerMsg* ringBufferReadArray = mCursorTriggerRingBufferPacks[waveIdx]->getExchangeArray();
196

    
197
    size_t availableRead = ringBuffer.getAvailableRead();
198
    bool successfulRead = ringBuffer.read( ringBufferReadArray, availableRead );
199

    
200
    if ( successfulRead ){
201
        for ( size_t i = 0; i < availableRead; i++ ){
202
            cursorTriggers.push_back( ringBufferReadArray[i] );
203
        }
204
    }
205
}
206

    
207
const ci::audio::Buffer& AudioEngine::getAudioOutputBuffer( size_t waveIdx ) const
208
{
209
    return mOutputMonitorNodes[waveIdx]->getBuffer();
210
}
211