Mercurial > hg > ugly-duckling
changeset 343:8bfd9586c78a
Move some functions out of waveform and into appropriately named modules.
author | Lucas Thompson <dev@lucas.im> |
---|---|
date | Tue, 23 May 2017 10:41:36 +0100 |
parents | b5f2ee789fb3 |
children | 7b099900f049 |
files | src/app/spectrogram/ColourMap.ts src/app/spectrogram/MatrixUtils.ts src/app/waveform/waveform.component.ts |
diffstat | 3 files changed, 137 insertions(+), 131 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/app/spectrogram/ColourMap.ts Tue May 23 10:41:36 2017 +0100 @@ -0,0 +1,81 @@ +/** + * Created by lucast on 23/05/2017. + */ + +export function interpolatingMapper(hexColours) { + const colours = hexColours.map(n => { + const i = parseInt(n, 16); + return [ ((i >> 16) & 255) / 255.0, + ((i >> 8) & 255) / 255.0, + ((i) & 255) / 255.0 ]; + }); + const last = colours.length - 1; + return (value => { + const m = value * last; + if (m >= last) { + return colours[last]; + } + if (m <= 0) { + return colours[0]; + } + const base = Math.floor(m); + const prop0 = base + 1.0 - m; + const prop1 = m - base; + const c0 = colours[base]; + const c1 = colours[base + 1]; + return [ c0[0] * prop0 + c1[0] * prop1, + c0[1] * prop0 + c1[1] * prop1, + c0[2] * prop0 + c1[2] * prop1 ]; + }); +} + +export function iceMapper() { + const hexColours = [ + // Based on ColorBrewer ylGnBu + 'ffffff', 'ffff00', 'f7fcf0', 'e0f3db', 'ccebc5', 'a8ddb5', + '7bccc4', '4eb3d3', '2b8cbe', '0868ac', '084081', '042040' + ]; + hexColours.reverse(); + return interpolatingMapper(hexColours); +} + +export function greenMapper() { + const blue = 0.6666; + const pieslice = 0.3333; + return (value => { + const h = blue - value * 2.0 * pieslice; + const s = 0.5 + value / 2.0; + const v = value; + return this.hsv2rgb(h, s, v); + }); +} + +export function sunsetMapper() { + return (value => { + const r = (value - 0.24) * 2.38; + const g = (value - 0.64) * 2.777; + let b = (3.6 * value); + if (value > 0.277) { + b = 2.0 - b; + } + return [ r, g, b ]; + }); +} + +export function hsv2rgb(h, s, v) { // all values in range [0, 1] + const i = Math.floor(h * 6); + const f = h * 6 - i; + const p = v * (1 - s); + const q = v * (1 - f * s); + const t = v * (1 - (1 - f) * s); + let r = 0, g = 0, b = 0; + switch (i % 6) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + return [ r, g, b ]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/app/spectrogram/MatrixUtils.ts Tue May 23 10:41:36 2017 +0100 @@ -0,0 +1,39 @@ +/** + * Created by lucast on 23/05/2017. + */ + +export function estimatePercentile(matrix, percentile) { + // our sample is not evenly distributed across the whole data set: + // it is guaranteed to include at least one sample from every + // column, and could sample some values more than once. But it + // should be good enough in most cases (todo: show this) + if (matrix.length === 0) { + return 0.0; + } + const w = matrix.length; + const h = matrix[0].length; + const n = w * h; + const m = (n > 50000 ? 50000 : n); // should base that on the %ile + let m_per = Math.floor(m / w); + if (m_per < 1) { + m_per = 1; + } + + const sample = []; + for (let x = 0; x < w; ++x) { + for (let i = 0; i < m_per; ++i) { + const y = Math.floor(Math.random() * h); + const value = matrix[x][y]; + if (!isNaN(value) && value !== Infinity) { + sample.push(value); + } + } + } + if (sample.length === 0) { + return 0.0; + } + sample.sort((a, b) => { return a - b; }); + const ix = Math.floor((sample.length * percentile) / 100); + const estimate = sample[ix]; + return estimate; +}
--- a/src/app/waveform/waveform.component.ts Tue May 23 10:25:54 2017 +0100 +++ b/src/app/waveform/waveform.component.ts Tue May 23 10:41:36 2017 +0100 @@ -29,6 +29,8 @@ import {FeatureList, Feature} from 'piper/Feature'; import * as Hammer from 'hammerjs'; import {WavesSpectrogramLayer} from '../spectrogram/Spectrogram'; +import {iceMapper, sunsetMapper} from 'app/spectrogram/ColourMap'; +import {estimatePercentile} from '../spectrogram/MatrixUtils'; type Layer = any; type Track = any; @@ -407,127 +409,6 @@ // this.timeline.createTrack(track, height/2, `grid-${this.trackIdPrefix}`); } - estimatePercentile(matrix, percentile) { - // our sample is not evenly distributed across the whole data set: - // it is guaranteed to include at least one sample from every - // column, and could sample some values more than once. But it - // should be good enough in most cases (todo: show this) - if (matrix.length === 0) { - return 0.0; - } - const w = matrix.length; - const h = matrix[0].length; - const n = w * h; - const m = (n > 50000 ? 50000 : n); // should base that on the %ile - let m_per = Math.floor(m / w); - if (m_per < 1) { - m_per = 1; - } - - const sample = []; - for (let x = 0; x < w; ++x) { - for (let i = 0; i < m_per; ++i) { - const y = Math.floor(Math.random() * h); - const value = matrix[x][y]; - if (!isNaN(value) && value !== Infinity) { - sample.push(value); - } - } - } - if (sample.length === 0) { - console.log('WARNING: No samples gathered, even though we hoped for ' + - (m_per * w) + ' of them'); - return 0.0; - } - sample.sort((a, b) => { return a - b; }); - const ix = Math.floor((sample.length * percentile) / 100); - console.log('Estimating ' + percentile + '-%ile of ' + - n + '-sample dataset (' + w + ' x ' + h + ') as value ' + ix + - ' of sorted ' + sample.length + '-sample subset'); - const estimate = sample[ix]; - console.log('Estimate is: ' + estimate + ' (where min sampled value = ' + - sample[0] + ' and max = ' + sample[sample.length - 1] + ')'); - return estimate; - } - - interpolatingMapper(hexColours) { - const colours = hexColours.map(n => { - const i = parseInt(n, 16); - return [ ((i >> 16) & 255) / 255.0, - ((i >> 8) & 255) / 255.0, - ((i) & 255) / 255.0 ]; - }); - const last = colours.length - 1; - return (value => { - const m = value * last; - if (m >= last) { - return colours[last]; - } - if (m <= 0) { - return colours[0]; - } - const base = Math.floor(m); - const prop0 = base + 1.0 - m; - const prop1 = m - base; - const c0 = colours[base]; - const c1 = colours[base + 1]; - return [ c0[0] * prop0 + c1[0] * prop1, - c0[1] * prop0 + c1[1] * prop1, - c0[2] * prop0 + c1[2] * prop1 ]; - }); - } - - iceMapper() { - const hexColours = [ - // Based on ColorBrewer ylGnBu - 'ffffff', 'ffff00', 'f7fcf0', 'e0f3db', 'ccebc5', 'a8ddb5', - '7bccc4', '4eb3d3', '2b8cbe', '0868ac', '084081', '042040' - ]; - hexColours.reverse(); - return this.interpolatingMapper(hexColours); - } - - hsv2rgb(h, s, v) { // all values in range [0, 1] - const i = Math.floor(h * 6); - const f = h * 6 - i; - const p = v * (1 - s); - const q = v * (1 - f * s); - const t = v * (1 - (1 - f) * s); - let r = 0, g = 0, b = 0; - switch (i % 6) { - case 0: r = v; g = t; b = p; break; - case 1: r = q; g = v; b = p; break; - case 2: r = p; g = v; b = t; break; - case 3: r = p; g = q; b = v; break; - case 4: r = t; g = p; b = v; break; - case 5: r = v; g = p; b = q; break; - } - return [ r, g, b ]; - } - - greenMapper() { - const blue = 0.6666; - const pieslice = 0.3333; - return (value => { - const h = blue - value * 2.0 * pieslice; - const s = 0.5 + value / 2.0; - const v = value; - return this.hsv2rgb(h, s, v); - }); - } - - sunsetMapper() { - return (value => { - const r = (value - 0.24) * 2.38; - const g = (value - 0.64) * 2.777; - let b = (3.6 * value); - if (value > 0.277) { - b = 2.0 - b; - } - return [ r, g, b ]; - }); - } - clearTimeline(): void { // loop through layers and remove them, waves-ui provides methods for this but it seems to not work properly const timeContextChildren = this.timeline.timeContext._children; @@ -611,7 +492,7 @@ stepSize: 512, blockSize: 1024, normalise: 'none', - mapper: this.sunsetMapper() + mapper: sunsetMapper() }); this.addLayer(spectrogramLayer, gridTrack, this.timeline.timeContext); @@ -686,7 +567,7 @@ return lineLayer; }); - this.addScaleAndHighlight(waveTrack, lineLayers, unit, colour, min, max); + this.addScaleAndHighlight(this.waveTrack, lineLayers, unit, colour, min, max); } private addScaleAndHighlight(waveTrack, @@ -697,7 +578,7 @@ max: number) { const height = this.trackDiv.nativeElement.getBoundingClientRect().height; - + // And a single scale layer at left // !!! todo: unit in scale layer const scaleLayer = new wavesUI.helpers.ScaleLayer({ @@ -708,7 +589,7 @@ }); this.addLayer( scaleLayer, - this.waveTrack, + waveTrack, this.timeline.timeContext ); @@ -724,7 +605,7 @@ }); this.addLayer( this.highlightLayer, - this.waveTrack, + waveTrack, this.timeline.timeContext ); } @@ -829,8 +710,14 @@ this.waveTrack, this.timeline.timeContext ); - this.addScaleAndHighlight(waveTrack, [pianoRollLayer], "", - colour, min, max); + this.addScaleAndHighlight( + this.waveTrack, + [pianoRollLayer], + '', + colour, + min, + max + ); break; } } catch (e) { @@ -851,9 +738,8 @@ console.log('matrix data length = ' + matrixData.length); console.log('height of first column = ' + matrixData[0].length); - const targetValue = this.estimatePercentile(matrixData, 95); + const targetValue = estimatePercentile(matrixData, 95); const gain = (targetValue > 0.0 ? (1.0 / targetValue) : 1.0); - console.log('setting gain to ' + gain); const matrixEntity = new wavesUI.utils.PrefilledMatrixEntity(matrixData, 0, // startTime @@ -863,7 +749,7 @@ top: 0, height: height, normalise: 'none', - mapper: this.iceMapper() + mapper: iceMapper() }); this.addLayer( matrixLayer,