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 @ 4:ab6db404403a

History | View | Annotate | Download (5.6 KB)

1
#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
    const boost::optional<size_t> selectionSize = mSelectionSize.get();
63
    if ( selectionSize ){
64
        mPGranularLoop->setSelectionSize( *selectionSize );
65
        for ( size_t i = 0; i < kMaxVoices; i++ ){
66
            mPGranularNotes[i]->setSelectionSize( *selectionSize );
67
        }
68
    }
69

    
70
    const boost::optional<size_t> selectionStart = mSelectionStart.get();
71
    if ( selectionStart ){
72
        mPGranularLoop->setSelectionStart( *selectionStart );
73
        for ( size_t i = 0; i < kMaxVoices; i++ ){
74
            mPGranularNotes[i]->setSelectionStart( *selectionStart );
75
        }
76
    }
77

    
78
    const boost::optional<double> grainDurationCoeff = mGrainDurationCoeff.get();
79
    if ( grainDurationCoeff ){
80
        mPGranularLoop->setGrainsDurationCoeff( *grainDurationCoeff );
81
        for ( size_t i = 0; i < kMaxVoices; i++ ){
82
            mPGranularNotes[i]->setGrainsDurationCoeff( *grainDurationCoeff );
83
        }
84
    }
85

    
86
    // check messages to start/stop notes or loop 
87
    size_t availableRead = mNoteMsgRingBufferPack.getBuffer().getAvailableRead();
88
    mNoteMsgRingBufferPack.getBuffer().read( mNoteMsgRingBufferPack.getExchangeArray(), availableRead );
89
    for ( size_t i = 0; i < availableRead; i++ ){
90
        handleNoteMsg( mNoteMsgRingBufferPack.getExchangeArray()[i] );
91
    }
92

    
93
    // process loop if not idle 
94
    if ( !mPGranularLoop->isIdle() ){
95
        /* buffer is one channel only so I can use getData */
96
        mPGranularLoop->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
97
    }
98

    
99
    // process notes if not idle 
100
    for ( size_t i = 0; i < kMaxVoices; i++ ){
101
        if ( mPGranularNotes[i]->isIdle() )
102
            continue;
103

    
104
        mPGranularNotes[i]->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
105

    
106
        if ( mPGranularNotes[i]->isIdle() ){
107
            // this note became idle so update mMidiNotes
108
            mMidiNotes[i] = kNoMidiNote;
109
        }
110
            
111
    }
112
}
113

    
114
// Called back when new grnular is triggered of turned off. Sends notification message to graphic thread.
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
}