Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Convert audio to MP3.
18
 *
19
 * @module     tiny_recordrtc/convert_to_mp3
20
 * @copyright  Meirza <meirza.arson@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import lamejs from './lame.all';
25
 
26
/**
27
 * Extract Pulse Code Modulation (PCM) data from an AudioBuffer to get raw channel data.
28
 *
29
 * @param {AudioBuffer} audioBuffer The AudioBuffer containing the audio data.
30
 * @returns {Array<Int16Array>} The PCM data for each channel.
31
 */
32
const extractPCM = (audioBuffer) => {
33
    const channelData = [];
34
    const numberOfChannels = audioBuffer.numberOfChannels;
35
    const audioBufferLength = audioBuffer.length;
36
 
37
    for (let channel = 0; channel < numberOfChannels; channel++) {
38
        const rawChannelData = audioBuffer.getChannelData(channel);
39
        channelData[channel] = new Int16Array(audioBufferLength);
40
        // Convert floating-point audio samples into 16-bit signed integer values.
41
        for (let i = 0; i < audioBufferLength; i++) {
42
            channelData[channel][i] = rawChannelData[i] * 32768;
43
        }
44
    }
45
 
46
    return channelData;
47
};
48
 
49
/**
50
 * Fetches and decodes the audio data from a given URL into an AudioBuffer.
51
 *
52
 * @param {string} sourceUrl - The URL of the source audio file.
53
 * @returns {Promise<AudioBuffer>} - A promise that resolves with the decoded AudioBuffer object.
54
 */
55
const getAudioBuffer = async(sourceUrl) => {
56
    const response = await fetch(sourceUrl);
57
    const arrayBuffer = await response.arrayBuffer();
58
    const audioContext = new (
59
        window.AudioContext // Default.
60
        || window.webkitAudioContext // Safari and old versions of Chrome.
61
    )();
62
    return audioContext.decodeAudioData(arrayBuffer);
63
};
64
 
65
/**
66
 * Converts an AudioBuffer to MP3 format using lamejs.
67
 *
68
 * @param {Object} lamejs - The lamejs library object.
69
 * @param {number} channels - The number of audio channels (1 for mono, 2 for stereo).
70
 * @param {number} sampleRate - The sample rate of the audio (e.g., 44100 Hz).
71
 * @param {number} bitRate - The bitrate (in kbps) to encode the MP3.
72
 * @param {Int16Array} left - The PCM data for the left channel.
73
 * @param {Int16Array} [right=null] - The PCM data for the right channel (optional for stereo).
74
 * @returns {Blob} - A Blob containing the MP3 audio data.
75
 */
76
const convertAudioBuffer = (lamejs, channels, sampleRate, bitRate, left, right = null) => {
77
    const mp3Data = [];
78
    const mp3Encoder = new lamejs.Mp3Encoder(channels, sampleRate, bitRate);
79
    // Each frame represents 1152 audio samples per channel (for both mono and stereo).
80
    const sampleBlockSize = 1152;
81
 
82
    // Ensure that the same encoding logic works for both mono and stereo audio by
83
    // either passing both channels or just the left channel to the MP3 encoder.
84
    for (let i = 0; i < left.length; i += sampleBlockSize) {
85
        const leftChunk = left.subarray(i, i + sampleBlockSize);
86
        const mp3Buf = right
87
            ? mp3Encoder.encodeBuffer(leftChunk, right.subarray(i, i + sampleBlockSize)) // Stereo.
88
            : mp3Encoder.encodeBuffer(leftChunk); // Mono.
89
 
90
        if (mp3Buf.length) {
91
            mp3Data.push(mp3Buf);
92
        }
93
    }
94
 
95
    // Preventing loss of the last few samples of audio.
96
    const mp3Buf = mp3Encoder.flush();
97
    if (mp3Buf.length) {
98
        mp3Data.push(new Int8Array(mp3Buf));
99
    }
100
 
101
    return new Blob(mp3Data, {type: 'audio/mp3'});
102
};
103
 
104
/**
105
 * Main function to handle the entire process of converting an audio file to MP3 format.
106
 *
107
 * @param {string} sourceUrl - The URL of the source audio file to be converted.
108
 * @param {number} [bitRate=128] - The bitrate (in kbps) for the MP3 conversion. Default is 128 kbps.
109
 * @returns {Promise<Blob>} - A promise that resolves with the MP3 file as a Blob.
110
 *
111
 * @throws {Error} If the Lamejs module or audio buffer fails to load.
112
 *
113
 * @example
114
 * const mp3Data = await convertMp3('audio-source.wav', 192);
115
 * window.console.log(mp3Data); // Logs the ArrayBuffer with MP3 data.
116
 */
117
export const convertMp3 = async(sourceUrl, bitRate = 128) => {
118
    const audioBuffer = await getAudioBuffer(sourceUrl);
119
    const [left, right] = extractPCM(audioBuffer);
120
    return convertAudioBuffer(lamejs, audioBuffer.numberOfChannels, audioBuffer.sampleRate, bitRate, left, right);
121
};