Skip to content

Commit ca2b7f3

Browse files
Update resampler.html
1 parent bd770f5 commit ca2b7f3

File tree

1 file changed

+74
-42
lines changed

1 file changed

+74
-42
lines changed

MP3Tools/resampler.html

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,91 @@
44
<head>
55
<meta charset="UTF-8">
66
<title>MP3 Resampler</title>
7-
<!-- ✅ Use the official ffmpeg.wasm build that provides a global FFmpeg object -->
8-
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.11.6/dist/ffmpeg.min.js"></script>
7+
<script src="https://cdn.jsdelivr.net/npm/lamejs@1.2.0/lame.min.js"></script>
98
</head>
109
<body>
1110
<h1>MP3 Resampler</h1>
12-
<input type="file" id="audioFile" accept=".mp3"><br><br>
11+
<input type="file" id="audioFile" accept="audio/mpeg"><br><br>
1312
<label>Target Sample Rate (Hz): </label>
1413
<input type="number" id="sampleRate" value="44100"><br><br>
1514
<button id="resampleBtn">Resample</button>
1615
<p id="status"></p>
1716

1817
<script>
19-
async function init() {
20-
const { createFFmpeg, fetchFile } = FFmpeg;
21-
const ffmpeg = createFFmpeg({ log: true });
22-
23-
document.getElementById('resampleBtn').addEventListener('click', async () => {
24-
const fileInput = document.getElementById('audioFile');
25-
const rateInput = document.getElementById('sampleRate');
26-
const status = document.getElementById('status');
27-
28-
if (!fileInput.files.length) {
29-
alert('Please select an MP3 file.');
30-
return;
31-
}
32-
33-
const sampleRate = parseInt(rateInput.value);
34-
if (!sampleRate || sampleRate <= 0) {
35-
alert('Please enter a valid sample rate.');
36-
return;
37-
}
38-
39-
const file = fileInput.files[0];
40-
status.textContent = 'Loading FFmpeg (this may take a moment)...';
41-
if (!ffmpeg.isLoaded()) await ffmpeg.load();
42-
43-
status.textContent = 'Resampling...';
44-
ffmpeg.FS('writeFile', 'input.mp3', await fetchFile(file));
45-
await ffmpeg.run('-i', 'input.mp3', '-ar', sampleRate.toString(), 'output.mp3');
46-
47-
const data = ffmpeg.FS('readFile', 'output.mp3');
48-
const url = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/mp3' }));
49-
50-
const link = document.createElement('a');
51-
link.href = url;
52-
link.download = `resampled_${sampleRate}Hz.mp3`;
53-
link.click();
54-
55-
status.textContent = 'Done!';
56-
});
18+
document.getElementById('resampleBtn').addEventListener('click', async () => {
19+
const fileInput = document.getElementById('audioFile');
20+
const sampleRate = parseInt(document.getElementById('sampleRate').value);
21+
const status = document.getElementById('status');
22+
23+
if (!fileInput.files.length) {
24+
alert('Please select an MP3 file.');
25+
return;
26+
}
27+
if (!sampleRate || sampleRate <= 0) {
28+
alert('Please enter a valid sample rate.');
29+
return;
30+
}
31+
32+
const file = fileInput.files[0];
33+
const arrayBuffer = await file.arrayBuffer();
34+
35+
status.textContent = "Decoding audio...";
36+
const tempCtx = new AudioContext();
37+
const decodedData = await tempCtx.decodeAudioData(arrayBuffer);
38+
tempCtx.close();
39+
40+
status.textContent = "Resampling...";
41+
const resampleCtx = new OfflineAudioContext(
42+
decodedData.numberOfChannels,
43+
decodedData.duration * sampleRate,
44+
sampleRate
45+
);
46+
47+
const source = resampleCtx.createBufferSource();
48+
source.buffer = decodedData;
49+
source.connect(resampleCtx.destination);
50+
source.start(0);
51+
52+
const renderedBuffer = await resampleCtx.startRendering();
53+
54+
status.textContent = "Encoding MP3...";
55+
const mp3Blob = encodeToMp3(renderedBuffer);
56+
const url = URL.createObjectURL(mp3Blob);
57+
const link = document.createElement("a");
58+
link.href = url;
59+
link.download = `resampled_${sampleRate}Hz.mp3`;
60+
link.click();
61+
62+
status.textContent = "Done!";
63+
});
64+
65+
function encodeToMp3(audioBuffer) {
66+
const samples = audioBuffer.getChannelData(0);
67+
const sampleRate = audioBuffer.sampleRate;
68+
const mp3Encoder = new lamejs.Mp3Encoder(1, sampleRate, 128);
69+
const blockSize = 1152;
70+
const mp3Data = [];
71+
72+
for (let i = 0; i < samples.length; i += blockSize) {
73+
const sampleChunk = samples.subarray(i, i + blockSize);
74+
const mp3buf = mp3Encoder.encodeBuffer(floatTo16BitPCM(sampleChunk));
75+
if (mp3buf.length > 0) mp3Data.push(mp3buf);
76+
}
77+
78+
const end = mp3Encoder.flush();
79+
if (end.length > 0) mp3Data.push(end);
80+
81+
return new Blob(mp3Data, { type: 'audio/mpeg' });
5782
}
5883

59-
init();
84+
function floatTo16BitPCM(float32Array) {
85+
const buffer = new Int16Array(float32Array.length);
86+
for (let i = 0; i < float32Array.length; i++) {
87+
const s = Math.max(-1, Math.min(1, float32Array[i]));
88+
buffer[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
89+
}
90+
return buffer;
91+
}
6092
</script>
6193

6294
</body>

0 commit comments

Comments
 (0)