use bevy::asset::{Asset, AssetLoader, LoadContext};
use bevy::audio::Decodable;
use bevy::reflect::TypePath;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use thiserror::Error;
use ym2149_common::MetadataFields;
use crate::error::{BevyYm2149Error, Result};
use crate::playback::ToneSettings;
use crate::song_player::{SharedSongPlayer, load_song_from_bytes};
use crate::streaming::{AudioStream, StreamingDecoder};
#[derive(Asset, TypePath)]
pub struct Ym2149AudioSource {
pub data: Vec<u8>,
pub metadata: Ym2149Metadata,
player: SharedSongPlayer,
stream: Arc<AudioStream>,
sample_rate: u32,
total_samples: usize,
}
impl Clone for Ym2149AudioSource {
fn clone(&self) -> Self {
let stream = Arc::new(AudioStream::start(Arc::clone(&self.player)));
Self {
data: self.data.clone(),
metadata: self.metadata.clone(),
player: Arc::clone(&self.player),
stream,
sample_rate: self.sample_rate,
total_samples: self.total_samples,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Ym2149Metadata {
pub title: String,
pub author: String,
pub comment: String,
pub frame_count: usize,
pub duration_seconds: f32,
}
impl MetadataFields for Ym2149Metadata {
fn title(&self) -> &str {
&self.title
}
fn author(&self) -> &str {
&self.author
}
fn comments(&self) -> &str {
&self.comment
}
fn format(&self) -> &str {
"YM" }
fn frame_count(&self) -> Option<usize> {
Some(self.frame_count)
}
fn duration_seconds(&self) -> Option<f32> {
Some(self.duration_seconds)
}
}
impl Ym2149AudioSource {
pub fn new(data: Vec<u8>) -> Result<Self> {
let (player, metrics, metadata) =
load_song_from_bytes(&data).map_err(BevyYm2149Error::MetadataExtraction)?;
let sample_rate = crate::playback::YM2149_SAMPLE_RATE;
let total_samples = metrics.total_samples();
let player = Arc::new(parking_lot::RwLock::new(player));
let stream = Arc::new(AudioStream::start(Arc::clone(&player)));
Ok(Self {
data,
metadata,
player,
stream,
sample_rate,
total_samples,
})
}
pub fn new_with_shared(
data: Vec<u8>,
stereo_gain: Arc<parking_lot::RwLock<(f32, f32)>>,
tone_settings: Arc<parking_lot::RwLock<ToneSettings>>,
) -> Result<Self> {
Self::new_with_subsong(data, stereo_gain, tone_settings, None)
}
pub fn new_with_subsong(
data: Vec<u8>,
stereo_gain: Arc<parking_lot::RwLock<(f32, f32)>>,
tone_settings: Arc<parking_lot::RwLock<ToneSettings>>,
subsong: Option<usize>,
) -> Result<Self> {
let (mut player, metrics, metadata) =
load_song_from_bytes(&data).map_err(BevyYm2149Error::MetadataExtraction)?;
if let Some(index) = subsong {
player.set_subsong(index);
}
let sample_rate = crate::playback::YM2149_SAMPLE_RATE;
let total_samples = metrics.total_samples();
let player = Arc::new(parking_lot::RwLock::new(player));
let stream = Arc::new(AudioStream::start(Arc::clone(&player)));
let (left, right) = *stereo_gain.read();
stream.state.set_stereo_gain(left, right);
stream.state.set_tone_settings(*tone_settings.read());
Ok(Self {
data,
metadata,
player,
stream,
sample_rate,
total_samples,
})
}
pub(crate) fn from_shared_player(
player: SharedSongPlayer,
metadata: Ym2149Metadata,
total_samples: usize,
) -> Self {
let stream = Arc::new(AudioStream::start(Arc::clone(&player)));
Self {
data: Vec::new(),
metadata,
player,
stream,
sample_rate: crate::playback::YM2149_SAMPLE_RATE,
total_samples,
}
}
pub fn duration(&self) -> f32 {
self.metadata.duration_seconds
}
pub fn metadata(&self) -> &Ym2149Metadata {
&self.metadata
}
pub fn player(&self) -> SharedSongPlayer {
Arc::clone(&self.player)
}
pub(crate) fn stream_state(&self) -> Arc<crate::streaming::AudioStreamState> {
self.stream.shared_state()
}
pub(crate) fn shared_player(&self) -> SharedSongPlayer {
Arc::clone(&self.player)
}
pub fn set_stereo_gain(&self, left: f32, right: f32) {
self.stream.state.set_stereo_gain(left, right);
}
pub fn set_tone_settings(&self, settings: ToneSettings) {
self.stream.state.set_tone_settings(settings);
}
}
#[derive(Error, Debug)]
#[error("{0}")]
pub struct Ym2149LoadError(String);
#[derive(Default)]
pub struct Ym2149Loader;
impl AssetLoader for Ym2149Loader {
type Asset = Ym2149AudioSource;
type Settings = ();
type Error = Ym2149LoadError;
async fn load(
&self,
reader: &mut dyn bevy::asset::io::Reader,
_settings: &Self::Settings,
_load_context: &mut LoadContext<'_>,
) -> std::result::Result<Self::Asset, Self::Error> {
let mut data = Vec::new();
reader
.read_to_end(&mut data)
.await
.map_err(|e| Ym2149LoadError(format!("Failed to read asset: {e}")))?;
Ym2149AudioSource::new(data).map_err(|e| Ym2149LoadError(e.to_string()))
}
fn extensions(&self) -> &[&str] {
&["ym", "aks", "ay", "sndh"]
}
}
impl Decodable for Ym2149AudioSource {
type DecoderItem = f32;
type Decoder = StreamingDecoder;
fn decoder(&self) -> Self::Decoder {
StreamingDecoder::new(
self.stream.shared_state(),
self.sample_rate,
self.total_samples,
)
}
}