Mercurial > hg > ugly-duckling
comparison src/app/waveform/waveform.component.ts @ 118:5a4cd7add25a
Update colour mappers to slightly changed API; add green & sunset
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Tue, 14 Mar 2017 11:57:21 +0000 |
parents | b8627a18c72d |
children | 7170e6ca2206 |
comparison
equal
deleted
inserted
replaced
117:b8627a18c72d | 118:5a4cd7add25a |
---|---|
158 } | 158 } |
159 | 159 |
160 interpolatingMapper(hexColours) { | 160 interpolatingMapper(hexColours) { |
161 const colours = hexColours.map(n => { | 161 const colours = hexColours.map(n => { |
162 const i = parseInt(n, 16); | 162 const i = parseInt(n, 16); |
163 return [ (i >> 16) & 255, (i >> 8) & 255, i & 255, 255 ]; | 163 return [ ((i >> 16) & 255) / 255.0, |
164 ((i >> 8) & 255) / 255.0, | |
165 ((i) & 255) / 255.0 ]; | |
164 }); | 166 }); |
165 const last = colours.length - 1; | 167 const last = colours.length - 1; |
166 return (value => { | 168 return (value => { |
167 // value must be in the range [0,1]. We quantize to 256 levels, | |
168 // as the PNG encoder deep inside uses a limited palette for | |
169 // simplicity. Should document this for the mapper. Also that | |
170 // individual colour values should be integers | |
171 value = Math.round(value * 255) / 255; | |
172 const m = value * last; | 169 const m = value * last; |
173 if (m >= last) { | 170 if (m >= last) { |
174 return colours[last]; | 171 return colours[last]; |
175 } | 172 } |
176 if (m <= 0) { | 173 if (m <= 0) { |
179 const base = Math.floor(m); | 176 const base = Math.floor(m); |
180 const prop0 = base + 1.0 - m; | 177 const prop0 = base + 1.0 - m; |
181 const prop1 = m - base; | 178 const prop1 = m - base; |
182 const c0 = colours[base]; | 179 const c0 = colours[base]; |
183 const c1 = colours[base+1]; | 180 const c1 = colours[base+1]; |
184 return [ Math.round(c0[0] * prop0 + c1[0] * prop1), | 181 return [ c0[0] * prop0 + c1[0] * prop1, |
185 Math.round(c0[1] * prop0 + c1[1] * prop1), | 182 c0[1] * prop0 + c1[1] * prop1, |
186 Math.round(c0[2] * prop0 + c1[2] * prop1), | 183 c0[2] * prop0 + c1[2] * prop1 ]; |
187 255 ]; | |
188 }); | 184 }); |
189 } | 185 } |
190 | 186 |
191 iceMapper() { | 187 iceMapper() { |
192 let hexColours = [ | 188 let hexColours = [ |
194 "ffffff", "ffff00", "f7fcf0", "e0f3db", "ccebc5", "a8ddb5", | 190 "ffffff", "ffff00", "f7fcf0", "e0f3db", "ccebc5", "a8ddb5", |
195 "7bccc4", "4eb3d3", "2b8cbe", "0868ac", "084081", "042040" | 191 "7bccc4", "4eb3d3", "2b8cbe", "0868ac", "084081", "042040" |
196 ]; | 192 ]; |
197 hexColours.reverse(); | 193 hexColours.reverse(); |
198 return this.interpolatingMapper(hexColours); | 194 return this.interpolatingMapper(hexColours); |
195 } | |
196 | |
197 hsv2rgb(h, s, v) { // all values in range [0, 1] | |
198 const i = Math.floor(h * 6); | |
199 const f = h * 6 - i; | |
200 const p = v * (1 - s); | |
201 const q = v * (1 - f * s); | |
202 const t = v * (1 - (1 - f) * s); | |
203 let r = 0, g = 0, b = 0; | |
204 switch (i % 6) { | |
205 case 0: r = v, g = t, b = p; break; | |
206 case 1: r = q, g = v, b = p; break; | |
207 case 2: r = p, g = v, b = t; break; | |
208 case 3: r = p, g = q, b = v; break; | |
209 case 4: r = t, g = p, b = v; break; | |
210 case 5: r = v, g = p, b = q; break; | |
211 } | |
212 return [ r, g, b ]; | |
213 } | |
214 | |
215 greenMapper() { | |
216 const blue = 0.6666; | |
217 const pieslice = 0.3333; | |
218 return (value => { | |
219 const h = blue - value * 2.0 * pieslice; | |
220 const s = 0.5 + value / 2.0; | |
221 const v = value; | |
222 return this.hsv2rgb(h, s, v); | |
223 }); | |
224 } | |
225 | |
226 sunsetMapper() { | |
227 return (value => { | |
228 let r = (value - 0.24) * 2.38; | |
229 let g = (value - 0.64) * 2.777; | |
230 let b = (3.6 * value); | |
231 if (value > 0.277) b = 2.0 - b; | |
232 return [ r, g, b ]; | |
233 }); | |
199 } | 234 } |
200 | 235 |
201 renderWaveform(buffer: AudioBuffer): void { | 236 renderWaveform(buffer: AudioBuffer): void { |
202 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; | 237 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; |
203 const waveTrack = this.timeline.getTrackById('wave'); | 238 const waveTrack = this.timeline.getTrackById('wave'); |
344 renderSpectrogram(buffer: AudioBuffer): void { | 379 renderSpectrogram(buffer: AudioBuffer): void { |
345 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; | 380 const height: number = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; |
346 const gridTrack = this.timeline.getTrackById('grid'); | 381 const gridTrack = this.timeline.getTrackById('grid'); |
347 | 382 |
348 const spectrogramLayer = new wavesUI.helpers.SpectrogramLayer(buffer, { | 383 const spectrogramLayer = new wavesUI.helpers.SpectrogramLayer(buffer, { |
349 top: 10, | 384 top: height * 0.05, |
350 height: height * 0.9, | 385 height: height * 0.9, |
351 stepSize: 512, | 386 stepSize: 512, |
352 fftSize: 1024 | 387 fftSize: 1024, |
388 normalise: 'none', | |
389 mapper: this.sunsetMapper() | |
353 }); | 390 }); |
354 this.addLayer(spectrogramLayer, gridTrack, this.timeline.timeContext); | 391 this.addLayer(spectrogramLayer, gridTrack, this.timeline.timeContext); |
355 | 392 |
356 this.timeline.tracks.update(); | 393 this.timeline.tracks.update(); |
357 } | 394 } |
360 private renderFeatures(extracted: SimpleResponse, colour: Colour): void { | 397 private renderFeatures(extracted: SimpleResponse, colour: Colour): void { |
361 if (!extracted.hasOwnProperty('features') || !extracted.hasOwnProperty('outputDescriptor')) return; | 398 if (!extracted.hasOwnProperty('features') || !extracted.hasOwnProperty('outputDescriptor')) return; |
362 if (!extracted.features.hasOwnProperty('shape') || !extracted.features.hasOwnProperty('data')) return; | 399 if (!extracted.features.hasOwnProperty('shape') || !extracted.features.hasOwnProperty('data')) return; |
363 const features: FeatureCollection = (extracted.features as FeatureCollection); | 400 const features: FeatureCollection = (extracted.features as FeatureCollection); |
364 const outputDescriptor = extracted.outputDescriptor; | 401 const outputDescriptor = extracted.outputDescriptor; |
365 const height = this.trackDiv.nativeElement.getBoundingClientRect().height; | 402 const height = this.trackDiv.nativeElement.getBoundingClientRect().height / 2; |
366 const waveTrack = this.timeline.getTrackById('main'); | 403 const waveTrack = this.timeline.getTrackById('wave'); |
367 | 404 |
368 // TODO refactor all of this | 405 // TODO refactor all of this |
369 switch (features.shape) { | 406 switch (features.shape) { |
370 case 'vector': { | 407 case 'vector': { |
371 const stepDuration = (features as FixedSpacedFeatures).stepDuration; | 408 const stepDuration = (features as FixedSpacedFeatures).stepDuration; |
500 const gain = (targetValue > 0.0 ? (1.0 / targetValue) : 1.0); | 537 const gain = (targetValue > 0.0 ? (1.0 / targetValue) : 1.0); |
501 console.log("setting gain to " + gain); | 538 console.log("setting gain to " + gain); |
502 const matrixEntity = new wavesUI.utils.PrefilledMatrixEntity(matrixData); | 539 const matrixEntity = new wavesUI.utils.PrefilledMatrixEntity(matrixData); |
503 let matrixLayer = new wavesUI.helpers.MatrixLayer(matrixEntity, { | 540 let matrixLayer = new wavesUI.helpers.MatrixLayer(matrixEntity, { |
504 gain, | 541 gain, |
505 height: height * 0.8, | 542 height: height * 0.9, |
506 top: height * 0.1, | 543 top: height * 0.05, |
507 normalise: 'none', | 544 normalise: 'none', |
508 mapper: this.iceMapper() | 545 mapper: this.iceMapper() |
509 }); | 546 }); |
510 this.colouredLayers.set(this.addLayer( | 547 this.colouredLayers.set(this.addLayer( |
511 matrixLayer, | 548 matrixLayer, |