mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-10-31 07:46:18 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			103 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Encodes PCM float-32 in native endianness into a WAV binary.
 | |
|  */
 | |
| export function encodePcmAsWav(buffer, numChannels, samplingRate) {
 | |
|   // See http://soundfile.sapp.org/doc/WaveFormat
 | |
| 
 | |
|   const HEADER_SIZE = 44;
 | |
| 
 | |
|   const wavBuffer = new ArrayBuffer(HEADER_SIZE + buffer.byteLength);
 | |
|   const view = new DataView(wavBuffer);
 | |
| 
 | |
|   const numFrames = buffer.byteLength / 4;
 | |
|   const bytesPerSample = 4;
 | |
| 
 | |
|   const blockAlign = numChannels * bytesPerSample;
 | |
|   const byteRate = samplingRate * blockAlign;
 | |
|   const dataSize = numFrames * blockAlign;
 | |
| 
 | |
|   let offset = 0;
 | |
| 
 | |
|   function writeUint32Big(int) {
 | |
|     view.setUint32(offset, int, false);
 | |
|     offset += 4;
 | |
|   }
 | |
| 
 | |
|   function writeUint32(int) {
 | |
|     view.setUint32(offset, int, true);
 | |
|     offset += 4;
 | |
|   }
 | |
| 
 | |
|   function writeUint16(int) {
 | |
|     view.setUint16(offset, int, true);
 | |
|     offset += 2;
 | |
|   }
 | |
| 
 | |
|   function writeFloat32(int) {
 | |
|     view.setFloat32(offset, int, true);
 | |
|     offset += 4;
 | |
|   }
 | |
| 
 | |
|   writeUint32Big(0x52494646);
 | |
|   writeUint32(36 + dataSize);
 | |
|   writeUint32Big(0x57415645);
 | |
| 
 | |
|   writeUint32Big(0x666d7420);
 | |
|   writeUint32(16);
 | |
|   writeUint16(3); // 3 represents 32-bit float PCM
 | |
|   writeUint16(numChannels);
 | |
|   writeUint32(samplingRate);
 | |
|   writeUint32(byteRate);
 | |
|   writeUint16(blockAlign);
 | |
|   writeUint16(bytesPerSample * 8);
 | |
| 
 | |
|   writeUint32Big(0x64617461);
 | |
|   writeUint32(dataSize);
 | |
| 
 | |
|   const array = new Float32Array(buffer);
 | |
| 
 | |
|   for (let i = 0; i < array.length; i++) {
 | |
|     writeFloat32(array[i]);
 | |
|   }
 | |
| 
 | |
|   return wavBuffer;
 | |
| }
 | |
| 
 | |
| const HEADER_LENGTH = 4;
 | |
| 
 | |
| /**
 | |
|  * Builds a single buffer with JSON-serialized `meta` and `buffer`.
 | |
|  */
 | |
| export function encodeAnnotatedBuffer(meta, buffer) {
 | |
|   const encoder = new TextEncoder();
 | |
|   const metaArray = encoder.encode(JSON.stringify(meta));
 | |
| 
 | |
|   const raw = new ArrayBuffer(
 | |
|     HEADER_LENGTH + metaArray.byteLength + buffer.byteLength,
 | |
|   );
 | |
|   const view = new DataView(raw);
 | |
| 
 | |
|   view.setUint32(0, metaArray.byteLength);
 | |
|   new Uint8Array(raw, HEADER_LENGTH, metaArray.byteLength).set(metaArray);
 | |
|   new Uint8Array(raw, HEADER_LENGTH + metaArray.byteLength).set(
 | |
|     new Uint8Array(buffer),
 | |
|   );
 | |
| 
 | |
|   return raw;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Decodes binary annotated with JSON-serialized metadata.
 | |
|  */
 | |
| export function decodeAnnotatedBuffer(raw) {
 | |
|   const view = new DataView(raw);
 | |
|   const metaArrayLength = view.getUint32(0);
 | |
| 
 | |
|   const metaArray = new Uint8Array(raw, HEADER_LENGTH, metaArrayLength);
 | |
|   const buffer = raw.slice(HEADER_LENGTH + metaArrayLength);
 | |
| 
 | |
|   const decoder = new TextDecoder();
 | |
|   const meta = JSON.parse(decoder.decode(metaArray));
 | |
| 
 | |
|   return [meta, buffer];
 | |
| }
 |