use crate::{
error::{AudioInitError, LoadSoundError, UpdateAudioStreamError},
ffi,
};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::path::Path;
use super::error::ExportWaveError;
make_thin_wrapper_lifetime!(
Wave,
ffi::Wave,
RaylibAudio,
ffi::UnloadWave
);
make_thin_wrapper_lifetime!(
Sound,
ffi::Sound,
RaylibAudio,
(ffi::UnloadSound),
true
);
make_thin_wrapper_lifetime!(
Music,
ffi::Music,
RaylibAudio,
ffi::UnloadMusicStream
);
make_thin_wrapper_lifetime!(
AudioStream,
ffi::AudioStream,
RaylibAudio,
ffi::UnloadAudioStream
);
pub struct WaveSamples(*mut f32, usize);
impl AsRef<[f32]> for WaveSamples {
fn as_ref(&self) -> &[f32] {
unsafe { std::slice::from_raw_parts(self.0, self.1) }
}
}
impl Drop for WaveSamples {
fn drop(&mut self) {
unsafe { ffi::UnloadWaveSamples(self.0) }
}
}
pub trait AudioSample: private::AudioSample {}
impl<T: private::AudioSample> AudioSample for T {}
mod private {
pub trait AudioSample {}
impl AudioSample for u8 {}
impl AudioSample for i16 {}
impl AudioSample for f32 {}
}
#[derive(Debug, Clone)]
pub struct RaylibAudio(PhantomData<()>);
impl RaylibAudio {
#[inline]
pub fn init_audio_device() -> Result<RaylibAudio, AudioInitError> {
unsafe {
if ffi::IsAudioDeviceReady() {
return Err(AudioInitError::DoubleInit);
}
ffi::InitAudioDevice();
if !ffi::IsAudioDeviceReady() {
return Err(AudioInitError::InitFailed);
}
}
Ok(RaylibAudio(PhantomData))
}
#[inline]
#[must_use]
pub fn is_audio_device_ready(&self) -> bool {
unsafe { ffi::IsAudioDeviceReady() }
}
#[inline]
#[must_use]
pub fn get_master_volume(&self) -> f32 {
unsafe { ffi::GetMasterVolume() }
}
#[inline]
pub fn set_master_volume(&self, volume: f32) {
unsafe { ffi::SetMasterVolume(volume) }
}
#[inline]
pub fn set_audio_stream_buffer_size_default(&self, size: i32) {
unsafe {
ffi::SetAudioStreamBufferSizeDefault(size);
}
}
#[inline]
pub fn new_sound<'aud>(&'aud self, filename: &str) -> Result<Sound<'aud>, LoadSoundError> {
let c_filename = CString::new(filename).unwrap();
let s = unsafe { ffi::LoadSound(c_filename.as_ptr()) };
if s.stream.buffer.is_null() {
return Err(LoadSoundError::LoadFailed {
path: filename.into(),
});
}
Ok(Sound(s, self))
}
#[inline]
pub fn new_sound_from_wave<'aud>(
&'aud self,
wave: &Wave,
) -> Result<Sound<'aud>, LoadSoundError> {
let s = unsafe { ffi::LoadSoundFromWave(wave.0) };
if s.stream.buffer.is_null() {
return Err(LoadSoundError::LoadFromWaveFailed);
}
Ok(Sound(s, self))
}
#[inline]
pub fn new_wave<'aud>(&'aud self, filename: &str) -> Result<Wave<'aud>, LoadSoundError> {
let c_filename = CString::new(filename).unwrap();
let w = unsafe { ffi::LoadWave(c_filename.as_ptr()) };
if w.data.is_null() {
return Err(LoadSoundError::LoadWaveFromFileFailed {
path: filename.into(),
});
}
Ok(Wave(w, self))
}
#[inline]
pub fn new_wave_from_memory<'aud>(
&'aud self,
filetype: &str,
bytes: &[u8],
) -> Result<Wave<'aud>, LoadSoundError> {
let c_filetype = CString::new(filetype).unwrap();
let w = unsafe {
ffi::LoadWaveFromMemory(c_filetype.as_ptr(), bytes.as_ptr(), bytes.len() as i32)
};
if w.data.is_null() {
return Err(LoadSoundError::Null);
};
Ok(Wave(w, self))
}
#[inline]
pub fn new_music<'aud>(&'aud self, filename: &str) -> Result<Music<'aud>, LoadSoundError> {
let c_filename = CString::new(filename).unwrap();
let m = unsafe { ffi::LoadMusicStream(c_filename.as_ptr()) };
if m.stream.buffer.is_null() {
return Err(LoadSoundError::LoadMusicFromFileFailed {
path: filename.into(),
});
}
Ok(Music(m, self))
}
#[inline]
pub fn new_music_from_memory<'aud>(
&'aud self,
filetype: &str,
bytes: &[u8],
) -> Result<Music<'aud>, LoadSoundError> {
let c_filetype = CString::new(filetype).unwrap();
let w = unsafe {
ffi::LoadMusicStreamFromMemory(c_filetype.as_ptr(), bytes.as_ptr(), bytes.len() as i32)
};
if w.stream.buffer.is_null() {
return Err(LoadSoundError::MusicNull);
};
Ok(Music(w, self))
}
#[inline]
#[must_use]
pub fn new_audio_stream(
&self,
sample_rate: u32,
sample_size: u32,
channels: u32,
) -> AudioStream<'_> {
unsafe {
AudioStream(
ffi::LoadAudioStream(sample_rate, sample_size, channels),
self,
)
}
}
}
impl Drop for RaylibAudio {
#[inline]
fn drop(&mut self) {
unsafe { ffi::CloseAudioDevice() }
}
}
impl Wave<'_> {
#[inline]
#[must_use]
pub const fn frame_count(&self) -> u32 {
self.0.frameCount
}
#[inline]
#[must_use]
pub const fn sample_rate(&self) -> u32 {
self.0.sampleRate
}
#[inline]
#[must_use]
pub const fn sample_size(&self) -> u32 {
self.0.sampleSize
}
#[inline]
#[must_use]
pub const fn channels(&self) -> u32 {
self.0.channels
}
#[inline]
#[must_use]
pub unsafe fn inner(self) -> ffi::Wave {
let inner = self.0;
std::mem::forget(self);
inner
}
#[inline]
#[must_use]
pub fn is_wave_valid(&self) -> bool {
unsafe { ffi::IsWaveValid(self.0) }
}
#[inline]
pub fn export(&self, filename: impl AsRef<Path>) -> Result<(), ExportWaveError> {
let c_filename = CString::new(filename.as_ref().to_string_lossy().as_bytes()).unwrap();
let success = unsafe { ffi::ExportWave(self.0, c_filename.as_ptr()) };
if success {
Ok(())
} else {
const QOA: &CStr = c".qoa";
let is_qoa = unsafe { ffi::IsFileExtension(c_filename.as_ptr(), QOA.as_ptr()) };
if is_qoa {
let samples = self.0.sampleSize as i32;
if samples != 16 {
return Err(ExportWaveError::QoaBadSamples(self.0.sampleSize as i32));
}
}
Err(ExportWaveError::ExportFailed)
}
}
#[inline]
#[must_use]
pub(crate) fn copy(&'_ self) -> Wave<'_> {
unsafe { Wave(ffi::WaveCopy(self.0), self.1) }
}
#[inline]
pub fn format(&mut self, sample_rate: i32, sample_size: i32, channels: i32) {
unsafe { ffi::WaveFormat(&mut self.0, sample_rate, sample_size, channels) }
}
#[inline]
pub fn crop(&mut self, init_sample: i32, final_sample: i32) {
unsafe { ffi::WaveCrop(&mut self.0, init_sample, final_sample) }
}
#[inline]
#[must_use]
pub fn load_samples(&self) -> WaveSamples {
WaveSamples(
unsafe { ffi::LoadWaveSamples(self.0) },
self.frameCount as usize,
)
}
}
impl Sound<'_> {
#[inline]
#[must_use]
pub fn is_sound_valid(&self) -> bool {
unsafe { ffi::IsSoundValid(self.0) }
}
#[inline]
#[must_use]
pub const fn frame_count(&self) -> u32 {
self.0.frameCount
}
#[inline]
#[must_use]
pub unsafe fn inner(self) -> ffi::Sound {
let inner = self.0;
std::mem::forget(self);
inner
}
#[inline]
pub fn play(&self) {
unsafe { ffi::PlaySound(self.0) }
}
#[inline]
pub fn pause(&self) {
unsafe { ffi::PauseSound(self.0) }
}
#[inline]
pub fn resume(&self) {
unsafe { ffi::ResumeSound(self.0) }
}
#[inline]
pub fn stop(&self) {
unsafe { ffi::StopSound(self.0) }
}
#[inline]
#[must_use]
pub fn is_playing(&self) -> bool {
unsafe { ffi::IsSoundPlaying(self.0) }
}
#[inline]
pub fn set_volume(&self, volume: f32) {
unsafe { ffi::SetSoundVolume(self.0, volume) }
}
#[inline]
pub fn set_pitch(&self, pitch: f32) {
unsafe { ffi::SetSoundPitch(self.0, pitch) }
}
#[inline]
pub fn set_pan(&self, pan: f32) {
unsafe { ffi::SetSoundPan(self.0, pan) }
}
#[inline]
pub fn update<T: AudioSample>(&mut self, data: &[T]) -> Result<(), UpdateAudioStreamError> {
let expected_sample_size_bits =
usize::try_from(self.stream.sampleSize).expect("sampleSize should be 8, 16, or 32");
let provided_sample_size_bits = size_of::<T>() * u8::BITS as usize;
if provided_sample_size_bits != expected_sample_size_bits {
return Err(UpdateAudioStreamError::SampleSizeMismatch {
expected: expected_sample_size_bits,
provided: provided_sample_size_bits,
});
}
let max_frame_count = usize::try_from(self.frameCount)
.expect("frameCount should be a valid memory allocation size");
let provided_frame_count = data.len();
if provided_frame_count > max_frame_count {
return Err(UpdateAudioStreamError::TooManyFrames {
max: max_frame_count,
provided: provided_frame_count,
});
}
unsafe {
ffi::UpdateSound(
self.0,
data.as_ptr() as *const std::os::raw::c_void,
provided_frame_count.try_into().unwrap(),
);
}
Ok(())
}
}
impl SoundAlias<'_, '_> {
#[inline]
#[must_use]
pub fn is_sound_valid(&self) -> bool {
unsafe { ffi::IsSoundValid(self.0) }
}
#[inline]
#[must_use]
pub const fn frame_count(&self) -> u32 {
self.0.frameCount
}
#[must_use]
pub unsafe fn inner(self) -> ffi::Sound {
let inner = self.0;
std::mem::forget(self);
inner
}
#[inline]
pub fn play(&self) {
unsafe { ffi::PlaySound(self.0) }
}
#[inline]
pub fn pause(&self) {
unsafe { ffi::PauseSound(self.0) }
}
#[inline]
pub fn resume(&self) {
unsafe { ffi::ResumeSound(self.0) }
}
#[inline]
pub fn stop(&self) {
unsafe { ffi::StopSound(self.0) }
}
#[inline]
#[must_use]
pub fn is_playing(&self) -> bool {
unsafe { ffi::IsSoundPlaying(self.0) }
}
#[inline]
pub fn set_volume(&self, volume: f32) {
unsafe { ffi::SetSoundVolume(self.0, volume) }
}
#[inline]
pub fn set_pitch(&self, pitch: f32) {
unsafe { ffi::SetSoundPitch(self.0, pitch) }
}
#[inline]
pub fn set_pan(&self, pan: f32) {
unsafe { ffi::SetSoundPan(self.0, pan) }
}
}
impl Drop for SoundAlias<'_, '_> {
fn drop(&mut self) {
unsafe { ffi::UnloadSoundAlias(self.0) }
}
}
impl Music<'_> {
#[inline]
pub fn play_stream(&self) {
unsafe { ffi::PlayMusicStream(self.0) }
}
#[inline]
pub fn update_stream(&self) {
unsafe { ffi::UpdateMusicStream(self.0) }
}
#[inline]
pub fn stop_stream(&self) {
unsafe { ffi::StopMusicStream(self.0) }
}
#[inline]
pub fn pause_stream(&self) {
unsafe { ffi::PauseMusicStream(self.0) }
}
#[inline]
pub fn resume_stream(&self) {
unsafe { ffi::ResumeMusicStream(self.0) }
}
#[inline]
#[must_use]
pub fn is_stream_playing(&self) -> bool {
unsafe { ffi::IsMusicStreamPlaying(self.0) }
}
#[inline]
pub fn set_volume(&self, volume: f32) {
unsafe { ffi::SetMusicVolume(self.0, volume) }
}
#[inline]
pub fn set_pitch(&self, pitch: f32) {
unsafe { ffi::SetMusicPitch(self.0, pitch) }
}
#[inline]
#[must_use]
pub fn get_time_length(&self) -> f32 {
unsafe { ffi::GetMusicTimeLength(self.0) }
}
#[inline]
#[must_use]
pub fn get_time_played(&self) -> f32 {
unsafe { ffi::GetMusicTimePlayed(self.0) }
}
#[inline]
pub fn seek_stream(&self, position: f32) {
unsafe { ffi::SeekMusicStream(self.0, position) }
}
#[inline]
pub fn set_pan(&self, pan: f32) {
unsafe { ffi::SetMusicPan(self.0, pan) }
}
#[inline]
#[must_use]
pub fn is_music_valid(&self) -> bool {
unsafe { ffi::IsMusicValid(self.0) }
}
}
impl AudioStream<'_> {
#[inline]
#[must_use]
pub fn is_audio_stream_valid(&self) -> bool {
unsafe { ffi::IsAudioStreamValid(self.0) }
}
#[inline]
#[must_use]
pub const fn sample_rate(&self) -> u32 {
self.0.sampleRate
}
#[inline]
#[must_use]
pub const fn sample_size(&self) -> u32 {
self.0.sampleSize
}
#[inline]
#[must_use]
pub const fn channels(&self) -> u32 {
self.0.channels
}
#[must_use]
pub unsafe fn inner(self) -> ffi::AudioStream {
let inner = self.0;
std::mem::forget(self);
inner
}
#[inline]
pub fn update<T: AudioSample>(&mut self, data: &[T]) -> Result<(), UpdateAudioStreamError> {
let expected_sample_size =
usize::try_from(self.sampleSize).expect("sampleSize should be 8, 16, or 32");
let provided_sample_size_bits = size_of::<T>() * u8::BITS as usize;
if provided_sample_size_bits != expected_sample_size {
return Err(UpdateAudioStreamError::SampleSizeMismatch {
expected: expected_sample_size,
provided: provided_sample_size_bits,
});
}
let provided_frame_count = data.len();
unsafe {
ffi::UpdateAudioStream(
self.0,
data.as_ptr() as *const std::os::raw::c_void,
provided_frame_count.try_into().unwrap(),
);
}
Ok(())
}
#[inline]
pub fn play(&self) {
unsafe {
ffi::PlayAudioStream(self.0);
}
}
#[inline]
pub fn pause(&self) {
unsafe {
ffi::PauseAudioStream(self.0);
}
}
#[inline]
pub fn resume(&self) {
unsafe {
ffi::ResumeAudioStream(self.0);
}
}
#[inline]
#[must_use]
pub fn is_playing(&self) -> bool {
unsafe { ffi::IsAudioStreamPlaying(self.0) }
}
#[inline]
pub fn stop(&self) {
unsafe {
ffi::StopAudioStream(self.0);
}
}
#[inline]
pub fn set_volume(&self, volume: f32) {
unsafe {
ffi::SetAudioStreamVolume(self.0, volume);
}
}
#[inline]
pub fn set_pitch(&self, pitch: f32) {
unsafe {
ffi::SetAudioStreamPitch(self.0, pitch);
}
}
#[inline]
#[must_use]
pub fn is_processed(&self) -> bool {
unsafe { ffi::IsAudioStreamProcessed(self.0) }
}
#[inline]
pub fn set_pan(&self, pan: f32) {
unsafe {
ffi::SetAudioStreamPan(self.0, pan);
}
}
}
impl<'bind> Sound<'bind> {
pub fn alias<'snd>(&'snd self) -> Result<SoundAlias<'snd, 'bind>, LoadSoundError> {
let s = unsafe { ffi::LoadSoundAlias(self.0) };
if s.stream.buffer.is_null() {
return Err(LoadSoundError::LoadFromWaveFailed);
}
Ok(SoundAlias(s, PhantomData))
}
}
pub struct SoundAlias<'snd, 'bind>(ffi::Sound, PhantomData<&'snd Sound<'bind>>);