editor.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /// <reference path="util.ts" />
  2. /// <reference path="level.ts" />
  3. /// <reference path="audio.ts" />
  4. namespace editor {
  5. export class Editor {
  6. audioManager: audio.AudioManager;
  7. audioElement: HTMLInputElement;
  8. barElement: HTMLElement;
  9. markerListElement: HTMLElement;
  10. intervalListElement: HTMLElement;
  11. kanaElement: HTMLElement;
  12. kanjiElement: HTMLElement;
  13. jsonElement: HTMLInputElement;
  14. track: audio.Track | null = null;
  15. markers: Marker[] = [];
  16. rafId: number;
  17. constructor() {
  18. this.audioManager = new audio.AudioManager();
  19. this.audioElement = document.querySelector('#audio');
  20. this.audioElement.addEventListener('change', event => {
  21. this.loadAudio();
  22. });
  23. this.barElement = document.querySelector('.bar-overlay');
  24. this.markerListElement = document.querySelector('.markers');
  25. this.intervalListElement = document.querySelector('#intervals');
  26. this.kanaElement = document.querySelector('#kana');
  27. this.kanjiElement = document.querySelector('#kanji');
  28. this.jsonElement = document.querySelector('#json');
  29. this.markerListElement.addEventListener('click', (event: MouseEvent) => this.markersClick(event));
  30. document.querySelector('#play').addEventListener('click', () => this.play());
  31. document.querySelector('#pause').addEventListener('click', () => this.pause());
  32. document.querySelector('#insert-marker').addEventListener('click', () => this.insertMarker());
  33. document.querySelector('.bar').addEventListener('click', (event: MouseEvent) => this.scrubberClick(event));
  34. document.querySelector('#import').addEventListener('click', () => this.import());
  35. document.querySelector('#export').addEventListener('click', () => this.export());
  36. }
  37. loadAudio(): void {
  38. let file = this.audioElement.files[0];
  39. if (file != null) {
  40. if (this.track != null) {
  41. this.track.stop();
  42. }
  43. this.clearMarkers();
  44. this.audioManager.loadTrackFromFile(file).then(t => this.track = t);
  45. }
  46. }
  47. update(): void {
  48. let percentage = this.track.getTime() / this.track.getDuration() * 100;
  49. this.barElement.style.width = `${percentage}%`;
  50. if (this.track.isPlaying()) {
  51. this.rafId = requestAnimationFrame(() => this.update());
  52. }
  53. }
  54. scrubberClick(event: MouseEvent): void {
  55. let pos = event.clientX - this.markerListElement.offsetLeft;
  56. let percentage = pos / this.markerListElement.clientWidth;
  57. let targetTime = percentage * this.track.getDuration();
  58. this.track.stop();
  59. this.track.resumeTime = targetTime;
  60. this.play();
  61. }
  62. markersClick(event: MouseEvent): void {
  63. let pos = event.clientX - this.markerListElement.offsetLeft;
  64. let percentage = pos / this.markerListElement.clientWidth;
  65. let targetTime = percentage * this.track.getDuration();
  66. this.insertMarker(targetTime);
  67. }
  68. insertMarker(time: number = undefined): void {
  69. let marker = new Marker(
  70. this.track.getDuration(),
  71. (marker: Marker) => this.removeMarker(marker),
  72. (marker: Marker) => this.playMarker(marker)
  73. );
  74. if (time !== undefined) {
  75. marker.time = time;
  76. } else {
  77. marker.time = this.track.getTime();
  78. }
  79. let insertIndex = this.markers.findIndex(m => m.time > marker.time);
  80. if (insertIndex >= 0) {
  81. this.markers.splice(insertIndex, 0, marker);
  82. } else {
  83. this.markers.push(marker);
  84. }
  85. this.markerListElement.appendChild(marker.markerElement);
  86. if (insertIndex >= 0) {
  87. this.intervalListElement.insertBefore(marker.liElement, this.markers[insertIndex+1].liElement);
  88. } else {
  89. this.intervalListElement.appendChild(marker.liElement);
  90. }
  91. }
  92. play(duration: number = undefined): void {
  93. window.cancelAnimationFrame(this.rafId);
  94. this.track.start(duration);
  95. this.update();
  96. }
  97. pause(): void {
  98. this.track.pause();
  99. }
  100. playMarker(marker: Marker): void {
  101. let start = 0;
  102. let index = this.markers.findIndex(m => m == marker);
  103. if (index > 0) {
  104. start = this.markers[index - 1].time;
  105. }
  106. let duration = marker.time - start;
  107. this.track.stop();
  108. this.track.resumeTime = start;
  109. this.play(duration);
  110. }
  111. removeMarker(marker: Marker): void {
  112. let index = this.markers.findIndex(m => m == marker);
  113. this.markers.splice(index, 1);
  114. this.markerListElement.removeChild(marker.markerElement);
  115. this.intervalListElement.removeChild(marker.liElement);
  116. }
  117. clearMarkers(): void {
  118. this.markers.forEach(m => {
  119. this.markerListElement.removeChild(m.markerElement);
  120. this.intervalListElement.removeChild(m.liElement);
  121. });
  122. this.markers = [];
  123. }
  124. import(): void {
  125. this.clearMarkers();
  126. let lines: level.Line[] = JSON.parse(this.jsonElement.value);
  127. let kanji = '';
  128. let kana = '';
  129. lines.forEach(line => {
  130. kanji += line.kanji + '\n';
  131. kana += line.kana + '\n';
  132. if (line.end != undefined) {
  133. this.insertMarker(line.end);
  134. }
  135. });
  136. this.kanjiElement.value = kanji;
  137. this.kanaElement.value = kana;
  138. }
  139. export(): void {
  140. let kanji = this.kanjiElement.value.split('\n');
  141. let kana = this.kanaElement.value.split('\n');
  142. kanji.pop();
  143. kana.pop();
  144. let length = Math.max(kanji.length, kana.length, this.markers.length);
  145. let lines = [];
  146. let lastStart = 0;
  147. for (let i = 0; i < length; ++i) {
  148. let data: level.Line = {
  149. kanji: kanji[i] || '@',
  150. kana: kana[i] || '@',
  151. }
  152. if (this.markers[i]) {
  153. data.start = lastStart;
  154. data.end = this.markers[i].time;
  155. lastStart = data.end;
  156. }
  157. lines.push(data);
  158. }
  159. this.jsonElement.value = JSON.stringify(lines);
  160. }
  161. start(): void {
  162. this.loadAudio();
  163. }
  164. }
  165. class Marker {
  166. markerElement: HTMLElement;
  167. liElement: HTMLElement;
  168. inputElement: HTMLInputElement;
  169. constructor(
  170. readonly duration: number,
  171. readonly remove: (marker: Marker) => void,
  172. readonly play: (marker: Marker) => void
  173. ) {
  174. this.markerElement = document.createElement('div');
  175. this.markerElement.className = 'marker';
  176. let fragment = util.loadTemplate('interval');
  177. this.liElement = fragment.querySelector('*');
  178. this.inputElement = fragment.querySelector('.interval');
  179. this.inputElement.addEventListener('change', () => {
  180. this.time = parseFloat(this.inputElement.value);
  181. });
  182. fragment.querySelector('.play-section').addEventListener('click', () => play(this));
  183. fragment.querySelector('.remove-section').addEventListener('click', () => remove(this));
  184. }
  185. get time(): number {
  186. return parseFloat(this.inputElement.value);
  187. }
  188. set time(t: number) {
  189. this.inputElement.value = t.toFixed(1);
  190. let percentage = t * 100 / this.duration;
  191. this.markerElement.style.left = `${percentage}%`;
  192. }
  193. }
  194. }