2019-05-03 09:33:58 +00:00
|
|
|
use generational_arena::Arena;
|
2019-07-25 07:58:34 +00:00
|
|
|
use ruffle_core::backend::audio::decoders::{stream_tag_reader, AdpcmDecoder, Decoder, Mp3Decoder};
|
2019-05-08 16:46:19 +00:00
|
|
|
use ruffle_core::backend::audio::{swf, AudioBackend, AudioStreamHandle, SoundHandle};
|
2019-05-03 09:33:58 +00:00
|
|
|
use std::io::Cursor;
|
2019-07-25 07:58:34 +00:00
|
|
|
use std::sync::Arc;
|
2019-05-03 09:33:58 +00:00
|
|
|
|
|
|
|
pub struct RodioAudioBackend {
|
2019-05-05 22:55:27 +00:00
|
|
|
sounds: Arena<Sound>,
|
2019-07-25 07:58:34 +00:00
|
|
|
active_sounds: Arena<rodio::Sink>,
|
2019-05-03 09:33:58 +00:00
|
|
|
streams: Arena<AudioStream>,
|
|
|
|
device: rodio::Device,
|
|
|
|
}
|
|
|
|
|
2019-05-24 17:25:03 +00:00
|
|
|
#[allow(dead_code)]
|
2019-05-03 09:33:58 +00:00
|
|
|
struct AudioStream {
|
2019-07-25 07:58:34 +00:00
|
|
|
clip_id: swf::CharacterId,
|
2019-07-19 08:32:41 +00:00
|
|
|
info: swf::SoundStreamHead,
|
2019-05-03 09:33:58 +00:00
|
|
|
sink: rodio::Sink,
|
|
|
|
}
|
|
|
|
|
2019-07-25 07:58:34 +00:00
|
|
|
#[allow(dead_code)]
|
2019-05-05 22:55:27 +00:00
|
|
|
struct Sound {
|
|
|
|
format: swf::SoundFormat,
|
2019-07-25 07:58:34 +00:00
|
|
|
data: Arc<Vec<u8>>,
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-03 09:33:58 +00:00
|
|
|
impl RodioAudioBackend {
|
2019-08-15 20:48:51 +00:00
|
|
|
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
2019-05-03 09:33:58 +00:00
|
|
|
Ok(Self {
|
2019-05-05 22:55:27 +00:00
|
|
|
sounds: Arena::new(),
|
2019-05-03 09:33:58 +00:00
|
|
|
streams: Arena::new(),
|
2019-05-10 00:43:53 +00:00
|
|
|
active_sounds: Arena::new(),
|
2019-05-03 09:33:58 +00:00
|
|
|
device: rodio::default_output_device().ok_or("Unable to create output device")?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AudioBackend for RodioAudioBackend {
|
2019-05-05 22:55:27 +00:00
|
|
|
fn register_sound(
|
|
|
|
&mut self,
|
|
|
|
swf_sound: &swf::Sound,
|
2019-08-15 20:48:51 +00:00
|
|
|
) -> Result<SoundHandle, Box<dyn std::error::Error>> {
|
2019-05-05 22:55:27 +00:00
|
|
|
let sound = Sound {
|
|
|
|
format: swf_sound.format.clone(),
|
2019-07-25 07:58:34 +00:00
|
|
|
data: Arc::new(swf_sound.data.clone()),
|
2019-05-05 22:55:27 +00:00
|
|
|
};
|
|
|
|
Ok(self.sounds.insert(sound))
|
|
|
|
}
|
|
|
|
|
2019-07-25 07:58:34 +00:00
|
|
|
fn start_stream(
|
|
|
|
&mut self,
|
|
|
|
clip_id: swf::CharacterId,
|
|
|
|
clip_data: ruffle_core::tag_utils::SwfSlice,
|
|
|
|
stream_info: &swf::SoundStreamHead,
|
|
|
|
) -> AudioStreamHandle {
|
|
|
|
let sink = rodio::Sink::new(&self.device);
|
2019-05-03 09:33:58 +00:00
|
|
|
|
|
|
|
let format = &stream_info.stream_format;
|
|
|
|
let decoder = Mp3Decoder::new(
|
|
|
|
if format.is_stereo { 2 } else { 1 },
|
2019-05-24 17:25:03 +00:00
|
|
|
format.sample_rate.into(),
|
2019-07-25 07:58:34 +00:00
|
|
|
stream_tag_reader(clip_data),
|
|
|
|
);
|
|
|
|
|
2019-05-03 09:33:58 +00:00
|
|
|
let stream = AudioStream {
|
2019-07-25 07:58:34 +00:00
|
|
|
clip_id,
|
2019-05-03 09:33:58 +00:00
|
|
|
info: stream_info.clone(),
|
|
|
|
sink,
|
|
|
|
};
|
2019-07-25 07:58:34 +00:00
|
|
|
stream.sink.append(DecoderSource(Box::new(decoder)));
|
2019-05-03 09:33:58 +00:00
|
|
|
self.streams.insert(stream)
|
|
|
|
}
|
|
|
|
|
2019-05-05 22:55:27 +00:00
|
|
|
fn play_sound(&mut self, sound: SoundHandle) {
|
|
|
|
let sound = &self.sounds[sound];
|
|
|
|
use swf::AudioCompression;
|
|
|
|
|
|
|
|
match sound.format.compression {
|
|
|
|
AudioCompression::Uncompressed => {
|
|
|
|
let mut data = Vec::with_capacity(sound.data.len() / 2);
|
|
|
|
let mut i = 0;
|
|
|
|
while i < sound.data.len() {
|
2019-05-24 17:25:03 +00:00
|
|
|
let val = i16::from(sound.data[i]) | (i16::from(sound.data[i + 1]) << 8);
|
2019-05-05 22:55:27 +00:00
|
|
|
data.push(val);
|
2019-05-24 17:25:03 +00:00
|
|
|
i += 2;
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
let buffer = rodio::buffer::SamplesBuffer::new(
|
|
|
|
if sound.format.is_stereo { 2 } else { 1 },
|
|
|
|
sound.format.sample_rate.into(),
|
|
|
|
data,
|
|
|
|
);
|
2019-07-25 07:58:34 +00:00
|
|
|
let sink = rodio::Sink::new(&self.device);
|
2019-05-05 22:55:27 +00:00
|
|
|
sink.append(buffer);
|
2019-05-10 00:43:53 +00:00
|
|
|
self.active_sounds.insert(sink);
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
2019-07-25 07:58:34 +00:00
|
|
|
AudioCompression::Adpcm => {
|
|
|
|
let decoder = AdpcmDecoder::new(
|
|
|
|
Cursor::new(sound.data.to_vec()),
|
|
|
|
sound.format.is_stereo,
|
|
|
|
sound.format.sample_rate,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
let sink = rodio::Sink::new(&self.device);
|
|
|
|
sink.append(DecoderSource(Box::new(decoder)));
|
|
|
|
self.active_sounds.insert(sink);
|
|
|
|
}
|
2019-05-05 22:55:27 +00:00
|
|
|
AudioCompression::Mp3 => {
|
2019-07-25 07:58:34 +00:00
|
|
|
let decoder = Mp3Decoder::new(
|
|
|
|
if sound.format.is_stereo { 2 } else { 1 },
|
|
|
|
sound.format.sample_rate.into(),
|
|
|
|
Cursor::new(sound.data.to_vec()),
|
|
|
|
);
|
|
|
|
let sink = rodio::Sink::new(&self.device);
|
|
|
|
sink.append(DecoderSource(Box::new(decoder)));
|
2019-05-10 00:43:53 +00:00
|
|
|
self.active_sounds.insert(sink);
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-10 00:43:53 +00:00
|
|
|
fn tick(&mut self) {
|
|
|
|
self.active_sounds.retain(|_, sink| !sink.empty());
|
|
|
|
}
|
2019-05-03 09:33:58 +00:00
|
|
|
}
|
|
|
|
|
2019-08-15 20:48:51 +00:00
|
|
|
struct DecoderSource(Box<dyn Decoder + Send>);
|
2019-05-03 09:33:58 +00:00
|
|
|
|
2019-07-25 07:58:34 +00:00
|
|
|
impl Iterator for DecoderSource {
|
2019-05-03 09:33:58 +00:00
|
|
|
type Item = i16;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<i16> {
|
2019-07-25 07:58:34 +00:00
|
|
|
self.0.next()
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-25 07:58:34 +00:00
|
|
|
impl rodio::Source for DecoderSource {
|
2019-05-05 22:55:27 +00:00
|
|
|
#[inline]
|
|
|
|
fn current_frame_len(&self) -> Option<usize> {
|
2019-07-25 07:58:34 +00:00
|
|
|
None
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn channels(&self) -> u16 {
|
2019-07-25 07:58:34 +00:00
|
|
|
self.0.num_channels().into()
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn sample_rate(&self) -> u32 {
|
2019-07-25 07:58:34 +00:00
|
|
|
self.0.sample_rate().into()
|
2019-05-05 22:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2019-07-25 07:58:34 +00:00
|
|
|
fn total_duration(&self) -> Option<std::time::Duration> {
|
2019-05-05 22:55:27 +00:00
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|