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@5:
f@0: #include "Wave.h"
f@0: #include "DrawInfo.h"
f@0:
f@0:
f@0: using namespace ci;
f@0:
f@0: Wave::Wave( size_t numChunks, Color selectionColor ):
f@0: mNumChunks( numChunks ),
f@5: mSelection( this, selectionColor ),
f@5: mColor(Color(0.5f, 0.5f, 0.5f)),
f@0: mFilterCoeff( 1.0f )
f@0: {
f@5: mChunks.reserve( numChunks );
f@0:
f@5: for ( size_t i = 0; i < numChunks; i++ ){
f@5: mChunks.emplace_back( i );
f@5: }
f@0:
f@4: // init cinder batch drawing
f@0: auto lambert = gl::ShaderDef().color();
f@0: gl::GlslProgRef shader = gl::getStockShader( lambert );
f@0: mChunkBatch = gl::Batch::create( geom::Rect( ci::Rectf( 0, 0, Chunk::kWidth, 1 ) ), shader );
f@0: }
f@0:
f@0: void Wave::reset( bool onlyChunks )
f@0: {
f@5: for (size_t i = 0; i < getSize(); i++){
f@5: mChunks[i].reset();
f@5: }
f@0:
f@5: if (onlyChunks)
f@5: return;
f@0:
f@5: mSelection.setToNull();
f@0: }
f@0:
f@0:
f@0: void Wave::setChunk(size_t index, float bottom, float top)
f@0: {
f@5: Chunk &c = mChunks[index];
f@5: c.setTop(top);
f@5: c.setBottom(bottom);
f@0: }
f@0:
f@0: inline const Chunk & Wave::getChunk(size_t index)
f@0: {
f@5: return mChunks[index];
f@0: }
f@0:
f@0: void Wave::update( double secondsPerChunk, const DrawInfo& di ) {
f@0: typedef std::map::iterator MapItr;
f@0:
f@0:
f@0: // update the cursor positions
f@0: double now = ci::app::getElapsedSeconds();
f@0: for (MapItr itr = mCursors.begin(); itr != mCursors.end(); ++itr){
f@0: if (mSelection.isNull()){
f@0: itr->second.pos = Cursor::kNoPosition;
f@0: }
f@0:
f@0: if ( itr->second.pos == Cursor::kNoPosition )
f@0: continue;
f@0:
f@0:
f@0: double elapsed = now - itr->second.lastUpdate;
f@0:
f@16: // A chunk of audio corresponds to a certain time lenght of audio, according to sample rate.
f@16: // Use elapsed time to advance through chunks so that the cursor is animated.
f@16: // So it goes from start to end of the selection in the time span of the grain
f@0: itr->second.pos = mSelection.getStart() + int( elapsed / secondsPerChunk );
f@0:
f@4: // check we don't go too far off
f@0: if (itr->second.pos > mSelection.getEnd()){
f@0: itr->second.pos = Cursor::kNoPosition;
f@0: }
f@0: }
f@0:
f@0: // update chunks for animation
f@0: for ( auto &chunk : mChunks ){
f@0: chunk.update( di );
f@0: }
f@0:
f@0: #ifdef USE_PARTICLES
f@0: mParticleController.updateParticles();
f@0: #endif
f@0:
f@0: }
f@0:
f@0: void Wave::draw( const DrawInfo& di ){
f@0:
f@0:
f@5: /* ########### draw the particles ########## */
f@0: #ifdef USE_PARTICLES
f@5: mParticleController.draw();
f@0: #endif
f@0:
f@5: /* ########### draw the wave ########## */
f@5: /* scale the wave to fit the window */
f@5: gl::pushModelView();
f@0:
f@5:
f@5: const float wavePixelLen = ( mNumChunks * ( 2 + Chunk::kWidth ) );
f@5: /* scale the x-axis for the wave to fit the window precisely */
f@5: gl::scale( ((float)di.getWindowWidth() ) / wavePixelLen , 1.0f);
f@5: /* draw the chunks */
f@5: if (mSelection.isNull()){
f@5: /* no selection: all chunks the same color */
f@5: gl::color(mColor);
f@5: for (size_t i = 0; i < getSize(); i++){
f@5: mChunks[i].draw( di, mChunkBatch );
f@5: }
f@5: }
f@0: else{
f@0: // Selection not null
f@5: gl::color(this->mColor);
f@0:
f@0: // update the array with cursor positions
f@0: mCursorsPos.clear();
f@0: for ( auto cursor : mCursors ){
f@0: mCursorsPos.push_back( cursor.second.pos );
f@0: }
f@0:
f@5: gl::enableAlphaBlending();
f@0:
f@5: const float selectionAlpha = 0.5f + mFilterCoeff * 0.5f;
f@0:
f@0:
f@5: for (size_t i = 0; i < getSize(); i++){
f@5: /* when in selection use selection color */
f@5:
f@5: if (i == mSelection.getStart()){
f@5: /* draw the selection bar with a transparent selection color */
f@5: gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, 0.5f);
f@0: mChunks[i].drawBar( di, mChunkBatch );
f@0:
f@5: /* set the color to the selection */
f@5: gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, selectionAlpha);
f@5: }
f@0:
f@16: // check if one of the cursors is positioned in this chunk, and draw it white if it is
f@5: if (std::find(mCursorsPos.begin(), mCursorsPos.end(),i) != mCursorsPos.end() ){
f@5: gl::color(CURSOR_CLR);
f@5: mChunks[i].draw( di, mChunkBatch );
f@5: gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, selectionAlpha);
f@5: }
f@5: else{
f@5: /* just draw with current color */
f@5: mChunks[i].draw( di, mChunkBatch );
f@5: }
f@5:
f@5: /* exit selection: go back to wave color */
f@5: if (i == mSelection.getEnd()){
f@5: /* draw the selection bar with a transparent selection color */
f@5: gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, 0.5f);
f@0: mChunks[i].drawBar( di, mChunkBatch );
f@16: /* set the color to the wave */
f@5: gl::color(this->mColor);
f@5: }
f@5: }
f@5: gl::disableAlphaBlending();
f@5: }
f@5:
f@0:
f@5: gl::popModelView();
f@0:
f@0: }
f@0:
f@0:
f@0:
f@0: //**************** Selection ***************//
f@0:
f@0: Wave::Selection::Selection(Wave * w, Color color) :
f@0: mWave( w ),
f@0: mSelectionStart( 0 ),
f@0: mSelectionEnd( 0 ),
f@0: mColor( color ),
f@0: mParticleSpread( 1 )
f@0: {}
f@0:
f@0:
f@0: void Wave::Selection::setStart(size_t start) {
f@0:
f@5: /* deselect the previous */
f@0: mWave->mChunks[mSelectionStart].setAsSelectionStart( false );
f@5: /* select the next */
f@0: mWave->mChunks[start].setAsSelectionStart( true );
f@5:
f@5: mNull = false;
f@0:
f@0: size_t size = getSize();
f@0:
f@5: mSelectionStart = start;
f@0: mSelectionEnd = start + size - 1;
f@0: if ( mSelectionEnd > mWave->getSize() - 1 )
f@0: mSelectionEnd = mWave->getSize() - 1;
f@0:
f@0: }
f@0:
f@0: void Wave::Selection::setSize(size_t size) {
f@0:
f@0: if ( size <= 0 ){
f@0: mNull = true;
f@0: return;
f@0: }
f@0:
f@0: size -= 1;
f@0:
f@0: // check boundaries: size cannot bring the selection end beyond the end of the wave
f@0: if ( mSelectionStart+size >= mWave->mNumChunks ){
f@0: size = mWave->mNumChunks - mSelectionStart - 1;
f@0: }
f@0:
f@5: /* deselect the previous */
f@0: mWave->mChunks[mSelectionEnd].setAsSelectionEnd( false );
f@0:
f@0: mSelectionEnd = mSelectionStart + size;
f@5: /* select the next */
f@0: mWave->mChunks[mSelectionEnd].setAsSelectionEnd( true );
f@0:
f@5: mNull = false;
f@0: }
f@0:
f@0:
f@0: const cinder::Color Wave::CURSOR_CLR = Color(1.f, 1.f, 1.f);
f@0:
f@0: