Mercurial > hg > ugly-duckling
comparison src/app/waveform/waveform.component.ts @ 110:9890436bcc9a
Bodge in okay-ish pan and zoom, with a lot of dupe from CenteredZoomState and state flying about everywhere.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Fri, 10 Mar 2017 18:24:52 +0000 |
parents | 68fe21cfda2a |
children | 689c1bfe8e68 |
comparison
equal
deleted
inserted
replaced
109:68fe21cfda2a | 110:9890436bcc9a |
---|---|
50 | 50 |
51 private featureExtractionSubscription: Subscription; | 51 private featureExtractionSubscription: Subscription; |
52 private playingStateSubscription: Subscription; | 52 private playingStateSubscription: Subscription; |
53 private seekedSubscription: Subscription; | 53 private seekedSubscription: Subscription; |
54 private isPlaying: boolean; | 54 private isPlaying: boolean; |
55 private offsetAtPanStart: number; | |
56 private initialZoom: number; | |
57 private initialDistance: number; | |
55 | 58 |
56 constructor(private audioService: AudioPlayerService, | 59 constructor(private audioService: AudioPlayerService, |
57 private piperService: FeatureExtractionService, | 60 private piperService: FeatureExtractionService, |
58 public ngZone: NgZone) { | 61 public ngZone: NgZone) { |
59 this.colouredLayers = new Map(); | 62 this.colouredLayers = new Map(); |
179 Math.round(c0[1] * prop0 + c1[1] * prop1), | 182 Math.round(c0[1] * prop0 + c1[1] * prop1), |
180 Math.round(c0[2] * prop0 + c1[2] * prop1), | 183 Math.round(c0[2] * prop0 + c1[2] * prop1), |
181 255 ]; | 184 255 ]; |
182 }); | 185 }); |
183 } | 186 } |
184 | 187 |
185 iceMapper() { | 188 iceMapper() { |
186 let hexColours = [ | 189 let hexColours = [ |
187 // Based on ColorBrewer ylGnBu | 190 // Based on ColorBrewer ylGnBu |
188 "ffffff", "ffff00", "f7fcf0", "e0f3db", "ccebc5", "a8ddb5", | 191 "ffffff", "ffff00", "f7fcf0", "e0f3db", "ccebc5", "a8ddb5", |
189 "7bccc4", "4eb3d3", "2b8cbe", "0868ac", "084081", "042040" | 192 "7bccc4", "4eb3d3", "2b8cbe", "0868ac", "084081", "042040" |
190 ]; | 193 ]; |
191 hexColours.reverse(); | 194 hexColours.reverse(); |
192 return this.interpolatingMapper(hexColours); | 195 return this.interpolatingMapper(hexColours); |
193 } | 196 } |
194 | 197 |
195 renderWaveform(buffer: AudioBuffer): void { | 198 renderWaveform(buffer: AudioBuffer): void { |
196 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height; | 199 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height; |
197 const mainTrack = this.timeline.getTrackById('main'); | 200 const mainTrack = this.timeline.getTrackById('main'); |
198 if (this.timeline) { | 201 if (this.timeline) { |
199 // resize | 202 // resize |
240 height: height * 0.9, | 243 height: height * 0.9, |
241 stepSize: 512, | 244 stepSize: 512, |
242 fftSize: 1024 | 245 fftSize: 1024 |
243 }); | 246 }); |
244 this.addLayer(spectrogramLayer, mainTrack, this.timeline.timeContext); | 247 this.addLayer(spectrogramLayer, mainTrack, this.timeline.timeContext); |
245 */ | 248 */ |
246 this.cursorLayer = new wavesUI.helpers.CursorLayer({ | 249 this.cursorLayer = new wavesUI.helpers.CursorLayer({ |
247 height: height | 250 height: height |
248 }); | 251 }); |
249 this.addLayer(this.cursorLayer, mainTrack, this.timeline.timeContext); | 252 this.addLayer(this.cursorLayer, mainTrack, this.timeline.timeContext); |
250 this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline); | 253 this.timeline.state = new wavesUI.states.CenteredZoomState(this.timeline); |
251 mainTrack.render(); | 254 mainTrack.render(); |
252 mainTrack.update(); | 255 mainTrack.update(); |
253 | 256 |
254 | 257 |
255 if ('ontouchstart' in window) { | 258 if ('ontouchstart' in window) { |
259 interface Point { | |
260 x: number; | |
261 y: number; | |
262 } | |
263 | |
264 const pixelToExponent: Function = wavesUI.utils.scales.linear() | |
265 .domain([0, 100]) // 100px => factor 2 | |
266 .range([0, 1]); | |
267 | |
268 const calculateDistance: (p1: Point, p2: Point) => number = (p1, p2) => { | |
269 return Math.pow( | |
270 Math.pow(p2.x - p1.x, 2) + | |
271 Math.pow(p2.y - p1.y, 2), 0.5); | |
272 }; | |
273 | |
256 const hammertime = new Hammer(this.trackDiv.nativeElement); | 274 const hammertime = new Hammer(this.trackDiv.nativeElement); |
257 const scroll = (ev) => { | 275 const scroll = (ev) => { |
258 const sign = ev.direction === Hammer.DIRECTION_LEFT ? -1 : 1; | 276 this.timeline.timeContext.offset = this.offsetAtPanStart + |
259 let delta = this.timeline.timeContext.timeToPixel.invert(sign * ev.distance); | 277 this.timeline.timeContext.timeToPixel.invert(ev.deltaX); |
260 const speed: number = Math.abs(ev.velocityX); | |
261 delta *= (speed > 0.075 ? 0.075 : speed); // this is completely made up to limit the max speed, TODO something sensible | |
262 this.timeline.timeContext.offset += delta; | |
263 this.timeline.tracks.update(); | 278 this.timeline.tracks.update(); |
264 }; | 279 }; |
265 | 280 |
266 const zoom = (ev) => { | 281 const zoom = (ev) => { |
267 const minZoom = this.timeline.state.minZoom; | 282 const minZoom = this.timeline.state.minZoom; |
268 const maxZoom = this.timeline.state.maxZoom; | 283 const maxZoom = this.timeline.state.maxZoom; |
269 const initialZoom = this.timeline.timeContext.zoom; | 284 const distance = calculateDistance({ |
270 const targetZoom = initialZoom * ev.scale; | 285 x: ev.pointers[0].clientX, |
271 const lastCenterTime = this.timeline.timeContext.timeToPixel.invert(ev.center.x); | 286 y: ev.pointers[0].clientY |
272 this.timeline.timeContext.zoom = Math.min(Math.max(targetZoom, minZoom), maxZoom); | 287 }, { |
273 const newCenterTime = this.timeline.timeContext.timeToPixel.invert(ev.center.x); | 288 x: ev.pointers[1].clientX, |
289 y: ev.pointers[1].clientY | |
290 }); | |
291 | |
292 const lastCenterTime = | |
293 this.timeline.timeContext.timeToPixel.invert(ev.center.x); | |
294 | |
295 const exponent = pixelToExponent(distance - this.initialDistance); | |
296 const targetZoom = this.initialZoom * Math.pow(2, exponent); | |
297 | |
298 this.timeline.timeContext.zoom = | |
299 Math.min(Math.max(targetZoom, minZoom), maxZoom); | |
300 | |
301 const newCenterTime = | |
302 this.timeline.timeContext.timeToPixel.invert(ev.center.x); | |
303 | |
274 this.timeline.timeContext.offset += newCenterTime - lastCenterTime; | 304 this.timeline.timeContext.offset += newCenterTime - lastCenterTime; |
275 this.timeline.tracks.update(); | 305 this.timeline.tracks.update(); |
276 }; | 306 }; |
277 const seek = (ev) => { | 307 const seek = (ev) => { |
278 this.audioService.seekTo( | 308 this.audioService.seekTo( |
279 this.timeline.timeContext.timeToPixel.invert(ev.center.x) - this.timeline.timeContext.offset | 309 this.timeline.timeContext.timeToPixel.invert(ev.center.x) - this.timeline.timeContext.offset |
280 ); | 310 ); |
281 }; | 311 }; |
282 hammertime.get('pinch').set({ enable: true }); | 312 hammertime.get('pinch').set({ enable: true }); |
313 hammertime.on('panstart', () => { | |
314 this.offsetAtPanStart = this.timeline.timeContext.offset; | |
315 }); | |
283 hammertime.on('panleft', scroll); | 316 hammertime.on('panleft', scroll); |
284 hammertime.on('panright', scroll); | 317 hammertime.on('panright', scroll); |
318 hammertime.on('pinchstart', (e) => { | |
319 this.initialZoom = this.timeline.timeContext.zoom; | |
320 | |
321 this.initialDistance = calculateDistance({ | |
322 x: e.pointers[0].clientX, | |
323 y: e.pointers[0].clientY | |
324 }, { | |
325 x: e.pointers[1].clientX, | |
326 y: e.pointers[1].clientY | |
327 }); | |
328 }); | |
285 hammertime.on('pinch', zoom); | 329 hammertime.on('pinch', zoom); |
286 hammertime.on('tap', seek); | 330 hammertime.on('tap', seek); |
287 } | 331 } |
288 | 332 |
289 this.animate(); | 333 this.animate(); |