click on waveform
This commit is contained in:
@ -124,6 +124,7 @@
|
|||||||
onProcess: processSound,
|
onProcess: processSound,
|
||||||
onDownload: download,
|
onDownload: download,
|
||||||
onUndo: undo,
|
onUndo: undo,
|
||||||
|
onPlayFromStart: replaySound,
|
||||||
onDurationDecrease: (large) => {
|
onDurationDecrease: (large) => {
|
||||||
duration = Math.max(0.05, duration - (large ? 1 : 0.05));
|
duration = Math.max(0.05, duration - (large ? 1 : 0.05));
|
||||||
},
|
},
|
||||||
@ -199,6 +200,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function playFromPosition(offset: number) {
|
||||||
|
if (currentBuffer) {
|
||||||
|
audioService.play(currentBuffer, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
if (!currentBuffer) return;
|
if (!currentBuffer) return;
|
||||||
downloadWAV(currentBuffer, "synth-sound.wav");
|
downloadWAV(currentBuffer, "synth-sound.wav");
|
||||||
@ -504,7 +511,7 @@
|
|||||||
{selectionStart}
|
{selectionStart}
|
||||||
{selectionEnd}
|
{selectionEnd}
|
||||||
onselectionchange={handleSelectionChange}
|
onselectionchange={handleSelectionChange}
|
||||||
onclick={replaySound}
|
onclick={playFromPosition}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export class AudioService {
|
|||||||
private currentSource: AudioBufferSourceNode | null = null;
|
private currentSource: AudioBufferSourceNode | null = null;
|
||||||
private gainNode: GainNode | null = null;
|
private gainNode: GainNode | null = null;
|
||||||
private startTime = 0;
|
private startTime = 0;
|
||||||
|
private currentOffset = 0;
|
||||||
private isPlaying = false;
|
private isPlaying = false;
|
||||||
private onPlaybackUpdate: ((position: number) => void) | null = null;
|
private onPlaybackUpdate: ((position: number) => void) | null = null;
|
||||||
private animationFrameId: number | null = null;
|
private animationFrameId: number | null = null;
|
||||||
@ -48,7 +49,7 @@ export class AudioService {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
play(buffer: AudioBuffer): void {
|
play(buffer: AudioBuffer, offset: number = 0): void {
|
||||||
this.stop();
|
this.stop();
|
||||||
|
|
||||||
const ctx = this.getContext();
|
const ctx = this.getContext();
|
||||||
@ -57,6 +58,7 @@ export class AudioService {
|
|||||||
source.connect(this.gainNode!);
|
source.connect(this.gainNode!);
|
||||||
|
|
||||||
this.startTime = ctx.currentTime;
|
this.startTime = ctx.currentTime;
|
||||||
|
this.currentOffset = offset;
|
||||||
this.isPlaying = true;
|
this.isPlaying = true;
|
||||||
this.currentSource = source;
|
this.currentSource = source;
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ export class AudioService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
source.start();
|
source.start(0, offset);
|
||||||
this.updatePlaybackPosition();
|
this.updatePlaybackPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +86,7 @@ export class AudioService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const elapsed = this.context.currentTime - this.startTime;
|
const elapsed = this.context.currentTime - this.startTime;
|
||||||
this.onPlaybackUpdate(elapsed);
|
this.onPlaybackUpdate(elapsed + this.currentOffset);
|
||||||
|
|
||||||
this.animationFrameId = requestAnimationFrame(() => this.updatePlaybackPosition());
|
this.animationFrameId = requestAnimationFrame(() => this.updatePlaybackPosition());
|
||||||
}
|
}
|
||||||
@ -99,6 +101,7 @@ export class AudioService {
|
|||||||
this.currentSource = null;
|
this.currentSource = null;
|
||||||
}
|
}
|
||||||
this.isPlaying = false;
|
this.isPlaying = false;
|
||||||
|
this.currentOffset = 0;
|
||||||
if (this.animationFrameId !== null) {
|
if (this.animationFrameId !== null) {
|
||||||
cancelAnimationFrame(this.animationFrameId);
|
cancelAnimationFrame(this.animationFrameId);
|
||||||
this.animationFrameId = null;
|
this.animationFrameId = null;
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
selectionStart?: number | null;
|
selectionStart?: number | null;
|
||||||
selectionEnd?: number | null;
|
selectionEnd?: number | null;
|
||||||
onselectionchange?: (start: number | null, end: number | null) => void;
|
onselectionchange?: (start: number | null, end: number | null) => void;
|
||||||
onclick?: () => void;
|
onclick?: (timeOffset: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@ -75,8 +75,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onclick) {
|
if (onclick && buffer) {
|
||||||
onclick();
|
const timeOffset = (x / canvas.width) * (buffer.length / buffer.sampleRate);
|
||||||
|
onclick(timeOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export interface KeyboardActions {
|
|||||||
onVolumeIncrease?: (large: boolean) => void;
|
onVolumeIncrease?: (large: boolean) => void;
|
||||||
onEscape?: () => void;
|
onEscape?: () => void;
|
||||||
onUndo?: () => void;
|
onUndo?: () => void;
|
||||||
|
onPlayFromStart?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createKeyboardHandler(actions: KeyboardActions) {
|
export function createKeyboardHandler(actions: KeyboardActions) {
|
||||||
@ -34,6 +35,10 @@ export function createKeyboardHandler(actions: KeyboardActions) {
|
|||||||
case 's':
|
case 's':
|
||||||
actions.onDownload?.();
|
actions.onDownload?.();
|
||||||
break;
|
break;
|
||||||
|
case ' ':
|
||||||
|
event.preventDefault();
|
||||||
|
actions.onPlayFromStart?.();
|
||||||
|
break;
|
||||||
case 'arrowleft':
|
case 'arrowleft':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
actions.onDurationDecrease?.(isLargeAdjustment);
|
actions.onDurationDecrease?.(isLargeAdjustment);
|
||||||
|
|||||||
Reference in New Issue
Block a user