f@5
|
1 /*
|
f@5
|
2
|
f@5
|
3 Copyright (C) 2015 Fiore Martin
|
f@5
|
4 Copyright (C) 2016 Queen Mary University of London
|
f@5
|
5 Author: Fiore Martin
|
f@5
|
6
|
f@5
|
7 This file is part of Collidoscope.
|
f@5
|
8
|
f@5
|
9 Collidoscope is free software: you can redistribute it and/or modify
|
f@5
|
10 it under the terms of the GNU General Public License as published by
|
f@5
|
11 the Free Software Foundation, either version 3 of the License, or
|
f@5
|
12 (at your option) any later version.
|
f@5
|
13
|
f@5
|
14 This program is distributed in the hope that it will be useful,
|
f@5
|
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
f@5
|
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
f@5
|
17 GNU General Public License for more details.
|
f@5
|
18
|
f@5
|
19 You should have received a copy of the GNU General Public License
|
f@5
|
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
f@5
|
21
|
f@5
|
22 */
|
f@5
|
23
|
f@0
|
24 #include "Wave.h"
|
f@0
|
25 #include "DrawInfo.h"
|
f@0
|
26
|
f@0
|
27
|
f@0
|
28 using namespace ci;
|
f@0
|
29
|
f@0
|
30 Wave::Wave( size_t numChunks, Color selectionColor ):
|
f@0
|
31 mNumChunks( numChunks ),
|
f@5
|
32 mSelection( this, selectionColor ),
|
f@5
|
33 mColor(Color(0.5f, 0.5f, 0.5f)),
|
f@0
|
34 mFilterCoeff( 1.0f )
|
f@0
|
35 {
|
f@5
|
36 mChunks.reserve( numChunks );
|
f@0
|
37
|
f@5
|
38 for ( size_t i = 0; i < numChunks; i++ ){
|
f@5
|
39 mChunks.emplace_back( i );
|
f@5
|
40 }
|
f@0
|
41
|
f@4
|
42 // init cinder batch drawing
|
f@0
|
43 auto lambert = gl::ShaderDef().color();
|
f@0
|
44 gl::GlslProgRef shader = gl::getStockShader( lambert );
|
f@0
|
45 mChunkBatch = gl::Batch::create( geom::Rect( ci::Rectf( 0, 0, Chunk::kWidth, 1 ) ), shader );
|
f@0
|
46 }
|
f@0
|
47
|
f@0
|
48 void Wave::reset( bool onlyChunks )
|
f@0
|
49 {
|
f@5
|
50 for (size_t i = 0; i < getSize(); i++){
|
f@5
|
51 mChunks[i].reset();
|
f@5
|
52 }
|
f@0
|
53
|
f@5
|
54 if (onlyChunks)
|
f@5
|
55 return;
|
f@0
|
56
|
f@5
|
57 mSelection.setToNull();
|
f@0
|
58 }
|
f@0
|
59
|
f@0
|
60
|
f@0
|
61 void Wave::setChunk(size_t index, float bottom, float top)
|
f@0
|
62 {
|
f@5
|
63 Chunk &c = mChunks[index];
|
f@5
|
64 c.setTop(top);
|
f@5
|
65 c.setBottom(bottom);
|
f@0
|
66 }
|
f@0
|
67
|
f@0
|
68 inline const Chunk & Wave::getChunk(size_t index)
|
f@0
|
69 {
|
f@5
|
70 return mChunks[index];
|
f@0
|
71 }
|
f@0
|
72
|
f@0
|
73 void Wave::update( double secondsPerChunk, const DrawInfo& di ) {
|
f@0
|
74 typedef std::map<int, Cursor>::iterator MapItr;
|
f@0
|
75
|
f@0
|
76
|
f@0
|
77 // update the cursor positions
|
f@0
|
78 double now = ci::app::getElapsedSeconds();
|
f@0
|
79 for (MapItr itr = mCursors.begin(); itr != mCursors.end(); ++itr){
|
f@0
|
80 if (mSelection.isNull()){
|
f@0
|
81 itr->second.pos = Cursor::kNoPosition;
|
f@0
|
82 }
|
f@0
|
83
|
f@0
|
84 if ( itr->second.pos == Cursor::kNoPosition )
|
f@0
|
85 continue;
|
f@0
|
86
|
f@0
|
87
|
f@0
|
88 double elapsed = now - itr->second.lastUpdate;
|
f@0
|
89
|
f@16
|
90 // A chunk of audio corresponds to a certain time lenght of audio, according to sample rate.
|
f@16
|
91 // Use elapsed time to advance through chunks so that the cursor is animated.
|
f@16
|
92 // So it goes from start to end of the selection in the time span of the grain
|
f@0
|
93 itr->second.pos = mSelection.getStart() + int( elapsed / secondsPerChunk );
|
f@0
|
94
|
f@4
|
95 // check we don't go too far off
|
f@0
|
96 if (itr->second.pos > mSelection.getEnd()){
|
f@0
|
97 itr->second.pos = Cursor::kNoPosition;
|
f@0
|
98 }
|
f@0
|
99 }
|
f@0
|
100
|
f@0
|
101 // update chunks for animation
|
f@0
|
102 for ( auto &chunk : mChunks ){
|
f@0
|
103 chunk.update( di );
|
f@0
|
104 }
|
f@0
|
105
|
f@0
|
106 #ifdef USE_PARTICLES
|
f@0
|
107 mParticleController.updateParticles();
|
f@0
|
108 #endif
|
f@0
|
109
|
f@0
|
110 }
|
f@0
|
111
|
f@0
|
112 void Wave::draw( const DrawInfo& di ){
|
f@0
|
113
|
f@0
|
114
|
f@5
|
115 /* ########### draw the particles ########## */
|
f@0
|
116 #ifdef USE_PARTICLES
|
f@5
|
117 mParticleController.draw();
|
f@0
|
118 #endif
|
f@0
|
119
|
f@5
|
120 /* ########### draw the wave ########## */
|
f@5
|
121 /* scale the wave to fit the window */
|
f@5
|
122 gl::pushModelView();
|
f@0
|
123
|
f@5
|
124
|
f@5
|
125 const float wavePixelLen = ( mNumChunks * ( 2 + Chunk::kWidth ) );
|
f@5
|
126 /* scale the x-axis for the wave to fit the window precisely */
|
f@5
|
127 gl::scale( ((float)di.getWindowWidth() ) / wavePixelLen , 1.0f);
|
f@5
|
128 /* draw the chunks */
|
f@5
|
129 if (mSelection.isNull()){
|
f@5
|
130 /* no selection: all chunks the same color */
|
f@5
|
131 gl::color(mColor);
|
f@5
|
132 for (size_t i = 0; i < getSize(); i++){
|
f@5
|
133 mChunks[i].draw( di, mChunkBatch );
|
f@5
|
134 }
|
f@5
|
135 }
|
f@0
|
136 else{
|
f@0
|
137 // Selection not null
|
f@5
|
138 gl::color(this->mColor);
|
f@0
|
139
|
f@0
|
140 // update the array with cursor positions
|
f@0
|
141 mCursorsPos.clear();
|
f@0
|
142 for ( auto cursor : mCursors ){
|
f@0
|
143 mCursorsPos.push_back( cursor.second.pos );
|
f@0
|
144 }
|
f@0
|
145
|
f@5
|
146 gl::enableAlphaBlending();
|
f@0
|
147
|
f@5
|
148 const float selectionAlpha = 0.5f + mFilterCoeff * 0.5f;
|
f@0
|
149
|
f@0
|
150
|
f@5
|
151 for (size_t i = 0; i < getSize(); i++){
|
f@5
|
152 /* when in selection use selection color */
|
f@5
|
153
|
f@5
|
154 if (i == mSelection.getStart()){
|
f@5
|
155 /* draw the selection bar with a transparent selection color */
|
f@5
|
156 gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, 0.5f);
|
f@0
|
157 mChunks[i].drawBar( di, mChunkBatch );
|
f@0
|
158
|
f@5
|
159 /* set the color to the selection */
|
f@5
|
160 gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, selectionAlpha);
|
f@5
|
161 }
|
f@0
|
162
|
f@16
|
163 // check if one of the cursors is positioned in this chunk, and draw it white if it is
|
f@5
|
164 if (std::find(mCursorsPos.begin(), mCursorsPos.end(),i) != mCursorsPos.end() ){
|
f@5
|
165 gl::color(CURSOR_CLR);
|
f@5
|
166 mChunks[i].draw( di, mChunkBatch );
|
f@5
|
167 gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, selectionAlpha);
|
f@5
|
168 }
|
f@5
|
169 else{
|
f@5
|
170 /* just draw with current color */
|
f@5
|
171 mChunks[i].draw( di, mChunkBatch );
|
f@5
|
172 }
|
f@5
|
173
|
f@5
|
174 /* exit selection: go back to wave color */
|
f@5
|
175 if (i == mSelection.getEnd()){
|
f@5
|
176 /* draw the selection bar with a transparent selection color */
|
f@5
|
177 gl::color(mSelection.getColor().r, mSelection.getColor().g, mSelection.getColor().b, 0.5f);
|
f@0
|
178 mChunks[i].drawBar( di, mChunkBatch );
|
f@16
|
179 /* set the color to the wave */
|
f@5
|
180 gl::color(this->mColor);
|
f@5
|
181 }
|
f@5
|
182 }
|
f@5
|
183 gl::disableAlphaBlending();
|
f@5
|
184 }
|
f@5
|
185
|
f@0
|
186
|
f@5
|
187 gl::popModelView();
|
f@0
|
188
|
f@0
|
189 }
|
f@0
|
190
|
f@0
|
191
|
f@0
|
192
|
f@0
|
193 //**************** Selection ***************//
|
f@0
|
194
|
f@0
|
195 Wave::Selection::Selection(Wave * w, Color color) :
|
f@0
|
196 mWave( w ),
|
f@0
|
197 mSelectionStart( 0 ),
|
f@0
|
198 mSelectionEnd( 0 ),
|
f@0
|
199 mColor( color ),
|
f@0
|
200 mParticleSpread( 1 )
|
f@0
|
201 {}
|
f@0
|
202
|
f@0
|
203
|
f@0
|
204 void Wave::Selection::setStart(size_t start) {
|
f@0
|
205
|
f@5
|
206 /* deselect the previous */
|
f@0
|
207 mWave->mChunks[mSelectionStart].setAsSelectionStart( false );
|
f@5
|
208 /* select the next */
|
f@0
|
209 mWave->mChunks[start].setAsSelectionStart( true );
|
f@5
|
210
|
f@5
|
211 mNull = false;
|
f@0
|
212
|
f@0
|
213 size_t size = getSize();
|
f@0
|
214
|
f@5
|
215 mSelectionStart = start;
|
f@0
|
216 mSelectionEnd = start + size - 1;
|
f@0
|
217 if ( mSelectionEnd > mWave->getSize() - 1 )
|
f@0
|
218 mSelectionEnd = mWave->getSize() - 1;
|
f@0
|
219
|
f@0
|
220 }
|
f@0
|
221
|
f@0
|
222 void Wave::Selection::setSize(size_t size) {
|
f@0
|
223
|
f@0
|
224 if ( size <= 0 ){
|
f@0
|
225 mNull = true;
|
f@0
|
226 return;
|
f@0
|
227 }
|
f@0
|
228
|
f@0
|
229 size -= 1;
|
f@0
|
230
|
f@0
|
231 // check boundaries: size cannot bring the selection end beyond the end of the wave
|
f@0
|
232 if ( mSelectionStart+size >= mWave->mNumChunks ){
|
f@0
|
233 size = mWave->mNumChunks - mSelectionStart - 1;
|
f@0
|
234 }
|
f@0
|
235
|
f@5
|
236 /* deselect the previous */
|
f@0
|
237 mWave->mChunks[mSelectionEnd].setAsSelectionEnd( false );
|
f@0
|
238
|
f@0
|
239 mSelectionEnd = mSelectionStart + size;
|
f@5
|
240 /* select the next */
|
f@0
|
241 mWave->mChunks[mSelectionEnd].setAsSelectionEnd( true );
|
f@0
|
242
|
f@5
|
243 mNull = false;
|
f@0
|
244 }
|
f@0
|
245
|
f@0
|
246
|
f@0
|
247 const cinder::Color Wave::CURSOR_CLR = Color(1.f, 1.f, 1.f);
|
f@0
|
248
|
f@0
|
249
|