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 @ 9:20bb004a36de

History | View | Annotate | Download (6.32 KB)

1
/*
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
#include "PGranularNode.h"
23

    
24
#include "cinder/audio/Context.h"
25

    
26
#include "cinder/Rand.h"
27

    
28
// generate random numbers from 0 to max 
29
// it's passed to PGranular to randomize the phase offset at grain creation 
30
struct RandomGenerator
31
{
32

    
33
    RandomGenerator( size_t max ) : mMax( max )
34
    {}
35

    
36
    size_t operator()() const {
37
        return ci::Rand::randUint( mMax );
38
    }
39

    
40
    size_t mMax;
41
};
42
// FIXME maybe use only one random gen 
43

    
44
PGranularNode::PGranularNode( ci::audio::Buffer *grainBuffer, CursorTriggerMsgRingBuffer &triggerRingBuffer ) :
45
    Node( Format().channels( 1 ) ),
46
    mGrainBuffer(grainBuffer),
47
    mSelectionStart( 0 ),
48
    mSelectionSize( 0 ),
49
    mGrainDurationCoeff( 1 ),
50
    mTriggerRingBuffer( triggerRingBuffer ),
51
    mNoteMsgRingBufferPack( 128 )
52
{
53
    for ( int i = 0; i < kMaxVoices; i++ ){
54
        mMidiNotes[i] = kNoMidiNote;
55

    
56
    }
57
}
58

    
59

    
60
PGranularNode::~PGranularNode()
61
{
62
}
63

    
64
void PGranularNode::initialize()
65
{
66
    mTempBuffer = std::make_shared< ci::audio::Buffer >( getFramesPerBlock() );
67

    
68
    mRandomOffset.reset( new RandomGenerator( getSampleRate() / 100 ) ); // divided by 100 corresponds to times 0.01 in the time domain 
69

    
70
    /* create the PGranular object for looping */
71
    mPGranularLoop.reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, -1 ) );
72

    
73
    /* create the PGranular object for notes */
74
    for ( size_t i = 0; i < kMaxVoices; i++ ){
75
        mPGranularNotes[i].reset( new collidoscope::PGranular<float, RandomGenerator, PGranularNode>( mGrainBuffer->getData(), mGrainBuffer->getNumFrames(), getSampleRate(), *mRandomOffset, *this, i ) );
76
    }
77

    
78
}
79

    
80
void PGranularNode::process (ci::audio::Buffer *buffer )
81
{
82
    // only update PGranular if the atomic value has changed from the previous time
83
    const boost::optional<size_t> selectionSize = mSelectionSize.get();
84
    if ( selectionSize ){
85
        mPGranularLoop->setSelectionSize( *selectionSize );
86
        for ( size_t i = 0; i < kMaxVoices; i++ ){
87
            mPGranularNotes[i]->setSelectionSize( *selectionSize );
88
        }
89
    }
90

    
91
    const boost::optional<size_t> selectionStart = mSelectionStart.get();
92
    if ( selectionStart ){
93
        mPGranularLoop->setSelectionStart( *selectionStart );
94
        for ( size_t i = 0; i < kMaxVoices; i++ ){
95
            mPGranularNotes[i]->setSelectionStart( *selectionStart );
96
        }
97
    }
98

    
99
    const boost::optional<double> grainDurationCoeff = mGrainDurationCoeff.get();
100
    if ( grainDurationCoeff ){
101
        mPGranularLoop->setGrainsDurationCoeff( *grainDurationCoeff );
102
        for ( size_t i = 0; i < kMaxVoices; i++ ){
103
            mPGranularNotes[i]->setGrainsDurationCoeff( *grainDurationCoeff );
104
        }
105
    }
106

    
107
    // check messages to start/stop notes or loop 
108
    size_t availableRead = mNoteMsgRingBufferPack.getBuffer().getAvailableRead();
109
    mNoteMsgRingBufferPack.getBuffer().read( mNoteMsgRingBufferPack.getExchangeArray(), availableRead );
110
    for ( size_t i = 0; i < availableRead; i++ ){
111
        handleNoteMsg( mNoteMsgRingBufferPack.getExchangeArray()[i] );
112
    }
113

    
114
    // process loop if not idle 
115
    if ( !mPGranularLoop->isIdle() ){
116
        /* buffer is one channel only so I can use getData */
117
        mPGranularLoop->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
118
    }
119

    
120
    // process notes if not idle 
121
    for ( size_t i = 0; i < kMaxVoices; i++ ){
122
        if ( mPGranularNotes[i]->isIdle() )
123
            continue;
124

    
125
        mPGranularNotes[i]->process( buffer->getData(), mTempBuffer->getData(), buffer->getSize() );
126

    
127
        if ( mPGranularNotes[i]->isIdle() ){
128
            // this note became idle so update mMidiNotes
129
            mMidiNotes[i] = kNoMidiNote;
130
        }
131
            
132
    }
133
}
134

    
135
// Called back when new grnular is triggered of turned off. Sends notification message to graphic thread.
136
void PGranularNode::operator()( char msgType, int ID ) {
137

    
138
    switch ( msgType ){
139
    case 't':  { // trigger 
140
        CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_UPDATE, ID ); // put ID 
141
        mTriggerRingBuffer.write( &msg, 1 );
142
    };
143
        break;
144

    
145
    case 'e': // end envelope 
146
        CursorTriggerMsg msg = makeCursorTriggerMsg( Command::TRIGGER_END, ID ); // put ID 
147
        mTriggerRingBuffer.write( &msg, 1 );
148
        break;
149
    }
150

    
151
    
152
}
153

    
154
void PGranularNode::handleNoteMsg( const NoteMsg &msg )
155
{
156
    switch ( msg.cmd ){
157
    case Command::NOTE_ON: {
158
        bool synthFound = false;
159

    
160
        for ( int i = 0; i < kMaxVoices; i++ ){
161
            // note was already on, so re-attack
162
            if ( mMidiNotes[i] == msg.midiNote ){
163
                mPGranularNotes[i]->noteOn( msg.rate );
164
                synthFound = true;
165
                break;
166
            }
167
        }
168

    
169
        if ( !synthFound ){
170
            // then look for a free synth 
171
            for ( int i = 0; i < kMaxVoices; i++ ){
172

    
173
                if ( mMidiNotes[i] == kNoMidiNote ){
174
                    mPGranularNotes[i]->noteOn( msg.rate );
175
                    mMidiNotes[i] = msg.midiNote;
176
                    synthFound = true;
177
                    break;
178
                }
179
            }
180
        }
181
    };
182
        break;
183

    
184
    case Command::NOTE_OFF: {
185
        for ( int i = 0; i < kMaxVoices; i++ ){
186
            if ( !mPGranularNotes[i]->isIdle() && mMidiNotes[i] == msg.midiNote ){
187
                mPGranularNotes[i]->noteOff();
188
                break;
189
            }
190
        }
191
    };
192
        break;
193

    
194
    case Command::LOOP_ON: {
195
        mPGranularLoop->noteOn( 1.0 );
196
    };
197
        break;
198

    
199
    case Command::LOOP_OFF: {
200
        mPGranularLoop->noteOff();
201
    };
202
        break;
203
    default:
204
        break;
205
    }
206
}