f@5: /* f@5: f@5: Copyright (C) 2016 Queen Mary University of London f@5: Author: Fiore Martin f@5: f@5: This file is part of Collidoscope. f@5: f@5: Collidoscope is free software: you can redistribute it and/or modify f@5: it under the terms of the GNU General Public License as published by f@5: the Free Software Foundation, either version 3 of the License, or f@5: (at your option) any later version. f@5: f@5: This program is distributed in the hope that it will be useful, f@5: but WITHOUT ANY WARRANTY; without even the implied warranty of f@5: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the f@5: GNU General Public License for more details. f@5: f@5: You should have received a copy of the GNU General Public License f@5: along with this program. If not, see . f@5: */ f@5: f@0: #pragma once f@0: f@0: #include "cinder/Cinder.h" f@0: #include "cinder/audio/Node.h" f@0: #include "cinder/audio/dsp/RingBuffer.h" f@0: #include "boost/optional.hpp" f@0: #include "Messages.h" f@0: #include "RingBufferPack.h" f@0: f@0: #include f@0: f@0: #include "PGranular.h" f@0: #include "EnvASR.h" f@0: f@0: typedef std::shared_ptr PGranularNodeRef; f@0: typedef ci::audio::dsp::RingBufferT CursorTriggerMsgRingBuffer; f@0: f@0: f@0: struct RandomGenerator; f@0: f@0: /* f@16: A node in the Cinder audio graph that holds PGranulars for loop and keyboard playing f@0: */ f@0: class PGranularNode : public ci::audio::Node f@0: { f@0: public: f@0: static const size_t kMaxVoices = 6; f@0: static const int kNoMidiNote = -50; f@0: f@0: explicit PGranularNode( ci::audio::Buffer *grainBuffer, CursorTriggerMsgRingBuffer &triggerRingBuffer ); f@0: ~PGranularNode(); f@0: f@3: /** Set selection size in samples */ f@0: void setSelectionSize( size_t size ) f@0: { f@0: mSelectionSize.set( size ); f@0: } f@0: f@3: /** Set selection start in samples */ f@0: void setSelectionStart( size_t start ) f@0: { f@0: mSelectionStart.set( start ); f@0: } f@0: f@0: void setGrainsDurationCoeff( double coeff ) f@0: { f@0: mGrainDurationCoeff.set( coeff ); f@0: } f@0: f@3: /* PGranularNode passes itself as trigger callback in PGranular */ f@0: void operator()( char msgType, int ID ); f@0: f@0: ci::audio::dsp::RingBufferT& getNoteRingBuffer() { return mNoteMsgRingBufferPack.getBuffer(); } f@0: f@0: protected: f@0: f@5: void initialize() override; f@0: f@5: void process( ci::audio::Buffer *buffer ) override; f@0: f@0: private: f@0: f@3: // Wraps a std::atomic but get() returns a boost::optional that is set to a real value only when the atomic has changed. f@16: // It is used to avoid calling PGranular setter methods with the same value at each audio callback. f@0: template< typename T> f@0: class LazyAtomic f@0: { f@0: public: f@0: LazyAtomic( T val ) : f@0: mAtomic( val ), f@0: mPreviousVal( val ) f@0: {} f@0: f@0: void set( T val ) f@0: { f@0: mAtomic = val; f@0: } f@0: f@0: boost::optional get() f@0: { f@0: const T val = mAtomic; f@0: if ( val != mPreviousVal ){ f@0: mPreviousVal = val; f@0: return val; f@0: } f@0: else{ f@0: return boost::none; f@0: } f@0: } f@0: f@0: private: f@0: std::atomic mAtomic; f@0: T mPreviousVal; f@0: }; f@0: f@3: // creates or re-start a PGranular and sets the pitch according to the MIDI note passed as argument f@0: void handleNoteMsg( const NoteMsg &msg ); f@0: f@3: // pointers to PGranular objects f@0: std::unique_ptr < collidoscope::PGranular > mPGranularLoop; f@0: std::array >, kMaxVoices> mPGranularNotes; f@16: // maps midi notes to pgranulars. When a noteOff is received makes sure the right PGranular is turned off f@0: std::array mMidiNotes; f@0: f@0: // pointer to the random generator struct passed over to PGranular f@0: std::unique_ptr< RandomGenerator > mRandomOffset; f@0: f@16: // buffer containing the recorded audio, to pass to PGranular in initialize() f@0: ci::audio::Buffer *mGrainBuffer; f@0: f@0: ci::audio::BufferRef mTempBuffer; f@0: f@0: CursorTriggerMsgRingBuffer &mTriggerRingBuffer; f@0: RingBufferPack mNoteMsgRingBufferPack; f@0: f@0: LazyAtomic mSelectionSize; f@0: f@0: LazyAtomic mSelectionStart; f@0: f@0: LazyAtomic mGrainDurationCoeff; f@0: f@0: f@0: }; f@0: