f@5: /* f@5: f@5: Copyright (C) 2015 Fiore Martin 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: f@0: #include "cinder/app/App.h" f@0: #include "cinder/gl/gl.h" f@0: #include "cinder/gl/Batch.h" f@0: f@0: f@0: #include "Chunk.h" f@0: #include "DrawInfo.h" f@0: f@0: #ifdef USE_PARTICLES f@0: #include "ParticleController.h" f@0: #endif f@0: f@0: #include "cinder/Color.h" f@0: #include "cinder/PolyLine.h" f@0: #include "cinder/Rand.h" f@0: f@0: #include f@0: #include f@0: f@0: f@0: class DrawInfo; f@0: typedef int SynthID; f@0: f@0: f@0: using ci::ivec2; f@0: using ci::vec2; f@0: using ci::Color; f@0: using ci::ColorA; f@0: f@3: /** f@3: * A Cursor is the white thingy that loops through the selection when Collidoscope is played. f@3: */ f@0: struct Cursor { f@0: static const int kNoPosition = -100; f@0: int pos; f@0: double lastUpdate; f@0: }; f@0: f@3: /** f@3: * Collidoscope's graphical wave f@3: * f@3: */ f@0: class Wave f@0: { f@5: friend class ParticleController; f@0: f@0: public: f@0: f@3: /** f@3: * The selection of the wave that is controlled by the big horizontal knob f@3: * f@3: */ f@5: class Selection { f@5: f@5: public: f@0: f@0: Selection( Wave * w, Color color ); f@0: f@3: /** Sets the start of selection. start is the index of the first chunk of the selection */ f@0: void setStart( size_t start ); f@0: f@3: /** Sets the size of selection. size is the number of chunks the selection is made of */ f@5: void setSize( size_t size ); f@5: f@7: /** The particle spread parameter affects the size of the cloud of particles f@16: * The cloud is the visual counterpart of the grain duration coefficient in sound. f@7: * Indeed spread accepts values from 1 to 8, exactly as the duration coefficient f@7: */ f@0: void inline setParticleSpread( float spread ){ f@0: mParticleSpread = spread; f@5: } f@0: f@5: size_t getStart(void) const { return mSelectionStart; } f@5: f@0: size_t getEnd(void) const { return mSelectionEnd; } f@0: f@5: size_t inline getSize(void) const { f@5: if (mNull) f@5: return 0; f@5: else f@5: return 1 + mSelectionEnd - mSelectionStart; f@5: } f@0: f@5: float inline getParticleSpread() const { return mParticleSpread; } f@0: f@3: /** When selection is null no selection is showed on the wave */ f@5: inline void setToNull(){ f@0: mParticleSpread = 1.0f; f@5: mNull = true; f@5: } f@0: f@5: inline bool isNull() const{ f@5: return mNull; f@5: } f@0: f@5: inline const Color & getColor() const{ f@5: return mColor; f@5: } f@0: f@0: private: f@0: f@0: size_t mSelectionStart; f@0: f@0: size_t mSelectionEnd; f@0: f@0: float mParticleSpread; f@0: f@0: bool mNull = true; f@0: f@0: Color mColor; f@0: f@0: Wave * mWave; f@0: f@5: }; // class Selection f@0: f@5: f@0: f@0: #ifdef USE_PARTICLES f@5: ParticleController mParticleController; f@0: #endif f@0: f@5: f@0: f@5: /* Maps id of the synth to cursor. There is one cursor for each Synth being played */ f@5: std::map < SynthID, Cursor > mCursors; f@16: /** Holds the positions of the cursor, namely on which chunk the cursor is currently on */ f@5: std::vector mCursorsPos; f@0: f@0: public: f@5: f@3: // value used to identify the loop for cursor position f@0: static const int kLoopNote = -1; f@5: static const cinder::Color CURSOR_CLR; f@5: static const int MAX_DURATION = 8; f@0: #ifdef USE_PARTICLES f@5: static const int PARTICLESIZE_COEFF = 40; f@0: #endif f@0: f@16: /** Resetting a wave makes it shrink until it disappears. Each time a new sample is recorded, the wave is reset. f@3: * \param onlyChunks if false the selection is also set to null, if true only the chunks are reset f@3: */ f@5: void reset(bool onlyChunks); f@0: f@3: /** sets top and bottom values for the chunk. f@3: * \a bottom and \a top are in audio coordinates [-1.0, 1.0] f@3: */ f@5: void setChunk(size_t index, float bottom, float top); f@0: f@5: const Chunk & getChunk(size_t index); f@0: f@16: /** Places the cursor on the wave. Every cursor is associated to a synth voice of the audio engine. f@3: * The synth id identifies uniquely the cursor in the internal map of the wave. f@3: * If the cursor doesn't exist it is created */ f@0: inline void setCursorPos( SynthID id, int pos, const DrawInfo& di ){ f@0: f@5: Cursor & cursor = mCursors[id]; f@5: cursor.pos = pos; f@5: cursor.lastUpdate = ci::app::getElapsedSeconds(); f@0: f@0: #ifdef USE_PARTICLES f@16: // The idea is that, if the duration is greater than 1.0, the cursor continues in form of particles. f@16: // The smaller the selection the more particles; the bigger the duration the more particles. f@5: if (mSelection.getParticleSpread() > 1.0f){ f@5: /* amountCoeff ranges from 1/8 to 1 */ f@0: const float amountCoeff = (mSelection.getParticleSpread() / MAX_DURATION); f@0: f@0: /* get radom point within seleciton as center of the particle */ f@16: vec2 centrePoint; f@0: const int randomChunkIndex = ci::Rand::randInt(mSelection.getStart(), mSelection.getEnd() ); f@0: f@0: centrePoint.x = di.flipX( 1 + (randomChunkIndex * (2 + Chunk::kWidth)) + Chunk::kWidth / 2 ); f@0: centrePoint.y = di.flipY( di.audioToHeigt(0.0) ); f@0: f@0: const float wavePixelLen = mNumChunks * ( 2 + Chunk::kWidth); f@5: centrePoint.x *= float(di.getWindowWidth()) / wavePixelLen; f@0: f@5: mParticleController.addParticles( f@0: std::max( 1, (int)(amountCoeff * ParticleController::kMaxParticleAdd * mFilterCoeff) ), // amount of particles to add f@5: centrePoint, f@0: mSelection.getParticleSpread() * PARTICLESIZE_COEFF // size of the cloud f@5: ); f@5: } f@0: #endif f@0: f@5: f@5: } f@0: f@0: void update( double secondsPerChunk, const DrawInfo& di ); f@0: f@0: void removeCursor( SynthID id ) { mCursors.erase( id ); } f@0: f@3: /** Sets the transparency of this wave. \a alpha ranges from 0 to 1 */ f@5: inline void setselectionAlpha(float alpha){ mFilterCoeff = alpha;} f@0: f@0: void draw( const DrawInfo& di ); f@0: f@5: Selection& getSelection() { return mSelection; }; f@0: f@5: size_t getSize() const{ return mChunks.size(); } f@0: f@5: void setScopePoint(int index, float audioVal); f@0: f@0: Wave( size_t numChunks, Color selectionColor ); f@0: f@3: /** no copies */ f@0: Wave( const Wave © ) = delete; f@0: Wave & operator=(const Wave ©) = delete; f@0: f@0: f@0: private: f@0: f@0: const size_t mNumChunks; f@0: f@0: std::vector mChunks; f@0: f@0: Selection mSelection; f@0: f@0: cinder::Color mColor; f@0: f@16: // How much filter is applied in audio. It affects the alpha value of the selection color. f@0: float mFilterCoeff; f@0: f@3: // cinder gl batch for batch drawing f@0: ci::gl::BatchRef mChunkBatch; f@0: f@0: }; f@0: