@opendaw/studio-core 0.0.121 → 0.0.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AssetService.d.ts +4 -3
- package/dist/AssetService.d.ts.map +1 -1
- package/dist/AssetService.js +3 -5
- package/dist/EffectBox.d.ts +2 -2
- package/dist/EffectBox.d.ts.map +1 -1
- package/dist/EffectFactories.d.ts +3 -0
- package/dist/EffectFactories.d.ts.map +1 -1
- package/dist/EffectFactories.js +15 -1
- package/dist/RecordingWorklet.d.ts +2 -0
- package/dist/RecordingWorklet.d.ts.map +1 -1
- package/dist/RecordingWorklet.js +8 -25
- package/dist/capture/CaptureAudio.d.ts.map +1 -1
- package/dist/capture/CaptureAudio.js +2 -1
- package/dist/capture/RecordMidi.d.ts.map +1 -1
- package/dist/capture/RecordMidi.js +24 -2
- package/dist/offline-engine.js +1 -1
- package/dist/offline-engine.js.map +1 -1
- package/dist/processors.js +25 -25
- package/dist/processors.js.map +4 -4
- package/dist/project/ProjectEnv.d.ts +4 -0
- package/dist/project/ProjectEnv.d.ts.map +1 -1
- package/dist/project/audio/AudioContentFactory.d.ts +2 -1
- package/dist/project/audio/AudioContentFactory.d.ts.map +1 -1
- package/dist/project/audio/AudioContentFactory.js +8 -7
- package/dist/samples/SampleService.d.ts +5 -3
- package/dist/samples/SampleService.d.ts.map +1 -1
- package/dist/samples/SampleService.js +16 -7
- package/dist/soundfont/SoundfontService.d.ts +2 -2
- package/dist/soundfont/SoundfontService.d.ts.map +1 -1
- package/dist/soundfont/SoundfontService.js +3 -3
- package/dist/ui/renderer/audio.d.ts +13 -1
- package/dist/ui/renderer/audio.d.ts.map +1 -1
- package/dist/ui/renderer/audio.js +28 -20
- package/dist/ui/renderer/fading.d.ts.map +1 -1
- package/dist/ui/renderer/fading.js +23 -20
- package/dist/ui/renderer/index.d.ts +1 -0
- package/dist/ui/renderer/index.d.ts.map +1 -1
- package/dist/ui/renderer/index.js +1 -0
- package/dist/ui/renderer/notes.d.ts.map +1 -1
- package/dist/ui/renderer/notes.js +7 -4
- package/dist/ui/renderer/riffle.d.ts +3 -0
- package/dist/ui/renderer/riffle.d.ts.map +1 -0
- package/dist/ui/renderer/riffle.js +106 -0
- package/dist/workers-main.js +1 -1
- package/dist/workers-main.js.map +1 -1
- package/package.json +15 -15
|
@@ -2,11 +2,15 @@ import { SampleLoaderManager, SoundfontLoaderManager } from "@opendaw/studio-ada
|
|
|
2
2
|
import { Editing, Func } from "@opendaw/lib-std";
|
|
3
3
|
import { BoxGraph } from "@opendaw/lib-box";
|
|
4
4
|
import { AudioWorklets } from "../AudioWorklets";
|
|
5
|
+
import { SampleService } from "../samples";
|
|
6
|
+
import { SoundfontService } from "../soundfont";
|
|
5
7
|
export interface ProjectEnv {
|
|
6
8
|
audioContext: AudioContext;
|
|
7
9
|
audioWorklets: AudioWorklets;
|
|
8
10
|
sampleManager: SampleLoaderManager;
|
|
9
11
|
soundfontManager: SoundfontLoaderManager;
|
|
12
|
+
sampleService: SampleService;
|
|
13
|
+
soundfontService: SoundfontService;
|
|
10
14
|
createEditing?: Func<BoxGraph, Editing>;
|
|
11
15
|
}
|
|
12
16
|
//# sourceMappingURL=ProjectEnv.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectEnv.d.ts","sourceRoot":"","sources":["../../src/project/ProjectEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAE,sBAAsB,EAAC,MAAM,0BAA0B,CAAA;AACpF,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"ProjectEnv.d.ts","sourceRoot":"","sources":["../../src/project/ProjectEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,mBAAmB,EAAE,sBAAsB,EAAC,MAAM,0BAA0B,CAAA;AACpF,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAC,aAAa,EAAC,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAC,aAAa,EAAC,MAAM,YAAY,CAAA;AACxC,OAAO,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAA;AAE7C,MAAM,WAAW,UAAU;IACvB,YAAY,EAAE,YAAY,CAAA;IAC1B,aAAa,EAAE,aAAa,CAAA;IAC5B,aAAa,EAAE,mBAAmB,CAAA;IAClC,gBAAgB,EAAE,sBAAsB,CAAA;IACxC,aAAa,EAAE,aAAa,CAAA;IAC5B,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,aAAa,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;CAC1C"}
|
|
@@ -15,6 +15,7 @@ export declare namespace AudioContentFactory {
|
|
|
15
15
|
warpMarkers?: ReadonlyArray<WarpMarkerTemplate>;
|
|
16
16
|
waveformOffset?: number;
|
|
17
17
|
gainInDb?: number;
|
|
18
|
+
disableQuantize?: boolean;
|
|
18
19
|
};
|
|
19
20
|
type Clip = Props & {
|
|
20
21
|
index: int;
|
|
@@ -33,7 +34,7 @@ export declare namespace AudioContentFactory {
|
|
|
33
34
|
* Calculates the duration of an audio region based on sample properties.
|
|
34
35
|
* Returns duration in PPQN for stretched regions, or in seconds for non-stretched.
|
|
35
36
|
*/
|
|
36
|
-
const calculateDuration: (sample: Sample) => ppqn;
|
|
37
|
+
const calculateDuration: (sample: Sample, disableQuantize?: boolean) => ppqn;
|
|
37
38
|
const createTimeStretchedRegion: (props: TimeStretchedProps & Region) => AudioRegionBox;
|
|
38
39
|
const createPitchStretchedRegion: (props: PitchStretchedProps & Region) => AudioRegionBox;
|
|
39
40
|
const createNotStretchedRegion: (props: NotStretchedProps & Region) => AudioRegionBox;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AudioContentFactory.d.ts","sourceRoot":"","sources":["../../../src/project/audio/AudioContentFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAiB,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAa,MAAM,EAAY,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"AudioContentFactory.d.ts","sourceRoot":"","sources":["../../../src/project/audio/AudioContentFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAiB,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAa,MAAM,EAAY,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAC,GAAG,EAAwC,MAAM,kBAAkB,CAAA;AAC3E,OAAO,EACH,YAAY,EACZ,YAAY,EAEZ,cAAc,EAEd,QAAQ,EAEX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAC,iBAAiB,EAAC,MAAM,uBAAuB,CAAA;AACvD,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAEzC,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAA;AAEvD,yBAAiB,mBAAmB,CAAC;IACjC,KAAY,KAAK,GAAG;QAChB,QAAQ,EAAE,QAAQ,CAAA;QAClB,WAAW,EAAE,QAAQ,CAAA;QACrB,YAAY,EAAE,YAAY,CAAA;QAC1B,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;QACxB,WAAW,CAAC,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;QAC/C,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,eAAe,CAAC,EAAE,OAAO,CAAA;KAC5B,CAAA;IAED,KAAY,IAAI,GAAG,KAAK,GAAG;QAAE,KAAK,EAAE,GAAG,CAAA;KAAE,CAAA;IACzC,KAAY,MAAM,GAAG,KAAK,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAE9D,KAAY,kBAAkB,GAAG;QAC7B,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;QACrC,YAAY,CAAC,EAAE,MAAM,CAAA;KACxB,GAAG,KAAK,CAAA;IAET,KAAY,mBAAmB,GAAG,EAA0C,GAAG,KAAK,CAAA;IAEpF,KAAY,iBAAiB,GAAG,EAA0C,GAAG,KAAK,CAAA;IAElF;;;OAGG;IACI,MAAM,iBAAiB,GAAI,QAAQ,MAAM,EAAE,kBAAiB,OAAe,KAAG,IAOpF,CAAA;IAIM,MAAM,yBAAyB,GAAI,OAAO,kBAAkB,GAAG,MAAM,KAAG,cAM9E,CAAA;IAEM,MAAM,0BAA0B,GAAI,OAAO,mBAAmB,GAAG,MAAM,KAAG,cAEhF,CAAA;IAEM,MAAM,wBAAwB,GAAI,OAAO,iBAAiB,GAAG,MAAM,KAAG,cAgB5E,CAAA;IAIM,MAAM,uBAAuB,GAAI,OAAO,kBAAkB,GAAG,IAAI,KAAG,YAM1E,CAAA;IAEM,MAAM,wBAAwB,GAAI,OAAO,mBAAmB,GAAG,IAAI,KAAG,YAG5E,CAAA;IAEM,MAAM,sBAAsB,GAAI,OAAO,iBAAiB,GAAG,IAAI,KAAG,YAcxE,CAAA;CA+DJ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PPQN, TimeBase } from "@opendaw/lib-dsp";
|
|
2
2
|
import { ColorCodes, TrackType } from "@opendaw/studio-adapters";
|
|
3
|
-
import { isDefined, panic,
|
|
3
|
+
import { isDefined, panic, quantizeRound, UUID } from "@opendaw/lib-std";
|
|
4
4
|
import { AudioClipBox, AudioPitchStretchBox, AudioRegionBox, AudioTimeStretchBox, ValueEventCollectionBox } from "@opendaw/studio-boxes";
|
|
5
5
|
import { TransientPlayMode } from "@opendaw/studio-enums";
|
|
6
6
|
import { AudioContentHelpers } from "./AudioContentHelpers";
|
|
@@ -10,14 +10,13 @@ export var AudioContentFactory;
|
|
|
10
10
|
* Calculates the duration of an audio region based on sample properties.
|
|
11
11
|
* Returns duration in PPQN for stretched regions, or in seconds for non-stretched.
|
|
12
12
|
*/
|
|
13
|
-
AudioContentFactory.calculateDuration = (sample) => {
|
|
13
|
+
AudioContentFactory.calculateDuration = (sample, disableQuantize = false) => {
|
|
14
14
|
const { duration: durationInSeconds, bpm } = sample;
|
|
15
15
|
if (bpm === 0) {
|
|
16
|
-
// Non-stretched: duration is in seconds
|
|
17
16
|
return durationInSeconds;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
return
|
|
18
|
+
const pulses = PPQN.secondsToPulses(durationInSeconds, bpm);
|
|
19
|
+
return disableQuantize ? pulses : quantizeRound(pulses, PPQN.SemiQuaver);
|
|
21
20
|
};
|
|
22
21
|
// --- Region Creation --- //
|
|
23
22
|
AudioContentFactory.createTimeStretchedRegion = (props) => {
|
|
@@ -81,7 +80,8 @@ export var AudioContentFactory;
|
|
|
81
80
|
return panic("Cannot create audio-region on non-audio track");
|
|
82
81
|
}
|
|
83
82
|
const { name, duration: durationInSeconds, bpm } = sample;
|
|
84
|
-
const
|
|
83
|
+
const pulses = PPQN.secondsToPulses(durationInSeconds, bpm);
|
|
84
|
+
const durationInPPQN = props.duration ?? (!props.disableQuantize ? quantizeRound(pulses, PPQN.SemiQuaver) : pulses);
|
|
85
85
|
if (isDefined(props.warpMarkers)) {
|
|
86
86
|
AudioContentHelpers.addWarpMarkers(boxGraph, playMode, props.warpMarkers);
|
|
87
87
|
}
|
|
@@ -110,7 +110,8 @@ export var AudioContentFactory;
|
|
|
110
110
|
return panic("Cannot create audio-region on non-audio track");
|
|
111
111
|
}
|
|
112
112
|
const { name, duration: durationInSeconds, bpm } = sample;
|
|
113
|
-
const
|
|
113
|
+
const pulses = PPQN.secondsToPulses(durationInSeconds, bpm);
|
|
114
|
+
const durationInPPQN = props.duration ?? (!props.disableQuantize ? quantizeRound(pulses, PPQN.SemiQuaver) : pulses);
|
|
114
115
|
if (isDefined(props.warpMarkers)) {
|
|
115
116
|
AudioContentHelpers.addWarpMarkers(boxGraph, playMode, props.warpMarkers);
|
|
116
117
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Class
|
|
1
|
+
import { Class } from "@opendaw/lib-std";
|
|
2
2
|
import { Box } from "@opendaw/lib-box";
|
|
3
|
+
import { AudioData } from "@opendaw/lib-dsp";
|
|
3
4
|
import { Sample } from "@opendaw/studio-adapters";
|
|
4
5
|
import { AssetService } from "../AssetService";
|
|
5
6
|
export declare class SampleService extends AssetService<Sample> {
|
|
@@ -9,8 +10,9 @@ export declare class SampleService extends AssetService<Sample> {
|
|
|
9
10
|
protected readonly nameSingular: string;
|
|
10
11
|
protected readonly boxType: Class<Box>;
|
|
11
12
|
protected readonly filePickerOptions: FilePickerOptions;
|
|
12
|
-
constructor(audioContext: AudioContext
|
|
13
|
-
|
|
13
|
+
constructor(audioContext: AudioContext);
|
|
14
|
+
importRecording(audioData: AudioData, name?: string): Promise<Sample>;
|
|
15
|
+
importFile({ uuid, name, arrayBuffer, progressHandler, origin }: AssetService.ImportArgs): Promise<Sample>;
|
|
14
16
|
protected collectAllFiles(): Promise<ReadonlyArray<Sample>>;
|
|
15
17
|
}
|
|
16
18
|
//# sourceMappingURL=SampleService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SampleService.d.ts","sourceRoot":"","sources":["../../src/samples/SampleService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,
|
|
1
|
+
{"version":3,"file":"SampleService.d.ts","sourceRoot":"","sources":["../../src/samples/SampleService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,EAA2B,MAAM,kBAAkB,CAAA;AACxE,OAAO,EAAC,GAAG,EAAC,MAAM,kBAAkB,CAAA;AACpC,OAAO,EAAC,SAAS,EAAc,MAAM,kBAAkB,CAAA;AAIvD,OAAO,EAAC,MAAM,EAAiB,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAO5C,qBAAa,aAAc,SAAQ,YAAY,CAAC,MAAM,CAAC;;IAMvC,QAAQ,CAAC,YAAY,EAAE,YAAY;IAL/C,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAY;IACjD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAW;IAClD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAe;IACrD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAiC;gBAEnE,YAAY,EAAE,YAAY;IAEzC,eAAe,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,GAAE,MAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAUlF,UAAU,CAAC,EAAC,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,eAAgC,EAAE,MAAiB,EAAC,EAC5E,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;cAyB5C,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;CAkBpE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Arrays,
|
|
1
|
+
import { Arrays, Progress, tryCatch, UUID } from "@opendaw/lib-std";
|
|
2
2
|
import { AudioData, estimateBpm } from "@opendaw/lib-dsp";
|
|
3
3
|
import { Promises } from "@opendaw/lib-runtime";
|
|
4
4
|
import { SamplePeaks } from "@opendaw/lib-fusion";
|
|
@@ -15,11 +15,20 @@ export class SampleService extends AssetService {
|
|
|
15
15
|
nameSingular = "Sample";
|
|
16
16
|
boxType = AudioFileBox;
|
|
17
17
|
filePickerOptions = FilePickerAcceptTypes.WavFiles;
|
|
18
|
-
constructor(audioContext
|
|
19
|
-
super(
|
|
18
|
+
constructor(audioContext) {
|
|
19
|
+
super();
|
|
20
20
|
this.audioContext = audioContext;
|
|
21
21
|
}
|
|
22
|
-
async
|
|
22
|
+
async importRecording(audioData, name = "Recording") {
|
|
23
|
+
const arrayBuffer = WavFile.encodeFloats({
|
|
24
|
+
frames: audioData.frames.slice(),
|
|
25
|
+
numberOfFrames: audioData.numberOfFrames,
|
|
26
|
+
numberOfChannels: audioData.numberOfChannels,
|
|
27
|
+
sampleRate: audioData.sampleRate
|
|
28
|
+
});
|
|
29
|
+
return this.importFile({ name, arrayBuffer, origin: "recording" });
|
|
30
|
+
}
|
|
31
|
+
async importFile({ uuid, name, arrayBuffer, progressHandler = Progress.Empty, origin = "import" }) {
|
|
23
32
|
console.debug(`importSample '${name}' (${arrayBuffer.byteLength >> 10}kb)`);
|
|
24
33
|
uuid ??= await UUID.sha256(arrayBuffer);
|
|
25
34
|
const audioData = await this.#decodeAudio(arrayBuffer);
|
|
@@ -28,14 +37,14 @@ export class SampleService extends AssetService {
|
|
|
28
37
|
const peaks = await Workers.Peak.generateAsync(progressHandler, shifts, audioData.frames, audioData.numberOfFrames, audioData.numberOfChannels);
|
|
29
38
|
const meta = {
|
|
30
39
|
bpm: estimateBpm(duration),
|
|
31
|
-
name:
|
|
40
|
+
name: name ?? "Unnnamed",
|
|
32
41
|
duration,
|
|
33
42
|
sample_rate: audioData.sampleRate,
|
|
34
|
-
origin
|
|
43
|
+
origin
|
|
35
44
|
};
|
|
36
45
|
await SampleStorage.get().save({ uuid, audio: audioData, peaks, meta });
|
|
37
46
|
const sample = { uuid: UUID.toString(uuid), ...meta };
|
|
38
|
-
this.
|
|
47
|
+
this.notifier.notify(sample);
|
|
39
48
|
return sample;
|
|
40
49
|
}
|
|
41
50
|
async collectAllFiles() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Class, Option
|
|
1
|
+
import { Class, Option } from "@opendaw/lib-std";
|
|
2
2
|
import { Box } from "@opendaw/lib-box";
|
|
3
3
|
import { Soundfont } from "@opendaw/studio-adapters";
|
|
4
4
|
import { AssetService } from "../AssetService";
|
|
@@ -8,7 +8,7 @@ export declare class SoundfontService extends AssetService<Soundfont> {
|
|
|
8
8
|
protected readonly nameSingular: string;
|
|
9
9
|
protected readonly boxType: Class<Box>;
|
|
10
10
|
protected readonly filePickerOptions: FilePickerOptions;
|
|
11
|
-
constructor(
|
|
11
|
+
constructor();
|
|
12
12
|
get local(): Option<ReadonlyArray<Soundfont>>;
|
|
13
13
|
get remote(): Option<ReadonlyArray<Soundfont>>;
|
|
14
14
|
importFile({ uuid, arrayBuffer }: AssetService.ImportArgs): Promise<Soundfont>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SoundfontService.d.ts","sourceRoot":"","sources":["../../src/soundfont/SoundfontService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"SoundfontService.d.ts","sourceRoot":"","sources":["../../src/soundfont/SoundfontService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,EAAE,MAAM,EAA+B,MAAM,kBAAkB,CAAA;AACpF,OAAO,EAAC,GAAG,EAAC,MAAM,kBAAkB,CAAA;AAGpC,OAAO,EAAC,SAAS,EAAoB,MAAM,0BAA0B,CAAA;AAIrE,OAAO,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAG5C,qBAAa,gBAAiB,SAAQ,YAAY,CAAC,SAAS,CAAC;;IACzD,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAe;IACpD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAc;IACrD,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAmB;IACzD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAuC;;IAgB9F,IAAI,KAAK,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAqB;IAClE,IAAI,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAsB;IAE9D,UAAU,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,EAAE,YAAY,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;cA4ClE,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;CAKvE"}
|
|
@@ -13,8 +13,8 @@ export class SoundfontService extends AssetService {
|
|
|
13
13
|
filePickerOptions = FilePickerAcceptTypes.SoundfontFiles;
|
|
14
14
|
#local = Option.None;
|
|
15
15
|
#remote = Option.None;
|
|
16
|
-
constructor(
|
|
17
|
-
super(
|
|
16
|
+
constructor() {
|
|
17
|
+
super();
|
|
18
18
|
Promise.all([
|
|
19
19
|
SoundfontStorage.get().list(),
|
|
20
20
|
OpenSoundfontAPI.get().all()
|
|
@@ -64,7 +64,7 @@ export class SoundfontService extends AssetService {
|
|
|
64
64
|
if (!list.some(other => other.uuid === soundfont.uuid)) {
|
|
65
65
|
list.push(soundfont);
|
|
66
66
|
}
|
|
67
|
-
this.
|
|
67
|
+
this.notifier.notify(soundfont);
|
|
68
68
|
updater.terminate();
|
|
69
69
|
return soundfont;
|
|
70
70
|
}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { RegionBound } from "./env";
|
|
2
2
|
import { Option } from "@opendaw/lib-std";
|
|
3
3
|
import { LoopableRegion, TempoMap } from "@opendaw/lib-dsp";
|
|
4
|
+
import { Peaks } from "@opendaw/lib-fusion";
|
|
4
5
|
import { AudioFileBoxAdapter, AudioPlayMode } from "@opendaw/studio-adapters";
|
|
5
6
|
import { TimelineRange } from "../timeline/TimelineRange";
|
|
6
7
|
export declare namespace AudioRenderer {
|
|
7
|
-
|
|
8
|
+
type Segment = {
|
|
9
|
+
x0: number;
|
|
10
|
+
x1: number;
|
|
11
|
+
u0: number;
|
|
12
|
+
u1: number;
|
|
13
|
+
outside: boolean;
|
|
14
|
+
};
|
|
15
|
+
interface Strategy {
|
|
16
|
+
render(context: CanvasRenderingContext2D, segments: ReadonlyArray<Segment>, peaks: Peaks, bound: RegionBound, gain: number): void;
|
|
17
|
+
}
|
|
18
|
+
const DefaultStrategy: Strategy;
|
|
19
|
+
const render: (context: CanvasRenderingContext2D, range: TimelineRange, file: AudioFileBoxAdapter, tempoMap: TempoMap, playMode: Option<AudioPlayMode>, waveformOffset: number, gain: number, bound: RegionBound, contentColor: string, { rawStart, resultStart, resultEnd }: LoopableRegion.LoopCycle, clip?: boolean, strategy?: Strategy) => void;
|
|
8
20
|
}
|
|
9
21
|
//# sourceMappingURL=audio.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,OAAO,CAAA;AACjC,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACvC,OAAO,EAAW,cAAc,EAAyB,QAAQ,EAAC,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/audio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,OAAO,CAAA;AACjC,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAA;AACvC,OAAO,EAAW,cAAc,EAAyB,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AAC1F,OAAO,EAAC,KAAK,EAAe,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAC,mBAAmB,EAAE,aAAa,EAAC,MAAM,0BAA0B,CAAA;AAC3E,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAA;AAEvD,yBAAiB,aAAa,CAAC;IAC3B,KAAY,OAAO,GAAG;QAClB,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,MAAM,CAAA;QACV,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,OAAO,CAAA;KACnB,CAAA;IAED,UAAiB,QAAQ;QACrB,MAAM,CAAC,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,EAChC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAC7B;IAEM,MAAM,eAAe,EAAE,QAyB7B,CAAA;IAEM,MAAM,MAAM,GAAI,SAAS,wBAAwB,EACjC,OAAO,aAAa,EACpB,MAAM,mBAAmB,EACzB,UAAU,QAAQ,EAClB,UAAU,MAAM,CAAC,aAAa,CAAC,EAC/B,gBAAgB,MAAM,EACtB,MAAM,MAAM,EACZ,OAAO,WAAW,EAClB,cAAc,MAAM,EACpB,sCAAoC,cAAc,CAAC,SAAS,EAC5D,OAAM,OAAc,EACpB,WAAU,QAA0B,SAyU1D,CAAA;CACJ"}
|
|
@@ -2,17 +2,39 @@ import { dbToGain, PPQN, TempoChangeGrid } from "@opendaw/lib-dsp";
|
|
|
2
2
|
import { PeaksPainter } from "@opendaw/lib-fusion";
|
|
3
3
|
export var AudioRenderer;
|
|
4
4
|
(function (AudioRenderer) {
|
|
5
|
-
AudioRenderer.
|
|
5
|
+
AudioRenderer.DefaultStrategy = {
|
|
6
|
+
render(context, segments, peaks, { top, bottom }, gain) {
|
|
7
|
+
const dpr = devicePixelRatio;
|
|
8
|
+
const actualTop = top * dpr;
|
|
9
|
+
const actualBottom = bottom * dpr;
|
|
10
|
+
const height = actualBottom - actualTop;
|
|
11
|
+
const numberOfChannels = peaks.numChannels;
|
|
12
|
+
const peaksHeight = Math.floor((height - 4) / numberOfChannels);
|
|
13
|
+
const scale = dbToGain(-gain);
|
|
14
|
+
for (const { x0, x1, u0, u1, outside } of segments) {
|
|
15
|
+
context.globalAlpha = outside ? 0.25 : 1.0;
|
|
16
|
+
for (let channel = 0; channel < numberOfChannels; channel++) {
|
|
17
|
+
PeaksPainter.renderPixelStrips(context, peaks, channel, {
|
|
18
|
+
u0,
|
|
19
|
+
u1,
|
|
20
|
+
v0: -scale,
|
|
21
|
+
v1: +scale,
|
|
22
|
+
x0,
|
|
23
|
+
x1,
|
|
24
|
+
y0: 3 + actualTop + channel * peaksHeight,
|
|
25
|
+
y1: 3 + actualTop + (channel + 1) * peaksHeight
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
AudioRenderer.render = (context, range, file, tempoMap, playMode, waveformOffset, gain, bound, contentColor, { rawStart, resultStart, resultEnd }, clip = true, strategy = AudioRenderer.DefaultStrategy) => {
|
|
6
32
|
if (file.peaks.isEmpty()) {
|
|
7
33
|
return;
|
|
8
34
|
}
|
|
9
35
|
const peaks = file.peaks.unwrap();
|
|
10
36
|
const durationInSeconds = file.endInSeconds - file.startInSeconds;
|
|
11
37
|
const numFrames = peaks.numFrames;
|
|
12
|
-
const numberOfChannels = peaks.numChannels;
|
|
13
|
-
const ht = bottom - top;
|
|
14
|
-
const peaksHeight = Math.floor((ht - 4) / numberOfChannels);
|
|
15
|
-
const scale = dbToGain(-gain);
|
|
16
38
|
const segments = [];
|
|
17
39
|
if (playMode.nonEmpty()) {
|
|
18
40
|
const { warpMarkers } = playMode.unwrap();
|
|
@@ -258,20 +280,6 @@ export var AudioRenderer;
|
|
|
258
280
|
}
|
|
259
281
|
}
|
|
260
282
|
context.fillStyle = contentColor;
|
|
261
|
-
|
|
262
|
-
context.globalAlpha = outside && !clip ? 0.25 : 1.0;
|
|
263
|
-
for (let channel = 0; channel < numberOfChannels; channel++) {
|
|
264
|
-
PeaksPainter.renderBlocks(context, peaks, channel, {
|
|
265
|
-
u0,
|
|
266
|
-
u1,
|
|
267
|
-
v0: -scale,
|
|
268
|
-
v1: +scale,
|
|
269
|
-
x0,
|
|
270
|
-
x1,
|
|
271
|
-
y0: 3 + top + channel * peaksHeight,
|
|
272
|
-
y1: 3 + top + (channel + 1) * peaksHeight
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
283
|
+
strategy.render(context, segments, peaks, bound, gain);
|
|
276
284
|
};
|
|
277
285
|
})(AudioRenderer || (AudioRenderer = {}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fading.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/fading.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,WAAW,EAAC,MAAM,OAAO,CAAA;AAEjC,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,MAAM,GAAI,SAAS,wBAAwB,EACjC,OAAO,aAAa,EACpB,QAAQ,cAAc,CAAC,MAAM,EAC7B,iBAAe,WAAW,EAC1B,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,OAAO,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"fading.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/fading.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,aAAa,EAAC,MAAM,aAAa,CAAA;AACzC,OAAO,EAAC,WAAW,EAAC,MAAM,OAAO,CAAA;AAEjC,yBAAiB,mBAAmB,CAAC;IAC1B,MAAM,MAAM,GAAI,SAAS,wBAAwB,EACjC,OAAO,aAAa,EACpB,QAAQ,cAAc,CAAC,MAAM,EAC7B,iBAAe,WAAW,EAC1B,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,OAAO,MAAM,KAAG,IAqDtC,CAAA;CACJ"}
|
|
@@ -2,6 +2,9 @@ import { Curve, TAU } from "@opendaw/lib-std";
|
|
|
2
2
|
export var AudioFadingRenderer;
|
|
3
3
|
(function (AudioFadingRenderer) {
|
|
4
4
|
AudioFadingRenderer.render = (context, range, fading, { top, bottom }, startPPQN, endPPQN, color) => {
|
|
5
|
+
const dpr = devicePixelRatio;
|
|
6
|
+
const actualTop = top * dpr;
|
|
7
|
+
const actualBottom = bottom * dpr;
|
|
5
8
|
const { inSlope: fadeInSlope, outSlope: fadeOutSlope } = fading;
|
|
6
9
|
const duration = endPPQN - startPPQN;
|
|
7
10
|
const totalFading = fading.in + fading.out;
|
|
@@ -10,46 +13,46 @@ export var AudioFadingRenderer;
|
|
|
10
13
|
const fadeOut = fading.out * scale;
|
|
11
14
|
context.strokeStyle = color;
|
|
12
15
|
context.fillStyle = "rgba(0,0,0,0.25)";
|
|
13
|
-
context.lineWidth =
|
|
16
|
+
context.lineWidth = dpr;
|
|
14
17
|
if (fadeIn > 0) {
|
|
15
18
|
const fadeInEndPPQN = startPPQN + fadeIn;
|
|
16
|
-
const x0 = range.unitToX(startPPQN) *
|
|
17
|
-
const x1 = range.unitToX(fadeInEndPPQN) *
|
|
19
|
+
const x0 = range.unitToX(startPPQN) * dpr;
|
|
20
|
+
const x1 = range.unitToX(fadeInEndPPQN) * dpr;
|
|
18
21
|
const xn = x1 - x0;
|
|
19
22
|
const path = new Path2D();
|
|
20
|
-
path.moveTo(x0,
|
|
23
|
+
path.moveTo(x0, actualBottom);
|
|
21
24
|
let x = x0;
|
|
22
|
-
Curve.run(fadeInSlope, xn,
|
|
23
|
-
path.lineTo(x1,
|
|
25
|
+
Curve.run(fadeInSlope, xn, actualBottom, actualTop, y => path.lineTo(++x, y));
|
|
26
|
+
path.lineTo(x1, actualTop);
|
|
24
27
|
context.stroke(path);
|
|
25
|
-
path.lineTo(x0,
|
|
26
|
-
path.lineTo(x0,
|
|
28
|
+
path.lineTo(x0, actualTop);
|
|
29
|
+
path.lineTo(x0, actualBottom);
|
|
27
30
|
context.fill(path);
|
|
28
31
|
}
|
|
29
32
|
if (fadeOut > 0) {
|
|
30
|
-
const x0 = range.unitToX(endPPQN - fadeOut) *
|
|
31
|
-
const x1 = range.unitToX(endPPQN) *
|
|
33
|
+
const x0 = range.unitToX(endPPQN - fadeOut) * dpr;
|
|
34
|
+
const x1 = range.unitToX(endPPQN) * dpr;
|
|
32
35
|
const xn = x1 - x0;
|
|
33
36
|
const path = new Path2D();
|
|
34
|
-
path.moveTo(x0,
|
|
37
|
+
path.moveTo(x0, actualTop);
|
|
35
38
|
let x = x0;
|
|
36
|
-
Curve.run(fadeOutSlope, xn,
|
|
37
|
-
path.lineTo(x1,
|
|
39
|
+
Curve.run(fadeOutSlope, xn, actualTop, actualBottom, y => path.lineTo(++x, y));
|
|
40
|
+
path.lineTo(x1, actualBottom);
|
|
38
41
|
context.strokeStyle = color;
|
|
39
42
|
context.stroke(path);
|
|
40
|
-
path.lineTo(x1,
|
|
41
|
-
path.lineTo(x0,
|
|
43
|
+
path.lineTo(x1, actualTop);
|
|
44
|
+
path.lineTo(x0, actualTop);
|
|
42
45
|
context.fill(path);
|
|
43
46
|
}
|
|
44
|
-
const handleRadius = 1.5 *
|
|
45
|
-
const x0 = Math.max(range.unitToX(startPPQN + fadeIn), range.unitToX(startPPQN)) *
|
|
46
|
-
const x1 = Math.min(range.unitToX(endPPQN - fadeOut), range.unitToX(endPPQN)) *
|
|
47
|
+
const handleRadius = 1.5 * dpr;
|
|
48
|
+
const x0 = Math.max(range.unitToX(startPPQN + fadeIn), range.unitToX(startPPQN)) * dpr;
|
|
49
|
+
const x1 = Math.min(range.unitToX(endPPQN - fadeOut), range.unitToX(endPPQN)) * dpr;
|
|
47
50
|
context.fillStyle = color;
|
|
48
51
|
context.beginPath();
|
|
49
|
-
context.arc(x0,
|
|
52
|
+
context.arc(x0, actualTop, handleRadius, 0, TAU);
|
|
50
53
|
context.fill();
|
|
51
54
|
context.beginPath();
|
|
52
|
-
context.arc(x1,
|
|
55
|
+
context.arc(x1, actualTop, handleRadius, 0, TAU);
|
|
53
56
|
context.fill();
|
|
54
57
|
};
|
|
55
58
|
})(AudioFadingRenderer || (AudioFadingRenderer = {}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,cAAc,OAAO,CAAA;AACrB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,SAAS,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,6BAA6B,EAAC,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,aAAa,CAAA;AAEtD,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,SAAS,wBAAwB,EACjC,OAAO,aAAa,EACpB,YAAY,6BAA6B,EACzC,iBAAe,WAAW,EAC1B,cAAc,MAAM,EACpB,mDAAiD,cAAc,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,6BAA6B,EAAC,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAC,WAAW,EAAE,aAAa,EAAC,MAAM,aAAa,CAAA;AAEtD,yBAAiB,aAAa,CAAC;IACpB,MAAM,MAAM,GAAI,SAAS,wBAAwB,EACjC,OAAO,aAAa,EACpB,YAAY,6BAA6B,EACzC,iBAAe,WAAW,EAC1B,cAAc,MAAM,EACpB,mDAAiD,cAAc,CAAC,SAAS,SAmB/F,CAAA;CACJ"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export var NotesRenderer;
|
|
2
2
|
(function (NotesRenderer) {
|
|
3
3
|
NotesRenderer.render = (context, range, collection, { top, bottom }, contentColor, { rawStart, regionStart, resultStart, resultEnd }) => {
|
|
4
|
-
const
|
|
4
|
+
const dpr = devicePixelRatio;
|
|
5
|
+
const actualTop = top * dpr;
|
|
6
|
+
const actualBottom = bottom * dpr;
|
|
7
|
+
const height = actualBottom - actualTop;
|
|
5
8
|
context.fillStyle = contentColor;
|
|
6
9
|
const padding = 8;
|
|
7
10
|
const noteHeight = 5;
|
|
@@ -13,9 +16,9 @@ export var NotesRenderer;
|
|
|
13
16
|
continue;
|
|
14
17
|
}
|
|
15
18
|
const complete = Math.min(rawStart + note.complete, resultEnd);
|
|
16
|
-
const x0 = Math.floor(range.unitToX(position) *
|
|
17
|
-
const x1 = Math.floor(range.unitToX(complete) *
|
|
18
|
-
const y =
|
|
19
|
+
const x0 = Math.floor(range.unitToX(position) * dpr);
|
|
20
|
+
const x1 = Math.floor(range.unitToX(complete) * dpr);
|
|
21
|
+
const y = actualTop + padding + Math.floor(note.normalizedPitch() * (height - (padding * 2 + noteHeight)));
|
|
19
22
|
context.fillRect(x0, y, Math.max(1, x1 - x0), noteHeight);
|
|
20
23
|
}
|
|
21
24
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"riffle.d.ts","sourceRoot":"","sources":["../../../src/ui/renderer/riffle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,SAAS,CAAA;AAErC,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,QAuF1C,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { dbToGain } from "@opendaw/lib-dsp";
|
|
2
|
+
import { Peaks } from "@opendaw/lib-fusion";
|
|
3
|
+
export const RiffleStrategy = {
|
|
4
|
+
render(context, segments, peaks, { top, bottom }, gain) {
|
|
5
|
+
if (segments.length === 0) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const dpr = devicePixelRatio;
|
|
9
|
+
const actualTop = top * dpr;
|
|
10
|
+
const actualBottom = bottom * dpr;
|
|
11
|
+
const height = actualBottom - actualTop;
|
|
12
|
+
const numberOfChannels = peaks.numChannels;
|
|
13
|
+
const peaksHeight = Math.floor(height / numberOfChannels);
|
|
14
|
+
const gainScale = dbToGain(-gain);
|
|
15
|
+
const blockWidth = 3 * dpr;
|
|
16
|
+
// noinspection PointlessArithmeticExpressionJS
|
|
17
|
+
const gap = dpr * 1;
|
|
18
|
+
const pixelsPerBlock = blockWidth + gap;
|
|
19
|
+
let globalX0 = Infinity;
|
|
20
|
+
let globalX1 = -Infinity;
|
|
21
|
+
for (const { x0, x1 } of segments) {
|
|
22
|
+
globalX0 = Math.min(globalX0, Math.floor(x0));
|
|
23
|
+
globalX1 = Math.max(globalX1, Math.floor(x1));
|
|
24
|
+
}
|
|
25
|
+
if (globalX0 >= globalX1) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const seg0 = segments[0];
|
|
29
|
+
const pxPerUnit = (seg0.x1 - seg0.x0) / (seg0.u1 - seg0.u0);
|
|
30
|
+
const anchorX = seg0.x0 - seg0.u0 * pxPerUnit;
|
|
31
|
+
const gridPhase = ((globalX0 - anchorX) % pixelsPerBlock + pixelsPerBlock) % pixelsPerBlock;
|
|
32
|
+
const firstBlockX = globalX0 - gridPhase;
|
|
33
|
+
for (let channel = 0; channel < numberOfChannels; channel++) {
|
|
34
|
+
const data = peaks.data[channel];
|
|
35
|
+
const channelY0 = actualTop + channel * peaksHeight;
|
|
36
|
+
const channelY1 = actualTop + (channel + 1) * peaksHeight;
|
|
37
|
+
const centerY = (channelY0 + channelY1) / 2;
|
|
38
|
+
const yScale = (channelY1 - channelY0 - 1.0) / (gainScale * 2);
|
|
39
|
+
for (let bx = firstBlockX; bx < globalX1; bx += pixelsPerBlock) {
|
|
40
|
+
const bxEnd = bx + pixelsPerBlock;
|
|
41
|
+
let blockMin = 0.0;
|
|
42
|
+
let blockMax = 0.0;
|
|
43
|
+
let blockAlpha = 0.0;
|
|
44
|
+
let hasData = false;
|
|
45
|
+
for (const seg of segments) {
|
|
46
|
+
const segX0 = Math.floor(seg.x0);
|
|
47
|
+
const segX1 = Math.floor(seg.x1);
|
|
48
|
+
if (segX0 >= bxEnd || segX1 <= bx) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const pixelSpan = seg.x1 - seg.x0;
|
|
52
|
+
if (pixelSpan <= 0) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const uPerPx = (seg.u1 - seg.u0) / pixelSpan;
|
|
56
|
+
const stage = peaks.nearest(uPerPx);
|
|
57
|
+
if (stage === null) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const uPerPeak = stage.unitsEachPeak();
|
|
61
|
+
const peaksPerPx = uPerPx / uPerPeak;
|
|
62
|
+
const overflow = seg.x0 - segX0;
|
|
63
|
+
const fromAtX0 = (seg.u0 - overflow * uPerPx) / uPerPx * peaksPerPx;
|
|
64
|
+
const overlapStart = Math.max(bx, segX0);
|
|
65
|
+
const overlapEnd = Math.min(bxEnd, segX1);
|
|
66
|
+
const fromPeak = fromAtX0 + (overlapStart - segX0) * peaksPerPx;
|
|
67
|
+
const toPeak = fromAtX0 + (overlapEnd - segX0) * peaksPerPx;
|
|
68
|
+
const idxFrom = Math.max(0, Math.floor(fromPeak));
|
|
69
|
+
const idxTo = Math.floor(toPeak);
|
|
70
|
+
for (let idx = idxFrom; idx < idxTo; idx++) {
|
|
71
|
+
const bits = data[stage.dataOffset + idx];
|
|
72
|
+
blockMin = Math.min(Peaks.unpack(bits, 0), blockMin);
|
|
73
|
+
blockMax = Math.max(Peaks.unpack(bits, 1), blockMax);
|
|
74
|
+
}
|
|
75
|
+
if (idxFrom < idxTo) {
|
|
76
|
+
hasData = true;
|
|
77
|
+
}
|
|
78
|
+
blockAlpha = Math.max(blockAlpha, seg.outside ? 0.25 : 1.0);
|
|
79
|
+
}
|
|
80
|
+
if (!hasData) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
context.globalAlpha = blockAlpha;
|
|
84
|
+
const x = Math.max(bx, globalX0);
|
|
85
|
+
const w = Math.min(blockWidth, Math.min(bxEnd, globalX1) - x);
|
|
86
|
+
if (w <= 0) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const yMin = channelY0 + Math.floor((blockMin + gainScale) * yScale);
|
|
90
|
+
const yMax = channelY0 + Math.floor((blockMax + gainScale) * yScale);
|
|
91
|
+
const ry0 = Math.max(channelY0, Math.min(yMin, yMax));
|
|
92
|
+
const ry1 = Math.min(channelY1, Math.max(yMin, yMax));
|
|
93
|
+
const finalY1 = ry0 === ry1 ? ry0 + 1 : ry1;
|
|
94
|
+
const maxDist = Math.max(centerY - ry0, finalY1 - centerY);
|
|
95
|
+
const symY0 = centerY - maxDist;
|
|
96
|
+
const symY1 = centerY + maxDist;
|
|
97
|
+
const h = symY1 - symY0;
|
|
98
|
+
const r = Math.min(dpr, w / 2, h / 2);
|
|
99
|
+
context.beginPath();
|
|
100
|
+
context.roundRect(x, symY0, w, h, r);
|
|
101
|
+
context.fill();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
context.globalAlpha = 1.0;
|
|
105
|
+
}
|
|
106
|
+
};
|