blob: 0678bcc73154667654a302050e0324c0804c8605 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use byteorder::{ByteOrder, NativeEndian};
use fidl_fuchsia_media::PcmFormat;
const PCM_SAMPLE_SIZE: usize = 2;
#[derive(Clone, Debug)]
pub struct PcmAudio {
// TODO(https://fxbug.dev/332390332): Remove or explain #[allow(dead_code)].
#[allow(dead_code)]
pub pcm_format: PcmFormat,
// TODO(https://fxbug.dev/332390332): Remove or explain #[allow(dead_code)].
#[allow(dead_code)]
pub frequency: f32,
// TODO(https://fxbug.dev/332390332): Remove or explain #[allow(dead_code)].
#[allow(dead_code)]
pub amplitude: f32,
pub buffer: Vec<u8>,
}
impl PcmAudio {
pub fn create_saw_wave(
frequency: f32,
amplitude: f32,
pcm_format: PcmFormat,
frame_count: usize,
) -> Self {
let pcm_frame_size = PCM_SAMPLE_SIZE * pcm_format.channel_map.len();
let samples_per_frame = pcm_format.channel_map.len();
let sample_count = frame_count * samples_per_frame;
let mut buffer = vec![0; frame_count * pcm_frame_size];
let amplitude = amplitude.min(1.0).max(0.0);
for i in 0..sample_count {
let frame = (i / samples_per_frame) as f32;
let value =
((frame * frequency / (pcm_format.frames_per_second as f32)) % 1.0) * amplitude;
let sample = (value * i16::max_value() as f32) as i16;
let mut sample_bytes = [0; std::mem::size_of::<i16>()];
NativeEndian::write_i16(&mut sample_bytes, sample);
let offset = i * PCM_SAMPLE_SIZE;
buffer[offset] = sample_bytes[0];
buffer[offset + 1] = sample_bytes[1];
}
Self { pcm_format, frequency, amplitude, buffer }
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl_fuchsia_media::{AudioChannelId, AudioPcmMode};
use sha2::{Digest as _, Sha256};
#[test]
fn saw_wave_matches_hash() {
/// This was obtained by writing the buffer out to file and inspecting the wave on each channel.
const GOLDEN_DIGEST: &str =
"2bf4f233a179f0cb572b72570a28c07a334e406baa7fb4fc65f641b82d0ae64a";
let pcm_audio = PcmAudio::create_saw_wave(
20.0,
0.2,
PcmFormat {
pcm_mode: AudioPcmMode::Linear,
bits_per_sample: 16,
frames_per_second: 44100,
channel_map: vec![AudioChannelId::Lf, AudioChannelId::Rf],
},
/*frame_count=*/ 50000,
);
let actual_digest = hex::encode(Sha256::digest(&pcm_audio.buffer));
assert_eq!(&actual_digest, GOLDEN_DIGEST);
}
}