|
@@ -1,358 +1,387 @@
|
|
-/// <reference path="util.ts" />
|
|
|
|
-/// <reference path="level.ts" />
|
|
|
|
-/// <reference path="audio.ts" />
|
|
|
|
-
|
|
|
|
-namespace editor {
|
|
|
|
- export class Editor {
|
|
|
|
- audioManager: audio.AudioManager;
|
|
|
|
- urlElement: HTMLInputElement;
|
|
|
|
- loadElement: HTMLButtonElement;
|
|
|
|
- audioElement: HTMLInputElement;
|
|
|
|
- barElement: HTMLElement;
|
|
|
|
- markerListElement: HTMLElement;
|
|
|
|
- intervalListElement: HTMLElement;
|
|
|
|
- kanaElement: HTMLTextAreaElement;
|
|
|
|
- kanjiElement: HTMLTextAreaElement;
|
|
|
|
- displayElement: HTMLElement;
|
|
|
|
- jsonElement: HTMLInputElement;
|
|
|
|
- waveFormContainer: HTMLDivElement;
|
|
|
|
- track: audio.Track | null = null;
|
|
|
|
- markers: Marker[] = [];
|
|
|
|
- waveForm: WaveForm;
|
|
|
|
- currentMarker: Marker | null = null;
|
|
|
|
-
|
|
|
|
- constructor() {
|
|
|
|
- this.audioManager = new audio.AudioManager();
|
|
|
|
- this.urlElement = util.getElement(document, '#url');
|
|
|
|
- this.loadElement = util.getElement(document, '#load');
|
|
|
|
- this.loadElement.addEventListener('click', event => {
|
|
|
|
- this.loadAudio();
|
|
|
|
- });
|
|
|
|
- this.audioElement = util.getElement(document, '#audio');
|
|
|
|
- this.audioElement.addEventListener('change', event => {
|
|
|
|
- this.urlElement.value = '';
|
|
|
|
- this.loadAudio();
|
|
|
|
- });
|
|
|
|
- this.barElement = util.getElement(document, '.bar-overlay');
|
|
|
|
- this.markerListElement = util.getElement(document, '.markers');
|
|
|
|
- this.intervalListElement = util.getElement(document, '#intervals');
|
|
|
|
- this.kanaElement = util.getElement(document, '#kana');
|
|
|
|
- this.kanjiElement = util.getElement(document, '#kanji');
|
|
|
|
- this.displayElement = util.getElement(document, '#display');
|
|
|
|
- this.jsonElement = util.getElement(document, '#json');
|
|
|
|
- this.waveFormContainer = util.getElement(document, '.waveform-container');
|
|
|
|
- this.waveForm = new WaveForm(
|
|
|
|
- util.getElement(document, '#waveform'),
|
|
|
|
- util.getElement(document, '#waveform-overlay'),
|
|
|
|
- (time: number) => this.play(time)
|
|
|
|
|
|
+import * as audio from './audio';
|
|
|
|
+import * as level from './level';
|
|
|
|
+import * as util from './util';
|
|
|
|
+import * as youtube from './youtube';
|
|
|
|
+
|
|
|
|
+export class Editor {
|
|
|
|
+ audioManager: audio.AudioManager;
|
|
|
|
+ urlElement: HTMLInputElement;
|
|
|
|
+ loadElement: HTMLButtonElement;
|
|
|
|
+ audioElement: HTMLInputElement;
|
|
|
|
+ barElement: HTMLElement;
|
|
|
|
+ markerListElement: HTMLElement;
|
|
|
|
+ intervalListElement: HTMLElement;
|
|
|
|
+ kanaElement: HTMLTextAreaElement;
|
|
|
|
+ kanjiElement: HTMLTextAreaElement;
|
|
|
|
+ displayElement: HTMLElement;
|
|
|
|
+ jsonElement: HTMLInputElement;
|
|
|
|
+ waveFormContainer: HTMLDivElement;
|
|
|
|
+ track: audio.Track | null = null;
|
|
|
|
+ markers: Marker[] = [];
|
|
|
|
+ waveForm: WaveForm;
|
|
|
|
+ currentMarker: Marker | null = null;
|
|
|
|
+
|
|
|
|
+ constructor() {
|
|
|
|
+ this.audioManager = new audio.AudioManager();
|
|
|
|
+ this.urlElement = util.getElement(document, '#url');
|
|
|
|
+ this.loadElement = util.getElement(document, '#load');
|
|
|
|
+ this.loadElement.addEventListener('click', (event) => {
|
|
|
|
+ this.loadAudio();
|
|
|
|
+ });
|
|
|
|
+ this.audioElement = util.getElement(document, '#audio');
|
|
|
|
+ this.audioElement.addEventListener('change', (event) => {
|
|
|
|
+ this.urlElement.value = '';
|
|
|
|
+ this.loadAudio();
|
|
|
|
+ });
|
|
|
|
+ this.barElement = util.getElement(document, '.bar-overlay');
|
|
|
|
+ this.markerListElement = util.getElement(document, '.markers');
|
|
|
|
+ this.intervalListElement = util.getElement(document, '#intervals');
|
|
|
|
+ this.kanaElement = util.getElement(document, '#kana');
|
|
|
|
+ this.kanjiElement = util.getElement(document, '#kanji');
|
|
|
|
+ this.displayElement = util.getElement(document, '#display');
|
|
|
|
+ this.jsonElement = util.getElement(document, '#json');
|
|
|
|
+ this.waveFormContainer = util.getElement(document, '.waveform-container');
|
|
|
|
+ this.waveForm = new WaveForm(
|
|
|
|
+ util.getElement(document, '#waveform'),
|
|
|
|
+ util.getElement(document, '#waveform-overlay'),
|
|
|
|
+ (time: number) => this.play(time)
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ this.markerListElement.addEventListener('click', (event: MouseEvent) =>
|
|
|
|
+ this.markersClick(event)
|
|
|
|
+ );
|
|
|
|
+ document
|
|
|
|
+ .querySelector('#play')!
|
|
|
|
+ .addEventListener('click', () => this.play());
|
|
|
|
+ document
|
|
|
|
+ .querySelector('#pause')!
|
|
|
|
+ .addEventListener('click', () => this.pause());
|
|
|
|
+ document
|
|
|
|
+ .querySelector('#insert-marker')!
|
|
|
|
+ .addEventListener('click', () => this.insertMarker());
|
|
|
|
+ document
|
|
|
|
+ .querySelector<HTMLElement>('.bar')!
|
|
|
|
+ .addEventListener('click', (event: MouseEvent) =>
|
|
|
|
+ this.scrubberClick(event)
|
|
);
|
|
);
|
|
|
|
+ document
|
|
|
|
+ .querySelector('#import')!
|
|
|
|
+ .addEventListener('click', () => this.import());
|
|
|
|
+ document
|
|
|
|
+ .querySelector('#export')!
|
|
|
|
+ .addEventListener('click', () => this.export());
|
|
|
|
+
|
|
|
|
+ this.update();
|
|
|
|
+ }
|
|
|
|
|
|
- this.markerListElement.addEventListener('click', (event: MouseEvent) => this.markersClick(event));
|
|
|
|
- document.querySelector('#play')!.addEventListener('click', () => this.play());
|
|
|
|
- document.querySelector('#pause')!.addEventListener('click', () => this.pause());
|
|
|
|
- document.querySelector('#insert-marker')!.addEventListener('click', () => this.insertMarker());
|
|
|
|
- document.querySelector<HTMLElement>('.bar')!.addEventListener('click', (event: MouseEvent) => this.scrubberClick(event));
|
|
|
|
- document.querySelector('#import')!.addEventListener('click', () => this.import());
|
|
|
|
- document.querySelector('#export')!.addEventListener('click', () => this.export());
|
|
|
|
-
|
|
|
|
- this.update();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- loadAudio(): void {
|
|
|
|
- const url = this.urlElement.value;
|
|
|
|
- if (url != '') {
|
|
|
|
- const videoId = youtube.getVideoId(url);
|
|
|
|
- if (videoId !== null) {
|
|
|
|
- const element = util.getElement(document, '#youtube');
|
|
|
|
- this.audioManager.loadTrackFromYoutube(videoId, element, () => {}).then(t => {
|
|
|
|
|
|
+ loadAudio(): void {
|
|
|
|
+ const url = this.urlElement.value;
|
|
|
|
+ if (url != '') {
|
|
|
|
+ const videoId = youtube.getVideoId(url);
|
|
|
|
+ if (videoId !== null) {
|
|
|
|
+ const element = util.getElement(document, '#youtube');
|
|
|
|
+ this.audioManager
|
|
|
|
+ .loadTrackFromYoutube(videoId, element, () => {})
|
|
|
|
+ .then((t) => {
|
|
this.track = t;
|
|
this.track = t;
|
|
this.waveForm.clear();
|
|
this.waveForm.clear();
|
|
this.waveFormContainer.style.display = 'none';
|
|
this.waveFormContainer.style.display = 'none';
|
|
});
|
|
});
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- let file = this.audioElement.files![0];
|
|
|
|
- if (file != null) {
|
|
|
|
- if (this.track != null) {
|
|
|
|
- this.track.stop();
|
|
|
|
- }
|
|
|
|
- this.clearMarkers();
|
|
|
|
- this.audioManager.loadTrackFromFile(file).then(t => {
|
|
|
|
- this.track = t;
|
|
|
|
- this.waveForm.setTrack(t);
|
|
|
|
- this.waveFormContainer.style.display = 'block';
|
|
|
|
- });
|
|
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- update(): void {
|
|
|
|
|
|
+ let file = this.audioElement.files![0];
|
|
|
|
+ if (file != null) {
|
|
if (this.track != null) {
|
|
if (this.track != null) {
|
|
- let percentage = this.track.getTime() / this.track.getDuration() * 100;
|
|
|
|
- this.barElement.style.width = `${percentage}%`;
|
|
|
|
- if (this.track instanceof audio.FileTrack) {
|
|
|
|
- this.waveForm.update(this.markers);
|
|
|
|
- }
|
|
|
|
- if (this.currentMarker) {
|
|
|
|
- this.currentMarker.liElement.className = '';
|
|
|
|
- }
|
|
|
|
- let index = this.markers.findIndex(m => m.time > this.track!.getTime());
|
|
|
|
- if (index < 0) index = 0;
|
|
|
|
- this.currentMarker = this.markers[index - 1];
|
|
|
|
- if (this.currentMarker) {
|
|
|
|
- this.currentMarker.liElement.className = 'highlight';
|
|
|
|
- }
|
|
|
|
- let text = this.kanjiElement.value.split('\n')[index] || '';
|
|
|
|
- this.displayElement.textContent = text;
|
|
|
|
|
|
+ this.track.stop();
|
|
}
|
|
}
|
|
- requestAnimationFrame(() => this.update());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- scrubberClick(event: MouseEvent): void {
|
|
|
|
- let pos = event.clientX - 10;
|
|
|
|
- console.log(pos);
|
|
|
|
- let percentage = pos / this.markerListElement.clientWidth;
|
|
|
|
- let targetTime = percentage * this.track!.getDuration();
|
|
|
|
- this.play(targetTime);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- markersClick(event: MouseEvent): void {
|
|
|
|
- let pos = event.clientX - 10;
|
|
|
|
- let percentage = pos / this.markerListElement.clientWidth;
|
|
|
|
- let targetTime = percentage * this.track!.getDuration();
|
|
|
|
- this.insertMarker(targetTime);
|
|
|
|
|
|
+ this.clearMarkers();
|
|
|
|
+ this.audioManager.loadTrackFromFile(file).then((t) => {
|
|
|
|
+ this.track = t;
|
|
|
|
+ this.waveForm.setTrack(t);
|
|
|
|
+ this.waveFormContainer.style.display = 'block';
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- insertMarker(time?: number): void {
|
|
|
|
- let marker = new Marker(
|
|
|
|
- this.track!.getDuration(),
|
|
|
|
- (marker: Marker) => this.removeMarker(marker),
|
|
|
|
- (marker: Marker) => this.playMarker(marker)
|
|
|
|
- );
|
|
|
|
- if (time !== undefined) {
|
|
|
|
- marker.time = time;
|
|
|
|
- } else {
|
|
|
|
- marker.time = this.track!.getTime();
|
|
|
|
|
|
+ update(): void {
|
|
|
|
+ if (this.track != null) {
|
|
|
|
+ let percentage = (this.track.getTime() / this.track.getDuration()) * 100;
|
|
|
|
+ this.barElement.style.width = `${percentage}%`;
|
|
|
|
+ if (this.track instanceof audio.FileTrack) {
|
|
|
|
+ this.waveForm.update(this.markers);
|
|
}
|
|
}
|
|
- let insertIndex = this.markers.findIndex(m => m.time > marker.time);
|
|
|
|
- if (insertIndex >= 0) {
|
|
|
|
- this.markers.splice(insertIndex, 0, marker);
|
|
|
|
- } else {
|
|
|
|
- this.markers.push(marker);
|
|
|
|
|
|
+ if (this.currentMarker) {
|
|
|
|
+ this.currentMarker.liElement.className = '';
|
|
}
|
|
}
|
|
- this.markerListElement.appendChild(marker.markerElement);
|
|
|
|
- if (insertIndex >= 0) {
|
|
|
|
- this.intervalListElement.insertBefore(marker.liElement, this.markers[insertIndex+1].liElement);
|
|
|
|
- } else {
|
|
|
|
- this.intervalListElement.appendChild(marker.liElement);
|
|
|
|
|
|
+ let index = this.markers.findIndex((m) => m.time > this.track!.getTime());
|
|
|
|
+ if (index < 0) index = 0;
|
|
|
|
+ this.currentMarker = this.markers[index - 1];
|
|
|
|
+ if (this.currentMarker) {
|
|
|
|
+ this.currentMarker.liElement.className = 'highlight';
|
|
}
|
|
}
|
|
|
|
+ let text = this.kanjiElement.value.split('\n')[index] || '';
|
|
|
|
+ this.displayElement.textContent = text;
|
|
}
|
|
}
|
|
|
|
+ requestAnimationFrame(() => this.update());
|
|
|
|
+ }
|
|
|
|
|
|
- play(start?: number, duration?: number): void {
|
|
|
|
- this.track!.pause();
|
|
|
|
- this.track!.start(start, duration);
|
|
|
|
- }
|
|
|
|
|
|
+ scrubberClick(event: MouseEvent): void {
|
|
|
|
+ let pos = event.clientX - 10;
|
|
|
|
+ console.log(pos);
|
|
|
|
+ let percentage = pos / this.markerListElement.clientWidth;
|
|
|
|
+ let targetTime = percentage * this.track!.getDuration();
|
|
|
|
+ this.play(targetTime);
|
|
|
|
+ }
|
|
|
|
|
|
- pause(): void {
|
|
|
|
- this.track!.pause();
|
|
|
|
|
|
+ markersClick(event: MouseEvent): void {
|
|
|
|
+ let pos = event.clientX - 10;
|
|
|
|
+ let percentage = pos / this.markerListElement.clientWidth;
|
|
|
|
+ let targetTime = percentage * this.track!.getDuration();
|
|
|
|
+ this.insertMarker(targetTime);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ insertMarker(time?: number): void {
|
|
|
|
+ let marker = new Marker(
|
|
|
|
+ this.track!.getDuration(),
|
|
|
|
+ (marker: Marker) => this.removeMarker(marker),
|
|
|
|
+ (marker: Marker) => this.playMarker(marker)
|
|
|
|
+ );
|
|
|
|
+ if (time !== undefined) {
|
|
|
|
+ marker.time = time;
|
|
|
|
+ } else {
|
|
|
|
+ marker.time = this.track!.getTime();
|
|
|
|
+ }
|
|
|
|
+ let insertIndex = this.markers.findIndex((m) => m.time > marker.time);
|
|
|
|
+ if (insertIndex >= 0) {
|
|
|
|
+ this.markers.splice(insertIndex, 0, marker);
|
|
|
|
+ } else {
|
|
|
|
+ this.markers.push(marker);
|
|
|
|
+ }
|
|
|
|
+ this.markerListElement.appendChild(marker.markerElement);
|
|
|
|
+ if (insertIndex >= 0) {
|
|
|
|
+ this.intervalListElement.insertBefore(
|
|
|
|
+ marker.liElement,
|
|
|
|
+ this.markers[insertIndex + 1].liElement
|
|
|
|
+ );
|
|
|
|
+ } else {
|
|
|
|
+ this.intervalListElement.appendChild(marker.liElement);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- playMarker(marker: Marker): void {
|
|
|
|
- let start = marker.time;
|
|
|
|
- let end = this.track!.getDuration();
|
|
|
|
- let index = this.markers.findIndex(m => m == marker);
|
|
|
|
- if (index < this.markers.length - 1) {
|
|
|
|
- end = this.markers[index + 1].time;
|
|
|
|
- }
|
|
|
|
- let duration = end - start;
|
|
|
|
- this.play(start, duration);
|
|
|
|
|
|
+ play(start?: number, duration?: number): void {
|
|
|
|
+ this.track!.pause();
|
|
|
|
+ this.track!.start(start, duration);
|
|
|
|
+ }
|
|
|
|
|
|
- this.highlightLine(this.kanjiElement, index + 1);
|
|
|
|
- }
|
|
|
|
|
|
+ pause(): void {
|
|
|
|
+ this.track!.pause();
|
|
|
|
+ }
|
|
|
|
|
|
- removeMarker(marker: Marker): void {
|
|
|
|
- let index = this.markers.findIndex(m => m == marker);
|
|
|
|
- this.markers.splice(index, 1);
|
|
|
|
- this.markerListElement.removeChild(marker.markerElement);
|
|
|
|
- this.intervalListElement.removeChild(marker.liElement);
|
|
|
|
|
|
+ playMarker(marker: Marker): void {
|
|
|
|
+ let start = marker.time;
|
|
|
|
+ let end = this.track!.getDuration();
|
|
|
|
+ let index = this.markers.findIndex((m) => m == marker);
|
|
|
|
+ if (index < this.markers.length - 1) {
|
|
|
|
+ end = this.markers[index + 1].time;
|
|
}
|
|
}
|
|
|
|
+ let duration = end - start;
|
|
|
|
+ this.play(start, duration);
|
|
|
|
|
|
- clearMarkers(): void {
|
|
|
|
- this.markers.forEach(m => {
|
|
|
|
- this.markerListElement.removeChild(m.markerElement);
|
|
|
|
- this.intervalListElement.removeChild(m.liElement);
|
|
|
|
- });
|
|
|
|
- this.markers = [];
|
|
|
|
- }
|
|
|
|
|
|
+ this.highlightLine(this.kanjiElement, index + 1);
|
|
|
|
+ }
|
|
|
|
|
|
- highlightLine(element: HTMLTextAreaElement, line: number) {
|
|
|
|
- let text = element.value;
|
|
|
|
- let index = 0;
|
|
|
|
- for (let i = 0; i < line; ++i) {
|
|
|
|
- index = text.indexOf('\n', index + 1);
|
|
|
|
- }
|
|
|
|
- let endIndex = text.indexOf('\n', index + 1);
|
|
|
|
- element.focus();
|
|
|
|
- element.setSelectionRange(index, endIndex);
|
|
|
|
- }
|
|
|
|
|
|
+ removeMarker(marker: Marker): void {
|
|
|
|
+ let index = this.markers.findIndex((m) => m == marker);
|
|
|
|
+ this.markers.splice(index, 1);
|
|
|
|
+ this.markerListElement.removeChild(marker.markerElement);
|
|
|
|
+ this.intervalListElement.removeChild(marker.liElement);
|
|
|
|
+ }
|
|
|
|
|
|
- import(): void {
|
|
|
|
- this.clearMarkers();
|
|
|
|
- let lines: level.Line[] = JSON.parse(this.jsonElement.value);
|
|
|
|
- let kanji = '';
|
|
|
|
- let kana = '';
|
|
|
|
-
|
|
|
|
- lines.forEach(line => {
|
|
|
|
- kanji += line.kanji + '\n';
|
|
|
|
- kana += line.kana + '\n';
|
|
|
|
- if (line.end != undefined) {
|
|
|
|
- this.insertMarker(line.end);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ clearMarkers(): void {
|
|
|
|
+ this.markers.forEach((m) => {
|
|
|
|
+ this.markerListElement.removeChild(m.markerElement);
|
|
|
|
+ this.intervalListElement.removeChild(m.liElement);
|
|
|
|
+ });
|
|
|
|
+ this.markers = [];
|
|
|
|
+ }
|
|
|
|
|
|
- this.kanjiElement.value = kanji;
|
|
|
|
- this.kanaElement.value = kana;
|
|
|
|
|
|
+ highlightLine(element: HTMLTextAreaElement, line: number) {
|
|
|
|
+ let text = element.value;
|
|
|
|
+ let index = 0;
|
|
|
|
+ for (let i = 0; i < line; ++i) {
|
|
|
|
+ index = text.indexOf('\n', index + 1);
|
|
}
|
|
}
|
|
|
|
+ let endIndex = text.indexOf('\n', index + 1);
|
|
|
|
+ element.focus();
|
|
|
|
+ element.setSelectionRange(index, endIndex);
|
|
|
|
+ }
|
|
|
|
|
|
- export(): void {
|
|
|
|
- let kanji = this.kanjiElement.value.split('\n');
|
|
|
|
- let kana = this.kanaElement.value.split('\n');
|
|
|
|
- let length = Math.max(kanji.length, kana.length, this.markers.length - 1);
|
|
|
|
-
|
|
|
|
- let lines = [];
|
|
|
|
- let lastStart = 0;
|
|
|
|
- for (let i = 0; i < length; ++i) {
|
|
|
|
- let data: level.Line = {
|
|
|
|
- kanji: kanji[i] || '@',
|
|
|
|
- kana: kana[i] || '@',
|
|
|
|
- }
|
|
|
|
- if (this.markers[i]) {
|
|
|
|
- data.start = lastStart;
|
|
|
|
- data.end = this.markers[i].time;
|
|
|
|
- lastStart = data.end;
|
|
|
|
- }
|
|
|
|
- lines.push(data);
|
|
|
|
|
|
+ import(): void {
|
|
|
|
+ this.clearMarkers();
|
|
|
|
+ let lines: level.Line[] = JSON.parse(this.jsonElement.value);
|
|
|
|
+ let kanji = '';
|
|
|
|
+ let kana = '';
|
|
|
|
+
|
|
|
|
+ lines.forEach((line) => {
|
|
|
|
+ kanji += line.kanji + '\n';
|
|
|
|
+ kana += line.kana + '\n';
|
|
|
|
+ if (line.end != undefined) {
|
|
|
|
+ this.insertMarker(line.end);
|
|
}
|
|
}
|
|
|
|
+ });
|
|
|
|
|
|
- this.jsonElement.value = JSON.stringify(lines);
|
|
|
|
- }
|
|
|
|
|
|
+ this.kanjiElement.value = kanji;
|
|
|
|
+ this.kanaElement.value = kana;
|
|
|
|
+ }
|
|
|
|
|
|
- start(): void {
|
|
|
|
- this.loadAudio();
|
|
|
|
|
|
+ export(): void {
|
|
|
|
+ let kanji = this.kanjiElement.value.split('\n');
|
|
|
|
+ let kana = this.kanaElement.value.split('\n');
|
|
|
|
+ let length = Math.max(kanji.length, kana.length, this.markers.length - 1);
|
|
|
|
+
|
|
|
|
+ let lines = [];
|
|
|
|
+ let lastStart = 0;
|
|
|
|
+ for (let i = 0; i < length; ++i) {
|
|
|
|
+ let data: level.Line = {
|
|
|
|
+ kanji: kanji[i] || '@',
|
|
|
|
+ kana: kana[i] || '@',
|
|
|
|
+ };
|
|
|
|
+ if (this.markers[i]) {
|
|
|
|
+ data.start = lastStart;
|
|
|
|
+ data.end = this.markers[i].time;
|
|
|
|
+ lastStart = data.end;
|
|
|
|
+ }
|
|
|
|
+ lines.push(data);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ this.jsonElement.value = JSON.stringify(lines);
|
|
}
|
|
}
|
|
|
|
|
|
- class Marker {
|
|
|
|
- markerElement: HTMLElement;
|
|
|
|
- liElement: HTMLElement;
|
|
|
|
- inputElement: HTMLInputElement;
|
|
|
|
-
|
|
|
|
- constructor(
|
|
|
|
- readonly duration: number,
|
|
|
|
- readonly remove: (marker: Marker) => void,
|
|
|
|
- readonly play: (marker: Marker) => void
|
|
|
|
- ) {
|
|
|
|
- this.markerElement = document.createElement('div');
|
|
|
|
- this.markerElement.className = 'marker';
|
|
|
|
-
|
|
|
|
- let fragment = util.loadTemplate(document, 'interval');
|
|
|
|
- this.liElement = util.getElement(fragment, '*');
|
|
|
|
- this.inputElement = util.getElement(fragment, '.interval');
|
|
|
|
- this.inputElement.addEventListener('change', () => {
|
|
|
|
- this.time = parseFloat(this.inputElement.value);
|
|
|
|
- });
|
|
|
|
|
|
+ start(): void {
|
|
|
|
+ this.loadAudio();
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
- fragment.querySelector('.play-section')!.addEventListener('click', () => play(this));
|
|
|
|
- fragment.querySelector('.remove-section')!.addEventListener('click', () => remove(this));
|
|
|
|
- }
|
|
|
|
|
|
+class Marker {
|
|
|
|
+ markerElement: HTMLElement;
|
|
|
|
+ liElement: HTMLElement;
|
|
|
|
+ inputElement: HTMLInputElement;
|
|
|
|
+
|
|
|
|
+ constructor(
|
|
|
|
+ readonly duration: number,
|
|
|
|
+ readonly remove: (marker: Marker) => void,
|
|
|
|
+ readonly play: (marker: Marker) => void
|
|
|
|
+ ) {
|
|
|
|
+ this.markerElement = document.createElement('div');
|
|
|
|
+ this.markerElement.className = 'marker';
|
|
|
|
+
|
|
|
|
+ let fragment = util.loadTemplate(document, 'interval');
|
|
|
|
+ this.liElement = util.getElement(fragment, '*');
|
|
|
|
+ this.inputElement = util.getElement(fragment, '.interval');
|
|
|
|
+ this.inputElement.addEventListener('change', () => {
|
|
|
|
+ this.time = parseFloat(this.inputElement.value);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ fragment
|
|
|
|
+ .querySelector('.play-section')!
|
|
|
|
+ .addEventListener('click', () => play(this));
|
|
|
|
+ fragment
|
|
|
|
+ .querySelector('.remove-section')!
|
|
|
|
+ .addEventListener('click', () => remove(this));
|
|
|
|
+ }
|
|
|
|
|
|
- get time(): number {
|
|
|
|
- return parseFloat(this.inputElement.value);
|
|
|
|
- }
|
|
|
|
|
|
+ get time(): number {
|
|
|
|
+ return parseFloat(this.inputElement.value);
|
|
|
|
+ }
|
|
|
|
|
|
- set time(t: number) {
|
|
|
|
- this.inputElement.value = t.toFixed(1);
|
|
|
|
- let percentage = t * 100 / this.duration;
|
|
|
|
- this.markerElement.style.left = `${percentage}%`;
|
|
|
|
- }
|
|
|
|
|
|
+ set time(t: number) {
|
|
|
|
+ this.inputElement.value = t.toFixed(1);
|
|
|
|
+ let percentage = (t * 100) / this.duration;
|
|
|
|
+ this.markerElement.style.left = `${percentage}%`;
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
- class WaveForm {
|
|
|
|
- ctx: CanvasRenderingContext2D;
|
|
|
|
- overlayCtx: CanvasRenderingContext2D;
|
|
|
|
- track: audio.FileTrack | null = null;
|
|
|
|
- data: Float32Array | null = null;
|
|
|
|
- stride: number = 0;
|
|
|
|
- currentSection: number = -1;
|
|
|
|
-
|
|
|
|
- constructor(
|
|
|
|
- readonly canvas: HTMLCanvasElement,
|
|
|
|
- readonly overlay: HTMLCanvasElement,
|
|
|
|
- readonly setTime: (time: number) => void
|
|
|
|
- ) {
|
|
|
|
- canvas.height = canvas.clientHeight;
|
|
|
|
- canvas.width = canvas.clientWidth;
|
|
|
|
- overlay.height = overlay.clientHeight;
|
|
|
|
- overlay.width = overlay.clientWidth;
|
|
|
|
- this.ctx = canvas.getContext('2d')!;
|
|
|
|
- this.overlayCtx = overlay.getContext('2d')!;
|
|
|
|
-
|
|
|
|
- this.overlayCtx.fillStyle = 'rgba(255, 0, 0, 0.5)';
|
|
|
|
-
|
|
|
|
- this.overlay.addEventListener('click', (event: MouseEvent) => {
|
|
|
|
- let pos = event.clientX - this.overlay.offsetLeft;
|
|
|
|
- let percentage = pos / this.overlay.width;
|
|
|
|
- let time = this.currentSection * 5 + percentage * 5;
|
|
|
|
- this.setTime(time);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
|
|
+class WaveForm {
|
|
|
|
+ ctx: CanvasRenderingContext2D;
|
|
|
|
+ overlayCtx: CanvasRenderingContext2D;
|
|
|
|
+ track: audio.FileTrack | null = null;
|
|
|
|
+ data: Float32Array | null = null;
|
|
|
|
+ stride: number = 0;
|
|
|
|
+ currentSection: number = -1;
|
|
|
|
+
|
|
|
|
+ constructor(
|
|
|
|
+ readonly canvas: HTMLCanvasElement,
|
|
|
|
+ readonly overlay: HTMLCanvasElement,
|
|
|
|
+ readonly setTime: (time: number) => void
|
|
|
|
+ ) {
|
|
|
|
+ canvas.height = canvas.clientHeight;
|
|
|
|
+ canvas.width = canvas.clientWidth;
|
|
|
|
+ overlay.height = overlay.clientHeight;
|
|
|
|
+ overlay.width = overlay.clientWidth;
|
|
|
|
+ this.ctx = canvas.getContext('2d')!;
|
|
|
|
+ this.overlayCtx = overlay.getContext('2d')!;
|
|
|
|
+
|
|
|
|
+ this.overlayCtx.fillStyle = 'rgba(255, 0, 0, 0.5)';
|
|
|
|
+
|
|
|
|
+ this.overlay.addEventListener('click', (event: MouseEvent) => {
|
|
|
|
+ let pos = event.clientX - this.overlay.offsetLeft;
|
|
|
|
+ let percentage = pos / this.overlay.width;
|
|
|
|
+ let time = this.currentSection * 5 + percentage * 5;
|
|
|
|
+ this.setTime(time);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
|
|
- clear(): void {
|
|
|
|
- this.track = null;
|
|
|
|
- }
|
|
|
|
|
|
+ clear(): void {
|
|
|
|
+ this.track = null;
|
|
|
|
+ }
|
|
|
|
|
|
- setTrack(track: audio.FileTrack): void {
|
|
|
|
- this.track = track;
|
|
|
|
- this.stride = Math.floor(this.track.buffer.sampleRate / this.canvas.width * 5);
|
|
|
|
- this.currentSection = -1;
|
|
|
|
- }
|
|
|
|
|
|
+ setTrack(track: audio.FileTrack): void {
|
|
|
|
+ this.track = track;
|
|
|
|
+ this.stride = Math.floor(
|
|
|
|
+ (this.track.buffer.sampleRate / this.canvas.width) * 5
|
|
|
|
+ );
|
|
|
|
+ this.currentSection = -1;
|
|
|
|
+ }
|
|
|
|
|
|
- timeToX(time: number): number {
|
|
|
|
- return (time - this.currentSection * 5) / 5 * this.canvas.width
|
|
|
|
- }
|
|
|
|
|
|
+ timeToX(time: number): number {
|
|
|
|
+ return ((time - this.currentSection * 5) / 5) * this.canvas.width;
|
|
|
|
+ }
|
|
|
|
|
|
- update(markers: Marker[]): void {
|
|
|
|
- let section = Math.floor(this.track!.getTime() / 5);
|
|
|
|
-
|
|
|
|
- let height = this.canvas.height;
|
|
|
|
- if (this.currentSection != section) {
|
|
|
|
- this.data = this.track!.buffer.getChannelData(0);
|
|
|
|
- this.ctx.clearRect(0, 0, this.canvas.width, height);
|
|
|
|
- this.ctx.beginPath();
|
|
|
|
- this.ctx.moveTo(0, height / 2);
|
|
|
|
- let offset = section * this.canvas.width * this.stride;
|
|
|
|
- for (let i = 0; i < this.canvas.width; ++i) {
|
|
|
|
- let index = offset + i * this.stride;
|
|
|
|
- let value = height / 2 + height / 2 * this.data[index];
|
|
|
|
- this.ctx.lineTo(i, value);
|
|
|
|
- }
|
|
|
|
- this.ctx.stroke();
|
|
|
|
- this.currentSection = section;
|
|
|
|
|
|
+ update(markers: Marker[]): void {
|
|
|
|
+ let section = Math.floor(this.track!.getTime() / 5);
|
|
|
|
+
|
|
|
|
+ let height = this.canvas.height;
|
|
|
|
+ if (this.currentSection != section) {
|
|
|
|
+ this.data = this.track!.buffer.getChannelData(0);
|
|
|
|
+ this.ctx.clearRect(0, 0, this.canvas.width, height);
|
|
|
|
+ this.ctx.beginPath();
|
|
|
|
+ this.ctx.moveTo(0, height / 2);
|
|
|
|
+ let offset = section * this.canvas.width * this.stride;
|
|
|
|
+ for (let i = 0; i < this.canvas.width; ++i) {
|
|
|
|
+ let index = offset + i * this.stride;
|
|
|
|
+ let value = height / 2 + (height / 2) * this.data[index];
|
|
|
|
+ this.ctx.lineTo(i, value);
|
|
}
|
|
}
|
|
-
|
|
|
|
- let marker = this.timeToX(this.track!.getTime());
|
|
|
|
- this.overlayCtx.clearRect(0, 0, this.canvas.width, height);
|
|
|
|
- this.overlayCtx.fillRect(0, 0, marker, height);
|
|
|
|
- markers.forEach(m => {
|
|
|
|
- if (m.time > section * 5 && m.time <= section * 5 + 5) {
|
|
|
|
- let x = this.timeToX(m.time);
|
|
|
|
- this.overlayCtx.beginPath();
|
|
|
|
- this.overlayCtx.moveTo(x, 0);
|
|
|
|
- this.overlayCtx.lineTo(x, height);
|
|
|
|
- this.overlayCtx.stroke();
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ this.ctx.stroke();
|
|
|
|
+ this.currentSection = section;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ let marker = this.timeToX(this.track!.getTime());
|
|
|
|
+ this.overlayCtx.clearRect(0, 0, this.canvas.width, height);
|
|
|
|
+ this.overlayCtx.fillRect(0, 0, marker, height);
|
|
|
|
+ markers.forEach((m) => {
|
|
|
|
+ if (m.time > section * 5 && m.time <= section * 5 + 5) {
|
|
|
|
+ let x = this.timeToX(m.time);
|
|
|
|
+ this.overlayCtx.beginPath();
|
|
|
|
+ this.overlayCtx.moveTo(x, 0);
|
|
|
|
+ this.overlayCtx.lineTo(x, height);
|
|
|
|
+ this.overlayCtx.stroke();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+let e = new Editor();
|
|
|
|
+e.start();
|