-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.html
356 lines (320 loc) · 12 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
<title>Enhanced LRC Maker</title>
<link rel="stylesheet" type="text/css" href="screen.css">
</head>
<body>
<div id="controls">
<div>
<div class="container">
<button class='btn faster'>Faster</button>
<button class='btn slower'>Slower</button>
Speed: <span class='speed'>1.0</span>
Position: <span class='position'>-</span>
<ul>
<li><a href='#' class='import-lyrics'>Import</a></li>
<li><a href='#' class='export'>Export</a></li>
<li><a href='#' class='save'>Save</a></li>
<li><a href='#' class='show-help'>Help</a></li>
</ul>
</div>
</div>
</div>
<audio controls="controls">
</audio>
<div class="container">
<div id="lyrics"></div>
<div id="introduction">
<h1>Maker of Enhanced LRCs</h1>
<p>This tool will help you create
<a href="http://en.wikipedia.org/wiki/LRC_(file_format)#Enhanced_format">Enhanced LRC files</a>
(where every word can be assigned a timestamp).
</p>
<p>
After loading both an audio file and the lyrics text, you use
mouse or keyboard to connect the correct word with the current
playing position.
</p>
<p>
Start by dragging & dropping an audio and a text file from your
computer into this window. You can also load lyrics using the
<em>Import</em> feature.
</p>
<p>
<a class="import-lyrics">
Import lyrics
</a>
<a class="show-help">
Show detailed Help
</a>
</p>
</div>
</div>
<div id="help" class="modal">
<div class="header">
<button class="close" data-dismiss="modal">×</button>
<h3>ELRC Maker Help</h3>
</div>
<div class="body">
<p>
The general principle is that you load a text file with the
subtitles/lyrics, and you load an audio file. While playing the
audio file with reduced speed, you assigned each word with the
current playing position.
</p>
<p>
This can be done using the mouse, by simply clicking the word, or
by using the keyboard, which advances a <em>keyboard cursor</em>
through the text.
</p>
<p>
The current word based on the timestamps already set will be
highlighted in green color. If a word does not have a timestamp
assigned, it's position will be guessed based on the timestamps
already set.
</p>
<h4>Getting started</h4>
<p>
Load an audio file dragging it from your local computer and
dropping it inside the browser window. Load lyrics by dragging
a text file by dragging and dropping a text file from your local
computer. You can also load lyrics via the <em>Import</em> menu
options.
</p>
<h4>Mouse mode</h4>
<dl>
<dt>Left click on a word</dt>
<dd>Associate the word with the current timestamp
(only while playing). This also moves the keyboard cursor.
</dd>
<dt>Right click on a word</dt>
<dd>Set the play position to shortly before the word. If the
word has no time attached, the position will be guessed.
</dd>
</dl>
<h4>Keyboard mode</h4>
<dl>
<dt>Left Arrow / Right Arrow</dt>
<dd>Move the keyboard cursor forward/backwards to the text.
</dd>
<dt>Space</dt>
<dd>Associated the word the keyboard cursor is at with the
current play position. Move the keyboard cursor one word
forward.
</dd>
</dl>
<h4>Global shortcuts</h4>
<dl>
<dt>Space</dt>
<dd>Start to play.</dd>
<dt>Ctrl+Space</dt>
<dd>Stop to play.</dd>
<dt>Ctrl+Left Arrow / Ctrl+Right Arrow</dt>
<dd>Skip play position forward or backward.</dd>
<dt>Up Arrow / Down Arrow</dt>
<dd>Increase / decrease the play speed.</dd>
</dl>
</div>
<div class="footer">
<a href="#" class="button">Close</a>
</div>
</div>
<div id="import" class="modal">
<div class="header">
<button class="close" data-dismiss="modal">×</button>
<h3>Import</h3>
</div>
<div class="body">
<p>
Paste some text here. <br>
<em><i class="icon-exclamation-sign"></i>This
will overwrite your current lyrics, and remove all timestamps
that have already been assigned!</em>
</p>
<textarea></textarea><br>
</div>
<div class="footer">
<a href="#" class="button">Load</a>
</div>
</div>
<div id="export" class="modal">
<div class="header">
<button class="close" data-dismiss="modal">×</button>
<h3>Export</h3>
</div>
<div class="body">
<p>
Your file in ELRC format.<br>
<em><i class="icon-exclamation-sign"></i>Currently, this is
dumb and will break text into lines after a fixed number of
words.</em>
</p>
<textarea></textarea><br>
</div>
<div class="footer">
<a href="#" class="button">Load</a>
</div>
</div>
<script src="merged.js"></script>
<script>
$(function() {
// Currently loaded Audio/Lyrics
var lyrics = new Lyrics();
var audio = $('audio')[0];
var loadedFilename = null;
// The Lyrics object needs the duration, not available right away.
audio.addEventListener('durationchange',
function() { lyrics.duration = audio.duration; });
// Enable speed controls
function setPlaybackRate(rate) {
var newRate = audio.playbackRate;
if (rate.constructor == Number)
newRate = rate;
else
newRate += parseFloat(rate);
newRate = Math.min(Math.max(newRate, 0.5), 4.0);
audio.playbackRate = newRate;
$('#controls .speed').text(newRate.toFixed(3));
}
$('.faster').click(function() { setPlaybackRate('+0.1'); });
$('.slower').click(function() { setPlaybackRate('-0.1'); });
setPlaybackRate(1.0);
function updatePosition() {
$('.position').text(
Lyrics.toTimer(audio.currentTime) + ' / ' +
Lyrics.toTimer(audio.duration));
}
audio.addEventListener('timeupdate', updatePosition);
updatePosition();
// Enable export/import buttons
$('.import-lyrics').click(function() {
// If lyrics are currently loaded, add them to the import dialog
// to allow editing them (albeit with loss of timestamps set).
// Note: Keeping the text originally imported is not good enough,
// because it might be a format like JSON, ELRC...
if (lyrics.length) {
$('#import textarea').val(
$.map(lyrics, function(i) {return i.text}).join(' '));
}
$('#import').modal();
});
$('.export').click(function() {
$('#export textarea').val(lyrics.toELRC());
$('#export').modal();
});
$('.export').on('dragstart', function(e) {
e.originalEvent.dataTransfer.setData("DownloadURL",
"application/octet-stream:"+(loadedFilename || 'export')+
".lrc:data:application/octet-stream," +
encodeURIComponent(lyrics.toELRC()));
});
$('.show-help').click(function() { $('#help').modal(); });
$('#help .button').click(function() { $('#help').modal('hide'); });
$('.save').click(function() {
localStorage['lyrics'] = JSON.stringify(lyrics);
});
// The Lyrics controller
var lyricsBox = new LyricsBox('#lyrics', audio);
// The load-text dialog.
$('#import .button').on('click', function() {
var text = $('#import textarea').val();
// Support a special JSON format that only I am using.s
try {
var json = jQuery.parseJSON(text);
if (json.text)
text = cleanText(json.text);
}
catch (e) {}
lyrics = Lyrics.fromText(text, audio.duration);
lyricsBox.setLyrics(lyrics);
// Store in local storage, so it won't be lost in reload
localStorage['lyrics'] = JSON.stringify(lyrics);
// Hide introduction, show, show lyrics
$('#introduction').slideUp();
$('#lyrics').slideDown();
// Close dialog
$('#import').modal('hide');
});
// Load lyrics from localStorage if there is anything
if (localStorage['lyrics']) {
lyrics = Lyrics.fromJSON(localStorage['lyrics'], audio.duration);
lyricsBox.setLyrics(lyrics);
$('#introduction').hide();
$('#lyrics').show();
}
// Enable drag&drop of audio files
$(document).on('dragenter dragover', function(e) {
e.stopPropagation();
e.preventDefault();
});
$(document).on('drop', function (event) {
// originalEvent required, dataTransfer not in jQuery.event.props
var data = event.originalEvent.dataTransfer;
var audioFound, textFound = false;
for (var i = 0; i < data.files.length; i++) {
var fileReader = new FileReader();
if (data.files[i].type.indexOf('audio/') == 0) {
// Only load the first audio file
if (audioFound) continue;
audioFound = true;
var theFilename = data.files[i].fileName;
fileReader.onload = function(e) {
audio.src = e.target.result;
loadedFilename = theFilename;
};
fileReader.readAsDataURL(data.files[i]);
}
// Assume a text file
else {
// Only load the first text file
if (textFound) continue;
textFound = true;
fileReader.onload = function(e) {
$('#import textarea').val(e.target.result);
$('#import').modal();
};
fileReader.readAsText(data.files[i]);
}
}
return false;
});
// Keyboard shortcuts to skip forward/backward/change speed.
$(document).on('keydown', function(e) {
if (e.keyCode == 38) { // up
setPlaybackRate('+0.1');
return false;
}
else if (e.keyCode == 40) { // down
setPlaybackRate('-0.1');
return false;
}
else if (e.keyCode == 32) { // space
if (audio.paused) {
audio.play();
return false;
}
else if (e.ctrlKey) {
audio.pause();
return false;
}
}
else if (e.keyCode == 37) { // left
if (e.ctrlKey) {
audio.currentTime -= 1;
return false;
}
}
else if (e.keyCode == 39) { // right
if (e.ctrlKey) {
audio.currentTime += 1;
return false;
}
}
});
window.geLyrics = function() { return lyrics };
});
</script>
</body>
</html>