diff --git a/public/rust_mic_worklet.js b/public/rust_mic_worklet.js index df12eb4..1b40f1f 100644 --- a/public/rust_mic_worklet.js +++ b/public/rust_mic_worklet.js @@ -1,15 +1,12 @@ const SAMPLE_RATE = 48000; -const PACKET_MS = 10; -const PACKET_FRAMES = PACKET_MS / 1000 * SAMPLE_RATE; -//const PACKET_FRAMES = 2400; -console.log("Frames per packet:", PACKET_FRAMES); +const PACKET_SAMPLES = 480; class RustWorklet extends AudioWorkletProcessor { constructor(options) { super(); this.module = options.processorOptions; this.timestamp = null; - this.buffer = new Float32Array(PACKET_FRAMES); + this.buffer = new Float32Array(PACKET_SAMPLES * 2); this.buffer_offset = 0; if (sampleRate != SAMPLE_RATE) { throw Error(`sample rate ${sampleRate} should be ${SAMPLE_RATE}`); @@ -23,15 +20,16 @@ class RustWorklet extends AudioWorkletProcessor { const data = { format: 'f32', sampleRate: SAMPLE_RATE, - //numberOfFrames: this.buffer_offset, numberOfFrames: this.buffer_offset, numberOfChannels: 1, timestamp: this.timestamp, - //timestamp: null, - data: this.buffer.slice(0, this.buffer_offset), + data: this.buffer.slice(0, PACKET_SAMPLES), }; this.port.postMessage(data); - this.buffer_offset = 0; + if (this.buffer_offset > PACKET_SAMPLES) { + this.buffer.copyWithin(0, PACKET_SAMPLES, this.buffer_offset); + } + this.buffer_offset -= PACKET_SAMPLES; this.timestamp = null; } @@ -52,18 +50,12 @@ class RustWorklet extends AudioWorkletProcessor { const frames = input[0]; - if (this.buffer_offset + frames.length > this.buffer.length) { - // too full, send now - this.do_send(); - } - this.buffer.set(frames, this.buffer_offset); this.buffer_offset += frames.length; - - if (this.buffer_offset + 128 > this.buffer.length) { - // full enough, send now + if (this.buffer_offset >= PACKET_SAMPLES) { this.do_send(); } + return true; } }; diff --git a/src/lib.rs b/src/lib.rs index b2b7644..acb095a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,89 +46,75 @@ use web_sys::WorkletOptions; mod ass { + use byteorder::{ByteOrder, LittleEndian}; + use ogg::PacketWriter; -use byteorder::{ByteOrder, LittleEndian}; -use ogg::PacketWriter; + const VER: &str = "ballz"; -const VER: &str = "ballz"; - -const fn to_samples(ms: u32) -> usize { - ((S_PS * ms) / 1000) as usize -} - -const fn calc_sr_u64(val: u64, from: u32, to: u32) -> u64 { - (val * to as u64) / from as u64 -} - -pub fn encode( - pre_encoded_frames: Vec>, - frame_size: usize, - skip: u16, -) -> Vec{ - let mut buffer: Vec = Vec::new(); - let mut packet_writer = PacketWriter::new(&mut buffer); - - // Hardcoded serial number - let serial = 12345; - - let skip_48 = calc_sr_u64(skip.into(), 48000, 48000); - - let opus_head: [u8; 19] = [ - b'O', b'p', b'u', b's', b'H', b'e', b'a', b'd', 1, 1, // NUM_CHANNELS = 1 - 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]; - - let mut head = opus_head; - LittleEndian::write_u16(&mut head[10..12], skip_48 as u16); - LittleEndian::write_u32(&mut head[12..16], 48000); - - let mut opus_tags: Vec = Vec::with_capacity(60); - let vendor_str = format!("ogg-opus {}", VER); - opus_tags.extend(b"OpusTags"); - let mut len_bf = [0u8; 4]; - LittleEndian::write_u32(&mut len_bf, vendor_str.len() as u32); - opus_tags.extend(&len_bf); - opus_tags.extend(vendor_str.bytes()); - opus_tags.extend(&[0u8; 4]); - - packet_writer.write_packet(&head, serial, ogg::PacketWriteEndInfo::EndPage, 0); - packet_writer.write_packet( - &opus_tags, - serial, - ogg::PacketWriteEndInfo::EndPage, - 0, - ); - - for (i, frame) in pre_encoded_frames.iter().enumerate() { - let is_last = i == pre_encoded_frames.len() - 1; - //let granule_pos = 0; - let granule_pos = calc_sr_u64( - (skip as usize + (i + 1) * frame_size) as u64, - 48000, - 48000, - ); - - packet_writer.write_packet( - frame.clone(), - serial, - if is_last { - ogg::PacketWriteEndInfo::EndStream - } else { - ogg::PacketWriteEndInfo::NormalPacket - }, - granule_pos, - ); + const fn to_samples(ms: u32) -> usize { + ((S_PS * ms) / 1000) as usize } - buffer -} + const fn calc_sr_u64(val: u64, from: u32, to: u32) -> u64 { + (val * to as u64) / from as u64 + } + pub fn encode(pre_encoded_frames: Vec>, frame_size: usize, skip: u16) -> Vec { + let mut buffer: Vec = Vec::new(); + let mut packet_writer = PacketWriter::new(&mut buffer); + + // Hardcoded serial number + let serial = 12345; + + let skip_48 = calc_sr_u64(skip.into(), 48000, 48000); + + let opus_head: [u8; 19] = [ + b'O', b'p', b'u', b's', b'H', b'e', b'a', b'd', 1, 1, // NUM_CHANNELS = 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]; + + let mut head = opus_head; + LittleEndian::write_u16(&mut head[10..12], skip_48 as u16); + LittleEndian::write_u32(&mut head[12..16], 48000); + + let mut opus_tags: Vec = Vec::with_capacity(60); + let vendor_str = format!("ogg-opus {}", VER); + opus_tags.extend(b"OpusTags"); + let mut len_bf = [0u8; 4]; + LittleEndian::write_u32(&mut len_bf, vendor_str.len() as u32); + opus_tags.extend(&len_bf); + opus_tags.extend(vendor_str.bytes()); + opus_tags.extend(&[0u8; 4]); + + packet_writer.write_packet(&head, serial, ogg::PacketWriteEndInfo::EndPage, 0); + packet_writer.write_packet(&opus_tags, serial, ogg::PacketWriteEndInfo::EndPage, 0); + + for (i, frame) in pre_encoded_frames.iter().enumerate() { + let is_last = i == pre_encoded_frames.len() - 1; + //let granule_pos = 0; + let granule_pos = + calc_sr_u64((skip as usize + (i + 1) * frame_size) as u64, 48000, 48000); + + packet_writer.write_packet( + frame.clone(), + serial, + if is_last { + ogg::PacketWriteEndInfo::EndStream + } else { + ogg::PacketWriteEndInfo::NormalPacket + }, + granule_pos, + ); + } + + buffer + } } // Function to download data as a file pub fn download_data(data: Vec, filename: &str) -> Result<(), JsValue> { use wasm_bindgen::prelude::*; - use web_sys::{Blob, Url, HtmlAnchorElement, window}; + use web_sys::{window, Blob, HtmlAnchorElement, Url}; // Create a new Blob from the data let array = web_sys::js_sys::Uint8Array::from(&data[..]); let blob = Blob::new_with_u8_array_sequence(&vec![array].into())?; @@ -138,7 +124,9 @@ pub fn download_data(data: Vec, filename: &str) -> Result<(), JsValue> { // Create an anchor element and set its href to the Blob URL let document = window().unwrap().document().unwrap(); - let a = document.create_element("a")?.dyn_into::()?; + let a = document + .create_element("a")? + .dyn_into::()?; a.set_href(&url); a.set_download(filename); @@ -153,7 +141,6 @@ pub fn download_data(data: Vec, filename: &str) -> Result<(), JsValue> { Ok(()) } - // Borrowed from // https://github.com/security-union/videocall-rs/blob/main/videocall-client/src/decode/config.rs#L6 fn configure_audio_context() -> AudioContext { @@ -207,9 +194,7 @@ async fn create_encoder_worklet( let error: Closure = Closure::new(|e| console::error_1(&e)); - - let mut download_buffer = std::cell::RefCell::new(Vec::new()); - + let mut download_buffer = std::cell::RefCell::new(Vec::new()); // This knows what MediaStreamTrackGenerator to use as it closes around it let mut sequence_num = 0; @@ -221,11 +206,13 @@ async fn create_encoder_worklet( download_buffer.borrow_mut().push(array.clone()); if download_buffer.borrow().len() > 200 { //download_data(download_buffer.borrow().to_vec(), "download_buffer.opus"); - download_data(ass::encode(download_buffer.borrow().to_vec(), 960, 0), "download_buffer.opus"); + download_data( + ass::encode(download_buffer.borrow().to_vec(), 960, 0), + "download_buffer.opus", + ); download_buffer.borrow_mut().clear(); } - let _ = packets.try_send(ControlPacket::UDPTunnel(Box::new(VoicePacket::Audio { _dst: std::marker::PhantomData, target: 0, @@ -234,7 +221,7 @@ async fn create_encoder_worklet( payload: VoicePacketPayload::Opus(array.into(), false), position_info: None, }))); - sequence_num = sequence_num.wrapping_add(2); + sequence_num = sequence_num.wrapping_add(1); }); let audio_encoder = AudioEncoder::new(&AudioEncoderInit::new( @@ -254,14 +241,14 @@ async fn create_encoder_worklet( audio_encoder.configure(&encoder_config); console::log_1(&"Created Audio Encoder".into()); - let mut download_buffer = std::cell::RefCell::new(Vec::new()); + let mut download_buffer = std::cell::RefCell::new(Vec::new()); let onmessage: Closure = Closure::new(move |event: MessageEvent| { match AudioData::new(event.data().unchecked_ref()) { Ok(data) => { let x = web_sys::AudioDataCopyToOptions::new(0); x.set_format(web_sys::AudioSampleFormat::F32); - let mut sub_buffer = vec![0;data.allocation_size(&x).unwrap() as usize]; + let mut sub_buffer = vec![0; data.allocation_size(&x).unwrap() as usize]; data.copy_to_with_u8_array(&mut sub_buffer, &x); download_buffer.borrow_mut().append(&mut sub_buffer); if download_buffer.borrow().len() > 48000 * 10 * 4 {