Skip to content

Commit

Permalink
Make AudioOutput a Resource (#6436)
Browse files Browse the repository at this point in the history
# Objective

- Make `AudioOutput` a `Resource`.

## Solution

- Do not store `OutputStream` in the struct.
- `mem::forget` `OutputStream`.

---

## Changelog

### Added

- `AudioOutput` is now a `Resource`.

## Migration Guide

- Use `Res<AudioOutput<Source>>` instead of `NonSend<AudioOutput<Source>>`. Same for `Mut` variants.
  • Loading branch information
harudagondi committed Dec 11, 2022
1 parent 63f1a9d commit 344a653
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 6 deletions.
21 changes: 16 additions & 5 deletions crates/bevy_audio/src/audio_output.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
use crate::{Audio, AudioSource, Decodable};
use bevy_asset::{Asset, Assets};
use bevy_ecs::system::{NonSend, Res, ResMut};
use bevy_ecs::system::{Res, ResMut, Resource};
use bevy_reflect::TypeUuid;
use bevy_utils::tracing::warn;
use rodio::{OutputStream, OutputStreamHandle, Sink, Source};
use std::marker::PhantomData;

/// Used internally to play audio on the current "audio device"
///
/// ## Note
///
/// Initializing this resource will leak [`rodio::OutputStream`](rodio::OutputStream)
/// using [`std::mem::forget`].
/// This is done to avoid storing this in the struct (and making this `!Send`)
/// while preventing it from dropping (to avoid halting of audio).
///
/// This is fine when initializing this once (as is default when adding this plugin),
/// since the memory cost will be the same.
/// However, repeatedly inserting this resource into the app will **leak more memory**.
#[derive(Resource)]
pub struct AudioOutput<Source = AudioSource>
where
Source: Decodable,
{
_stream: Option<OutputStream>,
stream_handle: Option<OutputStreamHandle>,
phantom: PhantomData<Source>,
}
Expand All @@ -22,15 +33,15 @@ where
{
fn default() -> Self {
if let Ok((stream, stream_handle)) = OutputStream::try_default() {
// We leak `OutputStream` to prevent the audio from stopping.
std::mem::forget(stream);
Self {
_stream: Some(stream),
stream_handle: Some(stream_handle),
phantom: PhantomData,
}
} else {
warn!("No audio device found.");
Self {
_stream: None,
stream_handle: None,
phantom: PhantomData,
}
Expand Down Expand Up @@ -84,7 +95,7 @@ where

/// Plays audio currently queued in the [`Audio`] resource through the [`AudioOutput`] resource
pub fn play_queued_audio_system<Source: Asset + Decodable>(
audio_output: NonSend<AudioOutput<Source>>,
audio_output: Res<AudioOutput<Source>>,
audio_sources: Option<Res<Assets<Source>>>,
mut audio: ResMut<Audio<Source>>,
mut sinks: ResMut<Assets<AudioSink>>,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_audio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub struct AudioPlugin;

impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) {
app.init_non_send_resource::<AudioOutput<AudioSource>>()
app.init_resource::<AudioOutput<AudioSource>>()
.add_asset::<AudioSource>()
.add_asset::<AudioSink>()
.init_resource::<Audio<AudioSource>>()
Expand Down

0 comments on commit 344a653

Please sign in to comment.