mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	 ee02923ace
			
		
	
	ee02923ace
	
	
	
		
			
			- Switch to pnpm based build - Switch circleci from docker to machine to have more memory - Fix frontend and backend tests Fixes https://linear.app/chatwoot/issue/CW-3610/fix-circle-ci-for-vite-build --------- Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
		
			
				
	
	
		
			147 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import lamejs from '@breezystack/lamejs';
 | |
| 
 | |
| const writeString = (view, offset, string) => {
 | |
|   // eslint-disable-next-line no-plusplus
 | |
|   for (let i = 0; i < string.length; i++) {
 | |
|     view.setUint8(offset + i, string.charCodeAt(i));
 | |
|   }
 | |
| };
 | |
| 
 | |
| const bufferToWav = async (buffer, numChannels, sampleRate) => {
 | |
|   const length = buffer.length * numChannels * 2;
 | |
|   const wav = new ArrayBuffer(44 + length);
 | |
|   const view = new DataView(wav);
 | |
| 
 | |
|   // WAV Header
 | |
|   writeString(view, 0, 'RIFF');
 | |
|   view.setUint32(4, 36 + length, true);
 | |
|   writeString(view, 8, 'WAVE');
 | |
|   writeString(view, 12, 'fmt ');
 | |
|   view.setUint32(16, 16, true);
 | |
|   view.setUint16(20, 1, true);
 | |
|   view.setUint16(22, numChannels, true);
 | |
|   view.setUint32(24, sampleRate, true);
 | |
|   view.setUint32(28, sampleRate * numChannels * 2, true);
 | |
|   view.setUint16(32, numChannels * 2, true);
 | |
|   view.setUint16(34, 16, true);
 | |
|   writeString(view, 36, 'data');
 | |
|   view.setUint32(40, length, true);
 | |
| 
 | |
|   // WAV Data
 | |
|   const offset = 44;
 | |
|   // eslint-disable-next-line no-plusplus
 | |
|   for (let i = 0; i < buffer.length; i++) {
 | |
|     // eslint-disable-next-line no-plusplus
 | |
|     for (let channel = 0; channel < numChannels; channel++) {
 | |
|       const sample = Math.max(
 | |
|         -1,
 | |
|         Math.min(1, buffer.getChannelData(channel)[i])
 | |
|       );
 | |
|       view.setInt16(
 | |
|         offset + (i * numChannels + channel) * 2,
 | |
|         sample * 0x7fff,
 | |
|         true
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return new Blob([wav], { type: 'audio/wav' });
 | |
| };
 | |
| 
 | |
| const decodeAudioData = async audioBlob => {
 | |
|   const audioContext = new (window.AudioContext || window.webkitAudioContext)();
 | |
|   const arrayBuffer = await audioBlob.arrayBuffer();
 | |
|   const audioData = await audioContext.decodeAudioData(arrayBuffer);
 | |
|   return audioData;
 | |
| };
 | |
| 
 | |
| export const convertToWav = async audioBlob => {
 | |
|   const audioBuffer = await decodeAudioData(audioBlob);
 | |
|   return bufferToWav(
 | |
|     audioBuffer,
 | |
|     audioBuffer.numberOfChannels,
 | |
|     audioBuffer.sampleRate
 | |
|   );
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Encodes audio samples to MP3 format.
 | |
|  * @param {number} channels - Number of audio channels.
 | |
|  * @param {number} sampleRate - Sample rate in Hz.
 | |
|  * @param {Int16Array} samples - Audio samples to be encoded.
 | |
|  * @param {number} bitrate - MP3 bitrate (default: 128)
 | |
|  * @returns {Blob} - The MP3 encoded audio as a Blob.
 | |
|  */
 | |
| export const encodeToMP3 = (channels, sampleRate, samples, bitrate = 128) => {
 | |
|   const outputBuffer = [];
 | |
|   const encoder = new lamejs.Mp3Encoder(channels, sampleRate, bitrate);
 | |
|   const maxSamplesPerFrame = 1152;
 | |
| 
 | |
|   for (let offset = 0; offset < samples.length; offset += maxSamplesPerFrame) {
 | |
|     const sliceEnd = Math.min(offset + maxSamplesPerFrame, samples.length);
 | |
|     const sampleSlice = samples.subarray(offset, sliceEnd);
 | |
|     const mp3Buffer = encoder.encodeBuffer(sampleSlice);
 | |
| 
 | |
|     if (mp3Buffer.length > 0) {
 | |
|       outputBuffer.push(new Int8Array(mp3Buffer));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const remainingData = encoder.flush();
 | |
|   if (remainingData.length > 0) {
 | |
|     outputBuffer.push(new Int8Array(remainingData));
 | |
|   }
 | |
| 
 | |
|   return new Blob(outputBuffer, { type: 'audio/mp3' });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Converts an audio Blob to an MP3 format Blob.
 | |
|  * @param {Blob} audioBlob - The audio data as a Blob.
 | |
|  * @param {number} bitrate - MP3 bitrate (default: 128)
 | |
|  * @returns {Promise<Blob>} - A Blob containing the MP3 encoded audio.
 | |
|  */
 | |
| export const convertToMp3 = async (audioBlob, bitrate = 128) => {
 | |
|   try {
 | |
|     const audioBuffer = await decodeAudioData(audioBlob);
 | |
|     const samples = new Int16Array(
 | |
|       audioBuffer.length * audioBuffer.numberOfChannels
 | |
|     );
 | |
|     let offset = 0;
 | |
|     for (let i = 0; i < audioBuffer.length; i += 1) {
 | |
|       for (
 | |
|         let channel = 0;
 | |
|         channel < audioBuffer.numberOfChannels;
 | |
|         channel += 1
 | |
|       ) {
 | |
|         const sample = Math.max(
 | |
|           -1,
 | |
|           Math.min(1, audioBuffer.getChannelData(channel)[i])
 | |
|         );
 | |
|         samples[offset] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
 | |
|         offset += 1;
 | |
|       }
 | |
|     }
 | |
|     return encodeToMP3(
 | |
|       audioBuffer.numberOfChannels,
 | |
|       audioBuffer.sampleRate,
 | |
|       samples,
 | |
|       bitrate
 | |
|     );
 | |
|   } catch (error) {
 | |
|     throw new Error('Conversion to MP3 failed.');
 | |
|   }
 | |
| };
 | |
| 
 | |
| export const convertAudio = async (inputBlob, outputFormat, bitrate = 128) => {
 | |
|   let audio;
 | |
|   if (outputFormat === 'audio/wav') {
 | |
|     audio = await convertToWav(inputBlob);
 | |
|   } else if (outputFormat === 'audio/mp3') {
 | |
|     audio = await convertToMp3(inputBlob, bitrate);
 | |
|   } else {
 | |
|     throw new Error('Unsupported output format');
 | |
|   }
 | |
|   return audio;
 | |
| };
 |