Revision 2:dd889fff8423 CollidoscopeApp
| CollidoscopeApp/include/AudioEngine.h | ||
|---|---|---|
| 14 | 14 |
#include "Config.h" |
| 15 | 15 |
|
| 16 | 16 |
|
| 17 |
/** |
|
| 18 |
* Audio engine of the application. It uses the Cinder library to process audio in input and output. |
|
| 19 |
* The audio engine manages both waves. All methods have a waveIndx parameter to address a specific wave. |
|
| 20 |
*/ |
|
| 17 | 21 |
class AudioEngine |
| 18 | 22 |
{
|
| 19 | 23 |
public: |
| ... | ... | |
| 26 | 30 |
AudioEngine( const AudioEngine © ) = delete; |
| 27 | 31 |
AudioEngine & operator=(const AudioEngine ©) = delete; |
| 28 | 32 |
|
| 33 |
/** |
|
| 34 |
* Set up of the audio engine. |
|
| 35 |
*/ |
|
| 29 | 36 |
void setup( const Config& Config ); |
| 30 | 37 |
|
| 31 | 38 |
size_t getSampleRate(); |
| ... | ... | |
| 40 | 47 |
|
| 41 | 48 |
void noteOff( size_t waveIdx, int note ); |
| 42 | 49 |
|
| 50 |
/** |
|
| 51 |
* Returns the number of elements available to read in the wave ring buffer. |
|
| 52 |
* The wave ring buffer is used to pass the size of the wave chunks from the audio thread to the graphic thread, |
|
| 53 |
* when a new wave is recorded. |
|
| 54 |
*/ |
|
| 43 | 55 |
size_t getRecordWaveAvailable( size_t index ); |
| 44 |
|
|
| 45 |
bool readRecordWave( size_t waveIdx, RecordWaveMsg*, size_t count ); |
|
| 56 |
/** |
|
| 57 |
* Called from the graphic thread. Reads count elements from the wave ring buffer into \a buffer. |
|
| 58 |
* The wave ring buffer is used to pass the size of the wave chunks from the audio thread to the graphic thread, |
|
| 59 |
* when a new wave is recorded. |
|
| 60 |
* |
|
| 61 |
*/ |
|
| 62 |
bool readRecordWave( size_t waveIdx, RecordWaveMsg* buffer, size_t count ); |
|
| 46 | 63 |
|
| 47 | 64 |
void setSelectionSize( size_t waveIdx, size_t size ); |
| 48 | 65 |
|
| ... | ... | |
| 54 | 71 |
|
| 55 | 72 |
void checkCursorTriggers( size_t waveIdx, std::vector<CursorTriggerMsg>& cursorTriggers ); |
| 56 | 73 |
|
| 74 |
/** |
|
| 75 |
* Returns a const reference to the audio output buffer. This is the buffer that is sent off to the audio interface at each audio cycle. |
|
| 76 |
* It is used in the graphic thread to draw the oscilloscope. |
|
| 77 |
*/ |
|
| 57 | 78 |
const ci::audio::Buffer& getAudioOutputBuffer( size_t waveIdx ) const; |
| 58 | 79 |
|
| 59 | 80 |
|
| ... | ... | |
| 76 | 97 |
|
| 77 | 98 |
std::array< std::unique_ptr< RingBufferPack<CursorTriggerMsg> >, NUM_WAVES > mCursorTriggerRingBufferPacks; |
| 78 | 99 |
|
| 79 |
}; |
|
| 100 |
}; |
|
| CollidoscopeApp/include/BufferToWaveRecorderNode.h | ||
|---|---|---|
| 12 | 12 |
|
| 13 | 13 |
typedef ci::audio::dsp::RingBufferT<RecordWaveMsg> RecordWaveMsgRingBuffer; |
| 14 | 14 |
|
| 15 |
|
|
| 15 |
/** |
|
| 16 |
* A \a Node in the audio graph of the Cinder audio library that records input in a buffer. |
|
| 17 |
* |
|
| 18 |
* This class is similar to \a cinder::audio::BufferRecorderNode (it's a derivative work of this class indeed) but it has an additional feature. |
|
| 19 |
* When recording it uses the audio input samples to compute the size values of the visual chunks. |
|
| 20 |
* The chunks values are stored in a ring buffer and fetched by the graphic thread to paint the wave as it gets recorded. |
|
| 21 |
* |
|
| 22 |
*/ |
|
| 16 | 23 |
class BufferToWaveRecorderNode : public ci::audio::SampleRecorderNode {
|
| 17 | 24 |
public: |
| 18 | 25 |
|
| ... | ... | |
| 54 | 61 |
//! Returns the frame of the last buffer overrun or 0 if none since the last time this method was called. When this happens, it means the recorded buffer probably has skipped some frames. |
| 55 | 62 |
uint64_t getLastOverrun(); |
| 56 | 63 |
|
| 64 |
//! returns a reference to the ring buffer when the size values of the chunks is stored, when a new wave is recorder |
|
| 57 | 65 |
RecordWaveMsgRingBuffer& getRingBuffer() { return mRingBuffer; }
|
| 58 | 66 |
|
| 67 |
//!returns a pointer to the buffer where the audio is recorder. This is used by the PGranular to create the granular synthesis |
|
| 59 | 68 |
ci::audio::Buffer* getRecorderBuffer() { return &mRecorderBuffer; }
|
| 60 | 69 |
|
| 61 | 70 |
|
| CollidoscopeApp/include/Chunk.h | ||
|---|---|---|
| 6 | 6 |
|
| 7 | 7 |
class DrawInfo; |
| 8 | 8 |
|
| 9 |
/** |
|
| 10 |
* |
|
| 11 |
* A chunk of audio in Collidoscope low-fi visual wave. |
|
| 12 |
* |
|
| 13 |
* The visual wave of Collidoscope is made out of a number of bars that mimics in a low-fi fashion the typical waveform based representation of audio. |
|
| 14 |
* A Chunk is one of the bars of the visual wave. |
|
| 15 |
* |
|
| 16 |
*/ |
|
| 17 |
|
|
| 9 | 18 |
class Chunk |
| 10 | 19 |
{
|
| 11 | 20 |
|
| ... | ... | |
| 14 | 23 |
const static float kWidth; |
| 15 | 24 |
const static float kHalfWidth; |
| 16 | 25 |
|
| 26 |
/** |
|
| 27 |
* Constructor, takes as argument the index of this chunk in the wave |
|
| 28 |
*/ |
|
| 17 | 29 |
Chunk( size_t index ); |
| 18 | 30 |
|
| 31 |
/** |
|
| 32 |
* Sets the top value of this chunk. The value is passed in audio coordinates : [-1.0, 1.0] |
|
| 33 |
*/ |
|
| 19 | 34 |
void inline setTop(float t) { mAudioTop = t; mAnimate = 0.0f; mResetting = false; /* startes the animation to crate a chunk */ }
|
| 35 |
/** |
|
| 36 |
* Sets the bottom value of this chunk. The value is passed in audio coordinates : [-1.0, 1.0] |
|
| 37 |
*/ |
|
| 20 | 38 |
void inline setBottom(float b) { mAudioBottom = b; mAnimate = 0.0f; mResetting = false; }
|
| 39 |
/** |
|
| 40 |
* Get the top value of this chunk. The value is returned in audio coordinates : [-1.0, 1.0] |
|
| 41 |
*/ |
|
| 21 | 42 |
float inline getTop() const { return mAudioTop; }
|
| 43 |
/** |
|
| 44 |
* Get the bottom value of this chunk. The value is returned in audio coordinates : [-1.0, 1.0] |
|
| 45 |
*/ |
|
| 22 | 46 |
float inline getBottom() const { return mAudioBottom; }
|
| 23 | 47 |
|
| 48 |
/** |
|
| 49 |
* Reset this chunks. When a chunk is reset it starts shrinking until it disappears. |
|
| 50 |
* |
|
| 51 |
*/ |
|
| 24 | 52 |
void reset(){
|
| 25 | 53 |
mResetting = true; |
| 26 | 54 |
} |
| 27 | 55 |
|
| 56 |
/** |
|
| 57 |
* Called in the graphic loop. It update this chunk. |
|
| 58 |
*/ |
|
| 28 | 59 |
void update( const DrawInfo& di ); |
| 29 | 60 |
|
| 61 |
/** |
|
| 62 |
* Called in the graphic loop. It draws this chunk. |
|
| 63 |
*/ |
|
| 30 | 64 |
void draw( const DrawInfo& di, ci::gl::BatchRef &batch ); |
| 31 | 65 |
|
| 66 |
/** |
|
| 67 |
* Called in the graphic loop. It draws this chunk all the way to the bottom of the screen. |
|
| 68 |
* This method is called when the chunk is the first or last in a selection. |
|
| 69 |
*/ |
|
| 32 | 70 |
void drawBar( const DrawInfo& di, ci::gl::BatchRef &batch ); |
| 33 | 71 |
|
| 72 |
/** |
|
| 73 |
* Informs this chunk that it's the first chunk of the selection. |
|
| 74 |
*/ |
|
| 34 | 75 |
void setAsSelectionStart(bool start){
|
| 35 | 76 |
isSelectionStart = start; |
| 36 | 77 |
} |
| 37 | 78 |
|
| 79 |
/** |
|
| 80 |
* Informs this chunk that it's the last chunk of the selection. |
|
| 81 |
*/ |
|
| 38 | 82 |
void setAsSelectionEnd(bool end){
|
| 39 | 83 |
isSelectionEnd = end; |
| 40 | 84 |
} |
| CollidoscopeApp/include/Config.h | ||
|---|---|---|
| 6 | 6 |
#include "cinder/Xml.h" |
| 7 | 7 |
|
| 8 | 8 |
|
| 9 |
/** |
|
| 10 |
* Configuration class gathers in one place all the values recided at runtime |
|
| 11 |
* |
|
| 12 |
* Reading the configuration from an XML file is partially implemented but not used at the moment |
|
| 13 |
* |
|
| 14 |
*/ |
|
| 9 | 15 |
class Config |
| 10 | 16 |
{
|
| 11 | 17 |
public: |
| ... | ... | |
| 21 | 27 |
|
| 22 | 28 |
std::string getInputDeviceKey() const |
| 23 | 29 |
{
|
| 24 |
return mAudioInputDeviceKey; // Komplete 1/2 |
|
| 25 |
//return "{0.0.1.00000000}.{a043bc8c-1dd1-4c94-82b4-ad8320cac5a5}"; // Komplete 3/4
|
|
| 26 |
//return "{0.0.1.00000000}.{828b681b-cc0c-44e1-93c9-5f1f46f5926f}"; // Realtek
|
|
| 30 |
return mAudioInputDeviceKey; |
|
| 27 | 31 |
} |
| 28 | 32 |
|
| 33 |
/** |
|
| 34 |
* Returns number of chunks in a wave |
|
| 35 |
*/ |
|
| 29 | 36 |
std::size_t getNumChunks() const |
| 30 | 37 |
{
|
| 31 | 38 |
return mNumChunks; |
| 32 | 39 |
} |
| 33 | 40 |
|
| 34 |
/* return wave lenght in seconds */
|
|
| 41 |
/** returns wave lenght in seconds */
|
|
| 35 | 42 |
double getWaveLen() const |
| 36 | 43 |
{
|
| 37 | 44 |
return mWaveLen; |
| 38 | 45 |
} |
| 39 | 46 |
|
| 47 |
/** |
|
| 48 |
* Returns wave's selection color |
|
| 49 |
*/ |
|
| 40 | 50 |
ci::Color getWaveSelectionColor(size_t waveIdx) const |
| 41 | 51 |
{
|
| 42 | 52 |
if (waveIdx == 0){
|
| ... | ... | |
| 47 | 57 |
} |
| 48 | 58 |
} |
| 49 | 59 |
|
| 60 |
/** |
|
| 61 |
* The size of the ring buffer used to trigger a visual cursor from the audio thread when a new grain is created |
|
| 62 |
*/ |
|
| 50 | 63 |
std::size_t getCursorTriggerMessageBufSize() const |
| 51 | 64 |
{
|
| 52 | 65 |
return 512; |
| 53 | 66 |
} |
| 54 | 67 |
|
| 55 |
// returns the index of the wave associated to the MIDI channel passed as argument
|
|
| 68 |
/** returns the index of the wave associated to the MIDI channel passed as argument */
|
|
| 56 | 69 |
size_t getWaveForMIDIChannel( unsigned char channelIdx ) |
| 57 | 70 |
{
|
| 58 | 71 |
return channelIdx; |
| 59 |
/*for ( int i = 0; i < NUM_WAVES; i++ ){
|
|
| 60 |
if ( channelIdx == mMidiChannels[i] ) |
|
| 61 |
return i; |
|
| 62 |
}*/ |
|
| 63 | 72 |
} |
| 64 | 73 |
|
| 65 | 74 |
double getMaxGrainDurationCoeff() const |
| ... | ... | |
| 82 | 91 |
return 6; |
| 83 | 92 |
} |
| 84 | 93 |
|
| 94 |
/** |
|
| 95 |
* Returns the maximum size of a wave selection in number of chunks. |
|
| 96 |
*/ |
|
| 85 | 97 |
size_t getMaxSelectionNumChunks() const |
| 86 | 98 |
{
|
| 87 | 99 |
return 37; |
| 88 | 100 |
} |
| 89 | 101 |
|
| 102 |
/** |
|
| 103 |
* The value returned is used when creating the oscilloscope. |
|
| 104 |
* The oscilloscope represents the audio output buffer graphically. However it doesn't need to be as refined as the |
|
| 105 |
* audio wave and it's downsampled using the following formula : number of oscilloscope points = size o audio output buffer / getOscilloscopeNumPointsDivider() |
|
| 106 |
*/ |
|
| 90 | 107 |
size_t getOscilloscopeNumPointsDivider() const |
| 91 | 108 |
{
|
| 92 | 109 |
return 4; |
| CollidoscopeApp/include/EnvASR.h | ||
|---|---|---|
| 2 | 2 |
|
| 3 | 3 |
namespace collidoscope {
|
| 4 | 4 |
|
| 5 |
|
|
| 6 |
/* |
|
| 7 |
* An ASR envelope with linear shape. It is modeled after the STK envelope classes. |
|
| 8 |
* The tick() method advances the computation of the envelope one sample and returns the computed sample |
|
| 9 |
* The class is templated for the type of the samples that each tick of the envelope produces. |
|
| 10 |
* |
|
| 11 |
* Client classes can set/get the current state of the envelope with the |
|
| 12 |
* respective getter/setter methods |
|
| 13 |
* |
|
| 14 |
*/ |
|
| 5 | 15 |
template <typename T> |
| 6 | 16 |
class EnvASR |
| 7 | 17 |
{
|
| ... | ... | |
| 88 | 98 |
}; |
| 89 | 99 |
|
| 90 | 100 |
|
| 91 |
} |
|
| 101 |
} |
|
| CollidoscopeApp/include/Oscilloscope.h | ||
|---|---|---|
| 4 | 4 |
|
| 5 | 5 |
#include "DrawInfo.h" |
| 6 | 6 |
|
| 7 |
|
|
| 8 |
|
|
| 9 |
/** |
|
| 10 |
* The oscilloscope that oscillates when Collidoscope is played |
|
| 11 |
*/ |
|
| 7 | 12 |
class Oscilloscope |
| 8 | 13 |
{
|
| 9 | 14 |
|
| 10 | 15 |
public: |
| 11 | 16 |
|
| 17 |
/** |
|
| 18 |
* Constructor, accepts as argument the number of points that make up the oscilloscope line |
|
| 19 |
*/ |
|
| 12 | 20 |
Oscilloscope( size_t numPoints ): |
| 13 | 21 |
mNumPoints( numPoints ), |
| 14 | 22 |
mLine( std::vector<ci::vec2>( numPoints, ci::vec2() ) ) |
| 15 | 23 |
{}
|
| 16 | 24 |
|
| 25 |
/** |
|
| 26 |
* Sets the value of a point of the oscilloscope. The value is passed as an audio coordinate [-1.0, 1.0]. |
|
| 27 |
* A reference to DrawInfo is passed to calculate the graphic coordinate of the point based on the audio value passed. |
|
| 28 |
*/ |
|
| 17 | 29 |
void setPoint( int index, float audioVal, const DrawInfo &di ){
|
| 18 | 30 |
|
| 19 | 31 |
if ( audioVal > 1.0f ){
|
| ... | ... | |
| 45 | 57 |
|
| 46 | 58 |
} |
| 47 | 59 |
|
| 60 |
/** |
|
| 61 |
* Draws this oscilloscope as a cinder::PolyLine2f |
|
| 62 |
*/ |
|
| 48 | 63 |
void draw() |
| 49 | 64 |
{
|
| 50 | 65 |
ci::gl::color(1.0f, 1.0f, 1.0f); |
| CollidoscopeApp/src/AudioEngine.cpp | ||
|---|---|---|
| 5 | 5 |
|
| 6 | 6 |
using namespace ci::audio; |
| 7 | 7 |
|
| 8 |
/* Frequency ratios in the chromatic scale */ |
|
| 8 | 9 |
double chromaticRatios[] = {
|
| 9 | 10 |
1, |
| 10 | 11 |
1.0594630943591, |
| ... | ... | |
| 20 | 21 |
1.8877486253586 |
| 21 | 22 |
}; |
| 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 |
*/ |
|
| 23 | 32 |
inline double calculateMidiNoteRatio( int midiNote ) |
| 24 | 33 |
{
|
| 25 | 34 |
int distanceFromCenter = midiNote - 60; // 60 is the central midi note |
| ... | ... | |
| 174 | 183 |
return mBufferRecorderNodes[waveIdx]->getRingBuffer().getAvailableRead(); |
| 175 | 184 |
} |
| 176 | 185 |
|
| 186 |
|
|
| 177 | 187 |
bool AudioEngine::readRecordWave( size_t waveIdx, RecordWaveMsg* buffer, size_t count ) |
| 178 | 188 |
{
|
| 179 | 189 |
return mBufferRecorderNodes[waveIdx]->getRingBuffer().read( buffer, count ); |
| CollidoscopeApp/src/Config.cpp | ||
|---|---|---|
| 17 | 17 |
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 |
|
|
| 20 |
// uses Cinder api to parse configuration in XML file |
|
| 21 | 21 |
void Config::loadFromFile( std::string&& path ) |
| 22 | 22 |
{
|
| 23 | 23 |
try {
|
| CollidoscopeApp/src/MIDI.cpp | ||
|---|---|---|
| 131 | 131 |
msg.mData2 = (numBytes == 3 ? (*rtMidiMessage)[2] : 0); |
| 132 | 132 |
|
| 133 | 133 |
return msg; |
| 134 |
} |
|
| 134 |
} |
|
Also available in: Unified diff