Skip to content

Commit e4a0a51

Browse files
committed
Ensure audio keeps playing when AudioContext goes out of scope
Fixes #464
1 parent d38a0b5 commit e4a0a51

File tree

4 files changed

+27
-9
lines changed

4 files changed

+27
-9
lines changed

examples/roundtrip_latency_test.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn main() {
138138

139139
let estimated_latency = Arc::new(AtomicF64::new(0.));
140140

141-
let _context = if test {
141+
if test {
142142
// emulate loopback
143143
let latency: f32 = 0.017;
144144
println!(
@@ -169,8 +169,6 @@ fn main() {
169169

170170
latency_tester.connect(&delay);
171171
delay.connect(&latency_tester);
172-
173-
context
174172
} else {
175173
// full round trip
176174
let devices = enumerate_devices_sync();
@@ -221,8 +219,6 @@ fn main() {
221219
// create media stream source node with mic stream
222220
let stream_source = context.create_media_stream_source(&mic);
223221
stream_source.connect(&latency_tester);
224-
225-
context
226222
};
227223

228224
loop {

src/context/online.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::Mutex;
44

55
use crate::context::{AudioContextState, BaseAudioContext, ConcreteBaseAudioContext};
66
use crate::events::{EventDispatch, EventHandler, EventPayload, EventType};
7-
use crate::io::{self, AudioBackendManager, ControlThreadInit, RenderThreadInit};
7+
use crate::io::{self, AudioBackendManager, ControlThreadInit, NoneBackend, RenderThreadInit};
88
use crate::media_devices::{enumerate_devices_sync, MediaDeviceInfoKind};
99
use crate::media_streams::{MediaStream, MediaStreamTrack};
1010
use crate::message::{ControlMessage, OneshotNotify};
@@ -129,6 +129,17 @@ impl std::fmt::Debug for AudioContext {
129129
}
130130
}
131131

132+
impl Drop for AudioContext {
133+
fn drop(&mut self) {
134+
// Continue playing the stream if the AudioContext goes out of scope
135+
if self.state() == AudioContextState::Running {
136+
let tombstone = Box::new(NoneBackend::void());
137+
let original = std::mem::replace(self.backend_manager.get_mut().unwrap(), tombstone);
138+
Box::leak(original);
139+
}
140+
}
141+
}
142+
132143
impl BaseAudioContext for AudioContext {
133144
fn base(&self) -> &ConcreteBaseAudioContext {
134145
&self.base

src/io/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::message::ControlMessage;
1414
use crate::{AudioRenderCapacityLoad, RENDER_QUANTUM_SIZE};
1515

1616
mod none;
17+
pub(crate) use none::NoneBackend;
1718

1819
#[cfg(feature = "cpal")]
1920
mod cpal;
@@ -91,7 +92,7 @@ pub(crate) fn build_output(
9192
render_thread_init: RenderThreadInit,
9293
) -> Box<dyn AudioBackendManager> {
9394
if options.sink_id == "none" {
94-
let backend = none::NoneBackend::build_output(options, render_thread_init);
95+
let backend = NoneBackend::build_output(options, render_thread_init);
9596
return Box::new(backend);
9697
}
9798

@@ -222,12 +223,12 @@ fn buffer_size_for_latency_category(
222223
pub(crate) fn enumerate_devices_sync() -> Vec<MediaDeviceInfo> {
223224
#[cfg(feature = "cubeb")]
224225
{
225-
crate::io::cubeb::CubebBackend::enumerate_devices_sync()
226+
cubeb::CubebBackend::enumerate_devices_sync()
226227
}
227228

228229
#[cfg(all(not(feature = "cubeb"), feature = "cpal"))]
229230
{
230-
crate::io::cpal::CpalBackend::enumerate_devices_sync()
231+
cpal::CpalBackend::enumerate_devices_sync()
231232
}
232233

233234
#[cfg(all(not(feature = "cubeb"), not(feature = "cpal")))]

src/io/none.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ pub(crate) struct NoneBackend {
2323
sample_rate: f32,
2424
}
2525

26+
impl NoneBackend {
27+
/// Creates a mock backend to be used as tombstones
28+
pub(crate) fn void() -> Self {
29+
Self {
30+
sample_rate: 0.,
31+
sender: crossbeam_channel::bounded(0).0,
32+
}
33+
}
34+
}
35+
2636
struct Callback {
2737
receiver: Receiver<NoneBackendMessage>,
2838
render_thread: RenderThread,

0 commit comments

Comments
 (0)