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 / PGranularNode.cpp @ 0:02467299402e

History | View | Annotate | Download (5.49 KB)

1 0:02467299402e f
#include "PGranularNode.h"
2
3
#include "cinder/audio/Context.h"
4
5
#include "cinder/Rand.h"
6
7
// generate random numbers from 0 to max
8
// it's passed to PGranular to randomize the phase offset at grain creation
9
struct RandomGenerator
10
{
11
12
    RandomGenerator( size_t max ) : mMax( max )
13
    {}
14
15
    size_t operator()() const {
16
        return ci::Rand::randUint( mMax );
17
    }
18
19
    size_t mMax;
20
};
21
// FIXME maybe use only one random gen
22
23
PGranularNode::PGranularNode( ci::audio::Buffer *grainBuffer, CursorTriggerMsgRingBuffer &triggerRingBuffer ) :
24
    Node( Format().channels( 1 ) ),
25
    mGrainBuffer(grainBuffer),
26
    mSelectionStart( 0 ),
27
    mSelectionSize( 0 ),
28
    mGrainDurationCoeff( 1 ),
29
    mTriggerRingBuffer( triggerRingBuffer ),
30
    mNoteMsgRingBufferPack( 128 )
31
{
32
    for ( int i = 0; i < kMaxVoices; i++ ){
33
        mMidiNotes[i] = kNoMidiNote;
34
35
    }
36
}
37
38
39
PGranularNode::~PGranularNode()
40
{
41
}
42
43
void PGranularNode::initialize()
44
{
45
    mTempBuffer = std::make_shared< ci::audio::Buffer >( getFramesPerBlock() );
46
47
    mRandomOffset.reset( new RandomGenerator( getSampleRate() / 100 ) ); // divided by 100 corresponds to times 0.01 in the time domain
48
49
    /* create the PGranular object for looping */
50
    mPGranularLoop.reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, -1 ) );
51
52
    /* create the PGranular object for notes */
53
    for ( size_t i = 0; i < kMaxVoices; i++ ){
54
        mPGranularNotes[i].reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, i ) );
55
    }
56
57
}
58
59
void PGranularNode::process (ci::audio::Buffer *buffer )
60
{
61
    // only update PGranular if the atomic value has changed from the previous time
62
63
    const boost::optional<size_t> selectionSize = mSelectionSize.get();
64
    if ( selectionSize ){
65
        mPGranularLoop->setSelectionSize( *selectionSize );
66
        for ( size_t i = 0; i < kMaxVoices; i++ ){
67
            mPGranularNotes[i]->setSelectionSize( *selectionSize );
68
        }
69
    }
70
71
    const boost::optional<size_t> selectionStart = mSelectionStart.get();
72
    if ( selectionStart ){
73
        mPGranularLoop->setSelectionStart( *selectionStart );
74
        for ( size_t i = 0; i < kMaxVoices; i++ ){
75
            mPGranularNotes[i]->setSelectionStart( *selectionStart );
76
        }
77
    }
78
79
    const boost::optional<double> grainDurationCoeff = mGrainDurationCoeff.get();
80
    if ( grainDurationCoeff ){
81
        mPGranularLoop->setGrainsDurationCoeff( *grainDurationCoeff );
82
        for ( size_t i = 0; i < kMaxVoices; i++ ){
83
            mPGranularNotes[i]->setGrainsDurationCoeff( *grainDurationCoeff );
84
        }
85
    }
86
87
    // check messages to start/stop notes or loop
88
    size_t availableRead = mNoteMsgRingBufferPack.getBuffer().getAvailableRead();
89
    mNoteMsgRingBufferPack.getBuffer().read( mNoteMsgRingBufferPack.getExchangeArray(), availableRead );
90
    for ( size_t i = 0; i < availableRead; i++ ){
91
        handleNoteMsg( mNoteMsgRingBufferPack.getExchangeArray()[i] );
92
    }
93
94
    // process loop if not idle
95
    if ( !mPGranularLoop->isIdle() ){
96
        /* buffer is one channel only so I can use getData */
97
        mPGranularLoop->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
98
    }
99
100
    // process notes if not idle
101
    for ( size_t i = 0; i < kMaxVoices; i++ ){
102
        if ( mPGranularNotes[i]->isIdle() )
103
            continue;
104
105
        mPGranularNotes[i]->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
106
107
        if ( mPGranularNotes[i]->isIdle() ){
108
            // this note became idle so update mMidiNotes
109
            mMidiNotes[i] = kNoMidiNote;
110
        }
111
112
    }
113
}
114
115
void PGranularNode::operator()( char msgType, int ID ) {
116
117
    switch ( msgType ){
118
    case 't':  { // trigger
119
        CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_UPDATE, ID ); // put ID
120
        mTriggerRingBuffer.write( &msg, 1 );
121
    };
122
        break;
123
124
    case 'e': // end envelope
125
        CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_END, ID ); // put ID
126
        mTriggerRingBuffer.write( &msg, 1 );
127
        break;
128
    }
129
130
131
}
132
133
void PGranularNode::handleNoteMsg( const NoteMsg &msg )
134
{
135
    switch ( msg.cmd ){
136
    case Command::NOTE_ON: {
137
        bool synthFound = false;
138
139
        for ( int i = 0; i < kMaxVoices; i++ ){
140
            // note was already on, so re-attack
141
            if ( mMidiNotes[i] == msg.midiNote ){
142
                mPGranularNotes[i]->noteOn( msg.rate );
143
                synthFound = true;
144
                break;
145
            }
146
        }
147
148
        if ( !synthFound ){
149
            // then look for a free synth
150
            for ( int i = 0; i < kMaxVoices; i++ ){
151
152
                if ( mMidiNotes[i] == kNoMidiNote ){
153
                    mPGranularNotes[i]->noteOn( msg.rate );
154
                    mMidiNotes[i] = msg.midiNote;
155
                    synthFound = true;
156
                    break;
157
                }
158
            }
159
        }
160
    };
161
        break;
162
163
    case Command::NOTE_OFF: {
164
        for ( int i = 0; i < kMaxVoices; i++ ){
165
            if ( !mPGranularNotes[i]->isIdle() && mMidiNotes[i] == msg.midiNote ){
166
                mPGranularNotes[i]->noteOff();
167
                break;
168
            }
169
        }
170
    };
171
        break;
172
173
    case Command::LOOP_ON: {
174
        mPGranularLoop->noteOn( 1.0 );
175
    };
176
        break;
177
178
    case Command::LOOP_OFF: {
179
        mPGranularLoop->noteOff();
180
    };
181
        break;
182
    default:
183
        break;
184
    }
185
}