playback: rewrite & switch to 32-bit sample data

- eliminate intermediate record buffer
- switch to u32 samples instead of byte mucking
- grow samples array to match C code (cannot go bigger as application
  cannot be loaded because ProcessManager tries to allocate all pages
  at once and MemoryManager runs out of contiguous memory)
- make left/right channel handling match C code
- when scaling recorded waveform for playback write left & right channels
  (C code writes only the right channel)
- detect the end of renode sample input data to speedup testing
- remove an unneeded log msg
- cleanup unsafe usage

Change-Id: Ic26356466cf8e53359bc6d44ac53e17ff5020274
diff --git a/apps/rust/playback/playback.rs b/apps/rust/playback/playback.rs
index 77fa8b2..c34aa81 100644
--- a/apps/rust/playback/playback.rs
+++ b/apps/rust/playback/playback.rs
@@ -10,30 +10,20 @@
 
 use core::mem::size_of;
 use libcantrip::sdk_init;
-use log::{info, trace};
+use log::info;
 use log::{set_max_level, LevelFilter};
 use sdk_interface::*;
 
+const RECORD_FREQ_HZ: usize = 1_000_000; // 1MHz
+const PLAY_FREQ_HZ: usize = 500_000; // .5MHz
+const BUFFER_SIZE: usize = 2024; // NB: 1/2 4K page - postcard overhead
+
 #[no_mangle]
 pub fn main() {
     static mut HEAP: [u8; 4096] = [0; 4096];
     sdk_init(unsafe { &mut HEAP });
     set_max_level(LevelFilter::Info);
 
-    const RECORD_FREQ_HZ: usize = 1_000_000; // 1MHz
-    const PLAY_FREQ_HZ: usize = 500_000; // .5MHz
-                                         //    const SAMPLE_COUNT: usize = 5 * RECORD_FREQ_HZ; // 5s of data XXX not enough memory
-    const SAMPLE_COUNT: usize = 16000; // XXX hack
-    const BUFFER_SIZE: usize = 2048; // NB: 1/2 4K page used for RPC's
-    static mut SAMPLES: [u32; SAMPLE_COUNT] = [0u32; SAMPLE_COUNT];
-
-    let mut samples_left: [i16; 256] = [0i16; 256];
-    let mut samples_right: [i16; 256] = [0i16; 256];
-    let mut index_left: usize = 0;
-    let mut index_right: usize = 0;
-    let mut total_left: i32 = 0;
-    let mut total_right: i32 = 0;
-
     info!("Audio playback demo.");
 
     sdk_audio_reset(
@@ -41,110 +31,137 @@
     )
     .expect("sdk_audio_reset");
 
+    const SAMPLE_COUNT: usize = 5 * 16000;
+    static mut SAMPLES: [u32; SAMPLE_COUNT] = [0u32; SAMPLE_COUNT];
+
     loop {
-        unsafe {
-            info!("Start recording...");
+        info!("Start recording...");
+        let samples_captured = record(unsafe { &mut SAMPLES });
+        info!("Done recording, collected {} samples.", samples_captured);
+        if samples_captured != SAMPLE_COUNT && samples_captured != 11580 {
+            info!("MISSING {} samples", 11580 - (samples_captured as isize));
+        }
 
-            sdk_audio_record_start(
-                /*rate=*/ RECORD_FREQ_HZ,
-                /*buffer_size=*/ BUFFER_SIZE,
-                /*stop_on_full=*/ true,
-            )
-            .expect("sdk_audio_record_start");
-
-            let mut sample: usize = 0;
-            while sample < SAMPLES.len() {
-                /*
-                            // XXX maybe combine wait & collect
-                            // Wait for record buffer to be at least 1/2 full.
-                            sdk_audio_record_wait(BUFFER_SIZE / 2).expect("sdk_audio_record_wait");
-                */
-                let mut data: [u8; 1024] = [0u8; 1024]; // XXX
-                let data_count =
-                    sdk_audio_record_collect(&mut data).expect("sdk_audio_record_collect");
-                trace!("collected {data_count} bytes of audio data");
-
-                assert!((data_count % size_of::<u32>()) == 0);
-                for ix in 0..(data_count / size_of::<u32>()) {
-                    let raw_sample: u32 = unsafe { data.as_ptr().cast::<u32>().add(ix).read() };
-                    let left = (raw_sample >> 16) as u16;
-                    let right = (raw_sample & 0xffff) as u16;
-                    total_left -= samples_left[index_left] as i32;
-                    total_right -= samples_right[index_right] as i32;
-                    total_left += left as i32;
-                    samples_left[index_left] = left as i16;
-                    total_right += right as i32;
-                    samples_right[index_right] = right as i16;
-                    index_left = (index_left + 1) % samples_left.len();
-                    index_right = (index_right + 1) % samples_right.len();
-                    let mean_left: u16 = (total_left as usize / samples_left.len()) as u16;
-                    let mean_right: u16 = (total_right as usize / samples_right.len()) as u16;
-
-                    SAMPLES[sample] =
-                        (((left - mean_left) as u32) << 16) | ((right - mean_right) as u32);
-                    sample += 1;
-                    if sample == SAMPLES.len() {
-                        break;
-                    }
-                }
-            }
-
-            // Disable recording and discard any buffered data.
-            // XXX consume collected data if space available?
-            sdk_audio_record_stop().expect("sdk_audio_record_stop");
-            let samples_captured: usize = sample;
-
-            info!("Done recording, collected {} samples.", samples_captured);
-
-            // Calculate min/max after correcting DC offsets.
-            let mut max: i32 = i16::MIN as i32;
-            let mut min: i32 = i16::MAX as i32;
-            for i in 0..samples_captured {
-                let s = (SAMPLES[i] >> 16) as i32; // XXX >>16 or &0xffff?
-                if s < min {
-                    min = s;
-                }
-                if s > max {
-                    max = s;
-                }
-            }
-            if min == 0 && max == 0 {
-                info!("Looks like silence, not playing samples...");
-                continue;
-            }
-
-            // Calculate a scaling factor and apply this to scale the waveform
-            // to a peak of 75% amplitude.
-            let scale_max: i32 = (max * 100) / (i16::MAX as i32);
-            let scale_min: i32 = (min * 100).abs() / (i16::MIN as i32);
-            let scale: i32 = core::cmp::max(core::cmp::max(scale_max, scale_min), 1);
-            for i in 0..samples_captured {
-                let s = (SAMPLES[i] >> 16) as i16; // XXX >>16 or &0xffff?
-                let scaled_sample: i16 = ((100 * (s as i32)) / scale) as i16;
-                SAMPLES[i] = ((75 * (scaled_sample as i32)) / 100) as u32;
-            }
-
-            info!("Playing recorded samples..");
-
-            sdk_audio_play_start(/*rate=*/ PLAY_FREQ_HZ, /*buffer_size=*/ BUFFER_SIZE)
-                .expect("sdk_audio_play_start");
-
-            const MAX_SAMPLES_PER_WRITE: usize = BUFFER_SIZE / size_of::<u32>();
-            let mut samples_remaining = samples_captured;
-            let mut next_sample = 0;
-            while samples_remaining > 0 {
-                let count = core::cmp::min(samples_remaining, MAX_SAMPLES_PER_WRITE);
-                sdk_audio_play_write(core::mem::transmute(
-                    &SAMPLES[next_sample..next_sample + count],
-                ))
-                .expect("sdk_audio_play_write");
-                next_sample += count;
-                samples_remaining -= count;
-            }
-
-            sdk_audio_play_stop().expect("sdk_audio_play_stop");
-
+        correct_dc_offsets(unsafe { &mut SAMPLES[..samples_captured] });
+        if scale_waveform(unsafe { &mut SAMPLES[..samples_captured] }) {
+            info!("Start playing {samples_captured} samples..");
+            play(unsafe { &SAMPLES[..samples_captured] });
             info!("Done playing.");
         }
     }
 }
+
+fn record(samples: &mut [u32]) -> usize {
+    fn first_zero(samples: &[u32]) -> Option<usize> {
+        for i in 0..samples.len() {
+            if samples[i] == 0 {
+                return Some(i);
+            }
+        }
+        None
+    }
+
+    sdk_audio_record_start(
+        /*rate=*/ RECORD_FREQ_HZ,
+        /*buffer_size=*/ BUFFER_SIZE,
+        /*stop_on_full=*/ true,
+    )
+    .expect("sdk_audio_record_start");
+
+    const MAX_SAMPLES_PER_READ: usize = BUFFER_SIZE / size_of::<u32>();
+    let mut samples_captured: usize = 0;
+    while samples_captured < samples.len() {
+        let count = core::cmp::min(samples.len() - samples_captured, MAX_SAMPLES_PER_READ);
+        let data_count =
+            sdk_audio_record_collect(&mut samples[samples_captured..samples_captured + count])
+                .expect("sdk_audio_record_collect");
+        // XXX need a proper check for "no signal" and over more data
+        if let Some(index) = first_zero(&samples[samples_captured..samples_captured + data_count]) {
+            samples_captured += index;
+            break;
+        }
+        samples_captured += data_count;
+    }
+
+    sdk_audio_record_stop().expect("sdk_audio_record_stop");
+
+    samples_captured
+}
+
+fn play(samples: &[u32]) {
+    sdk_audio_play_start(/*rate=*/ PLAY_FREQ_HZ, /*buffer_size=*/ BUFFER_SIZE)
+        .expect("sdk_audio_play_start");
+
+    const MAX_SAMPLES_PER_WRITE: usize = BUFFER_SIZE / size_of::<u32>();
+    let mut samples_remaining = samples.len();
+    let mut next_sample = 0;
+    while samples_remaining > 0 {
+        let count = core::cmp::min(samples_remaining, MAX_SAMPLES_PER_WRITE);
+        sdk_audio_play_write(&samples[next_sample..next_sample + count])
+            .expect("sdk_audio_play_write");
+        next_sample += count;
+        samples_remaining -= count;
+    }
+
+    sdk_audio_play_stop().expect("sdk_audio_play_stop");
+}
+
+fn correct_dc_offsets(samples: &mut [u32]) {
+    let mut samples_left: [i16; 256] = [0i16; 256];
+    let mut samples_right: [i16; 256] = [0i16; 256];
+    let mut index_left: usize = 0;
+    let mut index_right: usize = 0;
+    let mut total_left: i32 = 0;
+    let mut total_right: i32 = 0;
+
+    for ix in 0..samples.len() {
+        let raw_sample = samples[ix];
+        let left = (raw_sample >> 16) as u16;
+        let right = (raw_sample & 0xffff) as u16;
+        total_left -= samples_left[index_left] as i32;
+        total_right -= samples_right[index_right] as i32;
+        total_left += left as i32;
+        samples_left[index_left] = left as i16;
+        total_right += right as i32;
+        samples_right[index_right] = right as i16;
+        index_left = (index_left + 1) % samples_left.len();
+        index_right = (index_right + 1) % samples_right.len();
+        let mean_left: u16 = (total_left as usize / samples_left.len()) as u16;
+        let mean_right: u16 = (total_right as usize / samples_right.len()) as u16;
+
+        samples[ix] = (((left - mean_left) as u32) << 16) | ((right - mean_right) as u32);
+    }
+}
+
+fn scale_waveform(samples: &mut [u32]) -> bool {
+    // Calculate min/max after correcting DC offsets.
+    let mut max: i32 = i16::MIN as i32;
+    let mut min: i32 = i16::MAX as i32;
+    for i in 0..samples.len() {
+        let s = (samples[i] & 0xffff) as i32;
+        if s < min {
+            min = s;
+        }
+        if s > max {
+            max = s;
+        }
+    }
+    if min == 0 && max == 0 {
+        info!("Looks like silence, not playing samples...");
+        return false;
+    }
+
+    // Calculate a scaling factor and apply this to scale the waveform
+    // to a peak of 75% amplitude.
+    let scale_max: i32 = (max * 100) / (i16::MAX as i32);
+    let scale_min: i32 = (min * 100).abs() / (i16::MIN as i32);
+    let scale: i32 = core::cmp::max(core::cmp::max(scale_max, scale_min), 1);
+    for i in 0..samples.len() {
+        let s = (samples[i] & 0xffff) as i16;
+        let mut scaled_sample: i16 = ((100 * (s as i32)) / scale) as i16;
+        scaled_sample = (((scaled_sample as i32) * 75) / 100) as i16;
+        // Write scaled sample to both left+right channels.
+        samples[i] = ((scaled_sample as u32) << 16) | (scaled_sample as u32);
+    }
+    true
+}