blob: b100cf049d2a5b2645e1fdb1cce2433670d35026 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![allow(non_upper_case_globals)]
include!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/r8brain_sys/bindings.rs"
));
macro_rules! check_hresult {
($hr: expr, $error: expr, $msg: expr) => {
if winapi::shared::winerror::FAILED($hr) {
base::warn!("{}", $msg);
Err($error)
} else {
Ok($hr)
}
};
}
pub mod intermediate_resampler_buffer;
mod win_audio_impl;
use std::error;
use std::sync::Arc;
use audio_streams::AsyncPlaybackBufferStream;
use audio_streams::NoopStream;
use audio_streams::NoopStreamSource;
use audio_streams::NoopStreamSourceGenerator;
use audio_streams::PlaybackBufferStream;
use audio_streams::SampleFormat;
use audio_streams::StreamSource;
use base::info;
use base::warn;
use sync::Mutex;
use win_audio_impl::async_stream::WinAudioStreamSourceGenerator;
pub use win_audio_impl::*;
pub type BoxError = Box<dyn error::Error + Send + Sync>;
pub trait WinStreamSourceGenerator: Send + Sync {
fn generate(&self) -> Result<Box<dyn WinAudioServer>, BoxError>;
}
impl WinStreamSourceGenerator for WinAudioStreamSourceGenerator {
fn generate(&self) -> std::result::Result<Box<dyn WinAudioServer>, BoxError> {
Ok(Box::new(WinAudio::new()?))
}
}
impl WinStreamSourceGenerator for NoopStreamSourceGenerator {
fn generate(&self) -> Result<Box<dyn WinAudioServer>, BoxError> {
Ok(Box::new(NoopStreamSource))
}
}
/// Contains information about the audio engine's properties, such as its audio sample format
/// and its period in frames.
///
/// This does exclude whether the bit depth is in the form of floats or ints. The bit depth form
/// isn't used for sample rate conversion so it's excluded.
#[derive(Clone, Copy)]
pub struct AudioSharedFormat {
pub bit_depth: usize,
pub frame_rate: usize,
pub shared_audio_engine_period_in_frames: usize,
pub channels: usize,
// Only available for WAVEFORMATEXTENSIBLE
pub channel_mask: Option<u32>,
}
/// Implementation of StreamSource which will create the playback stream for the Windows
/// audio engine.
///
/// Extending the StreamSource trait will allow us to make necessary changes without modifying
/// the third party audiostream library.
pub trait WinAudioServer: StreamSource {
fn new_playback_stream_and_get_shared_format(
&mut self,
num_channels: usize,
format: SampleFormat,
frame_rate: usize,
buffer_size: usize,
) -> Result<(Arc<Mutex<Box<dyn PlaybackBufferStream>>>, AudioSharedFormat), BoxError>;
fn new_async_playback_stream_and_get_shared_format(
&mut self,
_num_channels: usize,
_format: SampleFormat,
_frame_rate: usize,
_buffer_size: usize,
_ex: &dyn audio_streams::AudioStreamsExecutor,
) -> Result<(Box<dyn AsyncPlaybackBufferStream>, AudioSharedFormat), BoxError> {
unimplemented!()
}
/// Evict the playback stream cache so that the audio device can be released, thus allowing
/// for machines to go to sleep.
fn evict_playback_stream_cache(&mut self) {
unimplemented!()
}
/// Returns true if audio server is a noop stream. This determine if evicting a cache is worth
/// doing
fn is_noop_stream(&self) -> bool;
}
impl WinAudioServer for WinAudio {
fn new_playback_stream_and_get_shared_format(
&mut self,
num_channels: usize,
_format: SampleFormat,
frame_rate: usize,
buffer_size: usize,
) -> Result<(Arc<Mutex<Box<dyn PlaybackBufferStream>>>, AudioSharedFormat), BoxError> {
let hr = WinAudio::co_init_once_per_thread();
let _ = check_hresult!(hr, RenderError::from(hr), "Co Initialized failed");
// Return the existing stream if we have one.
// This is mainly to reduce audio skips caused by a buffer underrun on the guest. An
// underrun causes the guest to stop the audio stream, but then start it back up when the
// guest buffer is filled again.
if let Some((playback_buffer_stream, audio_format)) =
self.cached_playback_buffer_stream.as_ref()
{
info!("Reusing playback_buffer_stream.");
return Ok((playback_buffer_stream.clone(), *audio_format));
}
let (playback_buffer_stream, audio_shared_format): (
Arc<Mutex<Box<dyn PlaybackBufferStream>>>,
AudioSharedFormat,
) = match win_audio_impl::WinAudioRenderer::new(
num_channels,
frame_rate as u32,
buffer_size,
) {
Ok(renderer) => {
let audio_shared_format = renderer.device.audio_shared_format;
let renderer_arc = Arc::new(Mutex::new(
Box::new(renderer) as Box<dyn PlaybackBufferStream>
));
self.cached_playback_buffer_stream =
Some((renderer_arc.clone(), audio_shared_format));
(renderer_arc, audio_shared_format)
}
Err(e) => {
warn!(
"Failed to create WinAudioRenderer. Fallback to NoopStream with error: {}",
e
);
(
Arc::new(Mutex::new(Box::new(NoopStream::new(
num_channels,
SampleFormat::S16LE,
frame_rate as u32,
buffer_size,
)))),
AudioSharedFormat {
bit_depth: 16,
frame_rate,
channels: 2,
shared_audio_engine_period_in_frames: frame_rate / 100,
channel_mask: None,
},
)
}
};
Ok((playback_buffer_stream, audio_shared_format))
}
fn new_async_playback_stream_and_get_shared_format(
&mut self,
num_channels: usize,
_format: SampleFormat,
frame_rate: usize,
buffer_size: usize,
ex: &dyn audio_streams::AudioStreamsExecutor,
) -> Result<(Box<dyn AsyncPlaybackBufferStream>, AudioSharedFormat), BoxError> {
let hr = WinAudio::co_init_once_per_thread();
let _ = check_hresult!(hr, RenderError::from(hr), "Co Initialized failed");
let (async_playback_buffer_stream, audio_shared_format): (
Box<dyn AsyncPlaybackBufferStream>,
AudioSharedFormat,
) = match win_audio_impl::WinAudioRenderer::new_async(
num_channels,
frame_rate as u32,
buffer_size,
ex,
) {
Ok(renderer) => {
let audio_shared_format = renderer.device.audio_shared_format;
let renderer_box = Box::new(renderer) as Box<dyn AsyncPlaybackBufferStream>;
(renderer_box, audio_shared_format)
}
Err(e) => {
warn!(
"Failed to create WinAudioRenderer. Fallback to NoopStream with error: {}",
e
);
(
Box::new(NoopStream::new(
num_channels,
SampleFormat::S16LE,
frame_rate as u32,
buffer_size,
)),
AudioSharedFormat {
bit_depth: 16,
frame_rate,
channels: 2,
shared_audio_engine_period_in_frames: frame_rate / 100,
channel_mask: None,
},
)
}
};
Ok((async_playback_buffer_stream, audio_shared_format))
}
fn evict_playback_stream_cache(&mut self) {
self.cached_playback_buffer_stream = None;
}
fn is_noop_stream(&self) -> bool {
false
}
}
impl WinAudioServer for NoopStreamSource {
fn new_playback_stream_and_get_shared_format(
&mut self,
num_channels: usize,
format: SampleFormat,
frame_rate: usize,
buffer_size: usize,
) -> Result<(Arc<Mutex<Box<dyn PlaybackBufferStream>>>, AudioSharedFormat), BoxError> {
let (_, playback_buffer_stream) = self
.new_playback_stream(num_channels, format, frame_rate as u32, buffer_size)
.unwrap();
Ok((
Arc::new(Mutex::new(playback_buffer_stream)),
AudioSharedFormat {
bit_depth: 16,
frame_rate,
channels: 2,
shared_audio_engine_period_in_frames: frame_rate / 100,
channel_mask: None,
},
))
}
fn is_noop_stream(&self) -> bool {
true
}
}
pub fn create_win_audio_device() -> Result<WinAudio, BoxError> {
WinAudio::new()
}