use std;
use std::rc::Rc;
use std::ops::{Deref, DerefMut};
use log;
use num_derive::FromPrimitive;
use crate::{ll, fmod_result, channel, Channel, ChannelGroup, Error, Guid, Mode,
SoundGroup, System, Timeunit};
#[derive(Clone, Debug, PartialEq)]
pub struct Sound {
inner : Rc <Inner>,
pub (crate) system : System
}
#[derive(Clone, Debug, PartialEq)]
pub struct SoundRef {
pub (crate) sound : Sound
}
#[derive(PartialEq)]
struct Inner {
raw : *mut ll::FMOD_SOUND,
owned : bool
}
#[derive(Default)]
pub struct Createsoundexinfo {
pub length : u32,
pub fileoffset : u32,
pub numchannels : i32,
pub defaultfrequency : i32,
pub format : Format,
pub decodebuffersize : u32,
pub initialsubsound : i32,
pub numsubsounds : i32,
pub inclusionlist : Vec <i32>,
pub pcmreadcallback : ll::FMOD_SOUND_PCMREAD_CALLBACK,
pub pcmsetposcallback : ll::FMOD_SOUND_PCMSETPOS_CALLBACK,
pub nonblockcallback : ll::FMOD_SOUND_NONBLOCK_CALLBACK,
pub dlsname : std::ffi::CString,
pub encryptionkey : std::ffi::CString,
pub maxpolyphony : i32,
pub userdata : Vec <u8>, pub suggestedsoundtype : Type,
pub fileuseropen : ll::FMOD_FILE_OPEN_CALLBACK,
pub fileuserclose : ll::FMOD_FILE_CLOSE_CALLBACK,
pub fileuserread : ll::FMOD_FILE_READ_CALLBACK,
pub fileuserseek : ll::FMOD_FILE_SEEK_CALLBACK,
pub fileuserasyncread : ll::FMOD_FILE_ASYNCREAD_CALLBACK,
pub fileuserasynccancel : ll::FMOD_FILE_ASYNCCANCEL_CALLBACK,
pub fileuserdata : Vec <u8>, pub filebuffersize : i32,
pub channelorder : channel::Channelorder,
pub channelmask : channel::Channelmask,
pub initialsoundgroup : Option <SoundGroup>,
pub initialseekposition : u32,
pub initialseekpostype : Timeunit,
pub ignoresetfilesystem : i32,
pub audioqueuepolicy : u32,
pub minmidigranularity : u32,
pub nonblockthreadid : i32,
pub fsbguid : Option <Guid>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
#[derive(Default)]
pub enum Format {
#[default]
None = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_NONE as isize,
Pcm8 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM8 as isize,
Pcm16 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM16 as isize,
Pcm24 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM24 as isize,
Pcm32 = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCM32 as isize,
PcmFloat = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_PCMFLOAT as isize,
Bitstream = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_BITSTREAM as isize,
MAX = ll::FMOD_SOUND_FORMAT_FMOD_SOUND_FORMAT_MAX as isize
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
#[derive(Default)]
pub enum Type {
#[default]
Unknown = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_UNKNOWN as isize,
Aiff = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AIFF as isize,
Asf = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_ASF as isize,
Dls = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_DLS as isize,
Flac = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FLAC as isize,
Fsb = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FSB as isize,
It = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_IT as isize,
Midi = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MIDI as isize,
Mod = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MOD as isize,
Mpeg = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MPEG as isize,
Oggvorbis = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_OGGVORBIS as isize,
Playlist = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_PLAYLIST as isize,
Raw = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_RAW as isize,
S3m = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_S3M as isize,
User = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_USER as isize,
Wav = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_WAV as isize,
Xm = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_XM as isize,
Xma = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_XMA as isize,
Audioqueue = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AUDIOQUEUE as isize,
At9 = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_AT9 as isize,
Vorbis = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_VORBIS as isize,
MediaFoundation = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MEDIA_FOUNDATION as isize,
Mediacodec = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MEDIACODEC as isize,
Fadpcm = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_FADPCM as isize,
MAX = ll::FMOD_SOUND_TYPE_FMOD_SOUND_TYPE_MAX as isize
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, FromPrimitive)]
pub enum Openstate {
Ready = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_READY as isize,
Loading = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_LOADING as isize,
Error = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_ERROR as isize,
Connecting = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_CONNECTING as isize,
Buffering = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_BUFFERING as isize,
Seeking = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_SEEKING as isize,
Playing = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_PLAYING as isize,
Setposition = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_SETPOSITION as isize,
MAX = ll::FMOD_OPENSTATE_FMOD_OPENSTATE_MAX as isize
}
impl Sound {
#[inline]
pub fn from_raw_parts (
raw : *mut ll::FMOD_SOUND,
owned : bool,
system : System
) -> Self {
let inner = Rc::new (Inner { raw, owned });
Sound { inner, system }
}
#[inline]
fn raw (&self) -> *mut ll::FMOD_SOUND {
self.inner.raw
}
#[inline]
pub fn sound_ref (&self) -> SoundRef {
let sound = {
let inner = Rc::new (Inner {
raw: self.raw(),
owned: false
});
let system = self.system.clone();
Sound { inner, system }
};
SoundRef { sound }
}
#[inline]
pub fn get_3d_cone_settings (&self) -> Result <(f32, f32, f32), Error> {
let mut insideconeangle = 0.0;
let mut outsideconeangle = 0.0;
let mut outsidevolume = 0.0;
unsafe {
fmod_result!(ll::FMOD_Sound_Get3DConeSettings (self.raw(),
&mut insideconeangle, &mut outsideconeangle, &mut outsidevolume
))?;
}
Ok ((insideconeangle, outsideconeangle, outsidevolume))
}
#[inline]
pub fn get_3d_min_max_distance (&self) -> Result <(f32, f32), Error> {
let mut min = 0.0;
let mut max = 0.0;
unsafe {
fmod_result!(
ll::FMOD_Sound_Get3DMinMaxDistance (self.raw(), &mut min, &mut max)
)?;
}
Ok ((min, max))
}
#[inline]
pub fn get_defaults (&self) -> Result <(f32, i32), Error> {
let mut frequency = 0.0;
let mut priority = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetDefaults (self.raw(), &mut frequency, &mut priority)
)?;
}
Ok ((frequency, priority))
}
#[inline]
pub fn get_format (&self) -> Result <(Type, Format, i32, i32), Error> {
let mut type_ = 0;
let mut format = 0;
let mut channels = 0;
let mut bits = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetFormat (self.raw(),
&mut type_, &mut format, &mut channels, &mut bits)
)?;
}
let type_ = Type::from_ll (type_);
let format = Format::from_ll (format);
Ok ((type_, format, channels, bits))
}
#[inline]
pub fn get_length (&self, timeunit : Timeunit) -> Result <u32, Error> {
let mut length = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetLength (self.raw(), &mut length, timeunit.bits())
)?;
}
Ok (length)
}
#[inline]
pub fn get_loop_count (&self) -> Result <i32, Error> {
let mut loopcount = 0;
unsafe {
fmod_result!(ll::FMOD_Sound_GetLoopCount (self.raw(), &mut loopcount))?;
}
Ok (loopcount)
}
#[inline]
pub fn get_loop_points (&self,
loopstarttype : Timeunit, loopendtype : Timeunit
) -> Result <(u32, u32), Error> {
let mut loopstart = 0;
let mut loopend = 0;
unsafe {
fmod_result!(ll::FMOD_Sound_GetLoopPoints (self.raw(),
&mut loopstart, loopstarttype.bits(),
&mut loopend, loopendtype.bits())
)?;
}
Ok ((loopstart, loopend))
}
#[inline]
pub fn get_mode (&self) -> Result <Mode, Error> {
let mut mode = 0;
unsafe {
fmod_result!(ll::FMOD_Sound_GetMode (self.raw(), &mut mode))?;
}
Mode::from_bits (mode).ok_or_else (||{
log::error!("sound get mode bits not valid: {mode:032b}");
Error::InvalidParam
})
}
#[inline]
pub fn get_open_state (&self) -> Result <(Openstate, u32, bool, bool), Error> {
let mut openstate = 0;
let mut percentbuffered = 0;
let mut starving = 0;
let mut diskbusy = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetOpenState (self.raw(),
&mut openstate, &mut percentbuffered, &mut starving, &mut diskbusy)
)?;
}
let openstate = Openstate::from_ll (openstate);
let starving = starving != 0;
let diskbusy = diskbusy != 0;
Ok ((openstate, percentbuffered, starving, diskbusy))
}
#[inline]
pub fn get_name (&self) -> Result <String, Error> {
let namelen = 256i32;
#[expect(clippy::cast_sign_loss)]
let mut name = vec![0; namelen as usize];
unsafe {
fmod_result!(
ll::FMOD_Sound_GetName (self.raw(), name.as_mut_ptr() as *mut i8, namelen)
)?;
}
name.retain (|c| *c != 0x0);
String::from_utf8 (name).map_err (|e|{
log::error!("sound get name string invalid utf8: {e}");
Error::InvalidString
})
}
#[inline]
pub fn get_num_sub_sounds (&self) -> Result <i32, Error> {
let mut numsubsounds = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetNumSubSounds (self.raw(), &mut numsubsounds)
)?;
}
Ok (numsubsounds)
}
#[inline]
pub fn get_num_sync_points (&self) -> Result <i32, Error> {
let mut numsyncpoints = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetNumSyncPoints (self.raw(), &mut numsyncpoints)
)?;
}
Ok (numsyncpoints)
}
#[inline]
pub fn get_num_tags (&self) -> Result <(i32, i32), Error> {
let mut numtags = 0;
let mut numtagsupdated = 0;
unsafe {
fmod_result!(
ll::FMOD_Sound_GetNumTags (self.raw(), &mut numtags, &mut numtagsupdated)
)?;
}
Ok ((numtags, numtagsupdated))
}
#[inline]
pub fn play (&mut self, channel_group : Option <&mut ChannelGroup>, paused : bool)
-> Result <Channel, Error>
{
let channel_group = channel_group
.map_or (std::ptr::null_mut(), ChannelGroup::raw_mut);
let mut channel = std::ptr::null_mut();
unsafe {
fmod_result!(ll::FMOD_System_PlaySound (
self.system.raw_mut(), self.raw(), channel_group, paused as i32,
&mut channel)
)?;
}
Ok (Channel::from_raw_parts (channel, self.sound_ref()))
}
#[inline]
pub fn set_defaults (&self, frequency : f32, priority : u8)
-> Result <(), Error>
{
unsafe {
fmod_result!(
ll::FMOD_Sound_SetDefaults (self.raw(), frequency, priority as i32)
)
}
}
#[inline]
pub fn set_mode (&mut self, mode : Mode) -> Result <(), Error> {
unsafe {
fmod_result!(ll::FMOD_Sound_SetMode (self.raw(), mode.bits()))
}
}
}
impl Deref for SoundRef {
type Target = Sound;
fn deref (&self) -> &Sound {
&self.sound
}
}
impl DerefMut for SoundRef {
fn deref_mut (&mut self) -> &mut Sound {
&mut self.sound
}
}
impl std::fmt::Debug for Inner {
fn fmt (&self, f : &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Inner {{ raw: {:p}, owned: {} }}", self.raw, self.owned)
}
}
impl Drop for Inner {
fn drop (&mut self) {
if self.owned {
unsafe {
let _ = fmod_result!(ll::FMOD_Sound_Release (self.raw)).map_err (|err|
log::error!("error releasing FMOD Sound@{self:?}: {err:?}"));
}
}
}
}
impl Createsoundexinfo {
pub fn to_ll (&mut self) -> ll::FMOD_CREATESOUNDEXINFO {
#[expect(clippy::cast_possible_truncation)]
ll::FMOD_CREATESOUNDEXINFO {
cbsize: std::mem::size_of::<ll::FMOD_CREATESOUNDEXINFO>() as i32,
length: self.length,
fileoffset: self.fileoffset,
numchannels: self.numchannels,
defaultfrequency: self.defaultfrequency,
format: self.format as ll::FMOD_SOUND_FORMAT,
decodebuffersize: self.decodebuffersize,
initialsubsound: self.initialsubsound,
numsubsounds: self.numsubsounds,
inclusionlist: if !self.inclusionlist.is_empty() {
self.inclusionlist.as_mut_ptr()
} else {
std::ptr::null_mut()
},
inclusionlistnum: self.inclusionlist.len() as i32,
pcmreadcallback: self.pcmreadcallback,
pcmsetposcallback: self.pcmsetposcallback,
nonblockcallback: self.nonblockcallback,
dlsname: if !self.dlsname.as_bytes().is_empty() {
self.dlsname.as_c_str().as_ptr() as *mut std::os::raw::c_char
} else {
std::ptr::null_mut()
},
encryptionkey: if !self.encryptionkey.as_bytes().is_empty() {
self.encryptionkey.as_c_str().as_ptr() as *mut std::os::raw::c_char
} else {
std::ptr::null_mut()
},
maxpolyphony: self.maxpolyphony,
userdata: std::ptr::null_mut(), suggestedsoundtype: self.suggestedsoundtype as ll::FMOD_SOUND_TYPE,
fileuseropen: self.fileuseropen,
fileuserclose: self.fileuserclose,
fileuserread: self.fileuserread,
fileuserseek: self.fileuserseek,
fileuserasyncread: self.fileuserasyncread,
fileuserasynccancel: self.fileuserasynccancel,
fileuserdata: std::ptr::null_mut(), filebuffersize: self.filebuffersize,
channelorder: self.channelorder as ll::FMOD_CHANNELORDER,
channelmask: self.channelmask.bits(),
initialsoundgroup: self.initialsoundgroup.as_mut()
.map_or (std::ptr::null_mut(), SoundGroup::raw_mut),
initialseekposition: self.initialseekposition,
initialseekpostype: self.initialseekpostype.bits(),
ignoresetfilesystem: self.ignoresetfilesystem,
audioqueuepolicy: self.audioqueuepolicy,
minmidigranularity: self.minmidigranularity,
nonblockthreadid: self.nonblockthreadid,
fsbguid: self.fsbguid.as_mut().map_or (
std::ptr::null_mut(),
|guid| std::ptr::from_mut::<ll::FMOD_GUID> (guid.as_mut_ll()))
}
}
}
impl Format {
pub fn from_ll (ll : ll::FMOD_SOUND_FORMAT) -> Self {
use num_traits::FromPrimitive;
#[allow(clippy::allow_attributes, clippy::unnecessary_cast)]
Self::from_u32 (ll as u32).unwrap()
}
}
impl Type {
pub fn from_ll (ll : ll::FMOD_SOUND_TYPE) -> Self {
use num_traits::FromPrimitive;
#[allow(clippy::allow_attributes, clippy::unnecessary_cast)]
Self::from_u32 (ll as u32).unwrap()
}
}
impl Openstate {
pub fn from_ll (ll : ll::FMOD_OPENSTATE) -> Self {
use num_traits::FromPrimitive;
#[allow(clippy::allow_attributes, clippy::unnecessary_cast)]
Self::from_u32 (ll as u32).unwrap()
}
}