Skip to content

Commit c802445

Browse files
committed
Initial start at sharing audio
1 parent d5932f7 commit c802445

4 files changed

Lines changed: 202 additions & 1 deletion

File tree

package-lock.json

Lines changed: 53 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"devDependencies": {
2525
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
2626
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
27+
"@ffmpeg/ffmpeg": "^0.12.1",
28+
"@ffmpeg/util": "^0.12.0",
2729
"@sveltejs/adapter-static": "^2",
2830
"@sveltejs/kit": "^1",
2931
"@tailwindcss/typography": "^0.5.2",
@@ -60,6 +62,6 @@
6062
"vite": "^4"
6163
},
6264
"volta": {
63-
"node": "16.17.0"
65+
"node": "18.17.0"
6466
}
6567
}

src/lib/data/audio-convert.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { FFmpeg } from '@ffmpeg/ffmpeg';
2+
import type { FileData } from '@ffmpeg/ffmpeg/dist/esm/types';
3+
import { toBlobURL, fetchFile } from '@ffmpeg/util';
4+
5+
//
6+
//-y -i /data/user/0/com.example.app.scripture/cache/B01___01_Matthew_____ENGWEBN2DA.mp3
7+
//-map_metadata 0 -ss 00:00:20.120 -to 00:00:28.960
8+
//-map 0:a -acodec copy -write_xing 0
9+
// "/storage/emulated/0/Android/data/com.example.app.scripture/files/WEB Gospels/Matthew_1_3.mp3"
10+
11+
let ffmpeg: FFmpeg = null;
12+
let loaded = false;
13+
export async function loadFFmpeg() {
14+
if (!ffmpeg) {
15+
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.1/dist/umd';
16+
ffmpeg = new FFmpeg();
17+
ffmpeg.on('log', ({ message }) => {
18+
console.log(message);
19+
});
20+
// toBlobURL is used to bypass CORS issue, urls with the same
21+
// domain can be used directly.
22+
await ffmpeg.load({
23+
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
24+
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm')
25+
});
26+
loaded = true;
27+
}
28+
}
29+
30+
export async function convert(
31+
inputPath: string,
32+
timingStart: string,
33+
timingEnd: string,
34+
output: string
35+
): Promise<FileData> {
36+
if (!loaded) {
37+
await loadFFmpeg();
38+
}
39+
40+
const input = 'input.' + inputPath.split('.').pop();
41+
await ffmpeg.writeFile(input, await fetchFile(inputPath));
42+
await ffmpeg.exec([
43+
'-i',
44+
input,
45+
'-map_metadata',
46+
'0',
47+
'-ss',
48+
timingStart,
49+
'-to',
50+
timingEnd,
51+
'-map',
52+
'0:a',
53+
'-acodec',
54+
'copy',
55+
'-write_xing',
56+
'0',
57+
output
58+
]);
59+
const data = await ffmpeg.readFile(output);
60+
await ffmpeg.deleteFile(input);
61+
await ffmpeg.deleteFile(output);
62+
return data;
63+
}

src/lib/data/audio.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,86 @@ export function seekToVerse(verseClicked) {
295295
//forces highlighting change
296296
updateTime();
297297
}
298+
299+
// This algorithm is based on the Android code in
300+
// AudioVideoFileManager::getExtractAudioProcessingTask
301+
export function getTimingForVerseRange(verseRange: string) {
302+
const timing = currentAudioPlayer.timing;
303+
304+
if (timing && timing.length > 0) {
305+
// Ensure the final timing has its end time set properly
306+
const lastTiming = timing[timing.length - 1];
307+
if (lastTiming.endtime - lastTiming.starttime < 0.01) {
308+
lastTiming.endtime = currentAudioPlayer.audio.duration;
309+
}
310+
}
311+
312+
let timing1 = null;
313+
let timing2 = null;
314+
315+
const vs = verseRange.split('-');
316+
if (vs.length === 1) {
317+
const verse = vs[0];
318+
timing1 = timing.find((t) => t.tag.includes(verse));
319+
timing2 = timing1;
320+
}
321+
const startVerse = verseRange.split('-')[0];
322+
const endVerse = verseRange.split('-')[1];
323+
const startTiming = timing.find((t) => t.tag.includes(startVerse));
324+
const endTiming = timing.find((t) => t.tag.includes(endVerse));
325+
return {
326+
start: startTiming,
327+
end: endTiming
328+
};
329+
}
330+
331+
function getTimingForVerse(timings: Array<any>, verse: string) {
332+
const result = [];
333+
for (let i = 0; i < timings.length; i++) {
334+
if (!isTimingForVerseRange(timings[i])) {
335+
// This timing is for a single verse or phrases in a verse
336+
// e.g. "4" - return timings 4a, 4b, 4c.
337+
if (getVerseForTiming(timings[i]) === verse) {
338+
result.push(timings[i]);
339+
}
340+
} else {
341+
}
342+
}
343+
}
344+
345+
function getVerseForTiming(timing: any) {
346+
function extractNumericalPart(inputString) {
347+
let numericalPart = '';
348+
let index = 0;
349+
350+
// Skip non-numerical characters at the beginning of the string
351+
while (index < inputString.length && isNaN(parseInt(inputString[index]))) {
352+
index++;
353+
}
354+
355+
// Extract numerical characters until a non-numerical character is encountered
356+
while (index < inputString.length && !isNaN(parseInt(inputString[index]))) {
357+
numericalPart += inputString[index];
358+
index++;
359+
}
360+
361+
// Return the numerical part or null if no numerical part is found
362+
return numericalPart.length > 0 ? numericalPart : null;
363+
}
364+
365+
return extractNumericalPart(timing.tag);
366+
}
367+
368+
function isVerseNumberInVerseRange(verse: string, verseRange: string) {
369+
const vs = verseRange.split('-');
370+
if (vs.length === 1) {
371+
return verse === vs[0];
372+
}
373+
const startVerse = vs[0];
374+
const endVerse = vs[1];
375+
return verse >= startVerse && verse <= endVerse;
376+
}
377+
378+
function isTimingForVerseRange(timing: any) {
379+
return timing.tag && timing.tag.includes('-');
380+
}

0 commit comments

Comments
 (0)