fmod/core/sound/
information.rs

1// Copyright (c) 2024 Lily Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std::{
8    ffi::{c_int, c_uint},
9    mem::MaybeUninit,
10};
11
12use fmod_sys::*;
13use lanyard::{Utf8CStr, Utf8CString};
14
15use crate::{get_string, Sound, SoundFormat, SoundType, Tag, TimeUnit};
16
17impl Sound {
18    /// Retrieves the name of a sound.
19    ///
20    /// If FMOD_LOWMEM has been specified in System::createSound, this function will return "(null)".
21    pub fn get_name(&self) -> Result<Utf8CString> {
22        get_string(|name| unsafe {
23            FMOD_Sound_GetName(self.inner, name.as_mut_ptr().cast(), name.len() as c_int)
24        })
25    }
26
27    /// Returns format information about the sound.
28    pub fn get_format(&self) -> Result<(SoundType, SoundFormat, c_int, c_int)> {
29        let mut kind = 0;
30        let mut format = 0;
31        let mut channels = 0;
32        let mut bits = 0;
33        unsafe {
34            FMOD_Sound_GetFormat(self.inner, &mut kind, &mut format, &mut channels, &mut bits)
35                .to_result()?;
36        }
37        let kind = kind.try_into()?;
38        let format = format.try_into()?;
39        Ok((kind, format, channels, bits))
40    }
41
42    /// Retrieves the length using the specified time unit.
43    ///
44    /// A length of `0xFFFFFFFF` means it is of unlimited length, such as an internet radio stream or MOD/S3M/XM/IT file which may loop forever.
45    ///
46    /// Note: Using a VBR (Variable Bit Rate) source that does not have metadata containing its accurate length (such as un-tagged MP3 or MOD/S3M/XM/IT) may return inaccurate length values.
47    /// For these formats, use FMOD_ACCURATETIME when creating the sound.
48    /// This will cause a slight delay and memory increase, as FMOD will scan the whole during creation to find the correct length.
49    /// This flag also creates a seek table to enable sample accurate seeking.
50    pub fn get_length(&self, unit: TimeUnit) -> Result<c_uint> {
51        let mut length = 0;
52        unsafe {
53            FMOD_Sound_GetLength(self.inner, &mut length, unit.into()).to_result()?;
54        }
55        Ok(length)
56    }
57
58    /// Retrieves the number of metadata tags.
59    ///
60    /// 'Tags' are metadata stored within a sound file. These can be things like a song's name, composer etc.
61    ///
62    /// The second tuple field could be periodically checked to see if new tags are available in certain circumstances.
63    /// This might be the case with internet based streams (i.e. shoutcast or icecast) where the name of the song or other attributes might change.
64    pub fn get_tag_count(&self) -> Result<(c_int, c_int)> {
65        let mut tags = 0;
66        let mut updated = 0;
67        unsafe {
68            FMOD_Sound_GetNumTags(self.inner, &mut tags, &mut updated).to_result()?;
69        }
70        Ok((tags, updated))
71    }
72
73    /// Retrieves a metadata tag.
74    ///
75    /// 'Tags' are metadata stored within a sound file. These can be things like a song's name, composer etc.
76    ///
77    /// The number of tags available can be found with Sound::getNumTags.
78    ///
79    /// The way to display or retrieve tags can be done in 3 different ways:
80    /// - All tags can be continuously retrieved by looping from 0 to the numtags value in Sound::getNumTags - 1. Updated tags will refresh automatically, and the 'updated' member of the FMOD_TAG structure will be set to true if a tag has been updated, due to something like a netstream changing the song name for example.
81    /// - Tags can be retrieved by specifying -1 as the index and only updating tags that are returned. If all tags are retrieved and this function is called the function will return an error of FMOD_ERR_TAGNOTFOUND.
82    /// - Specific tags can be retrieved by specifying a name parameter. The index can be 0 based or -1 in the same fashion as described previously.
83    ///
84    /// Note with netstreams an important consideration must be made between songs, a tag may occur that changes the playback rate of the song. It is up to the user to catch this and reset the playback rate with Channel::setFrequency.
85    /// A sample rate change will be signalled with a tag of type FMOD_TAGTYPE_FMOD.
86    ///```rs
87    /// while let Ok(tag) = sound->getTag(None, -1)
88    /// {
89    ///   if matches!(tag.type, TagType::Fmod) {
90    ///     // When a song changes, the sample rate may also change, so compensate here.
91    ///     if tag.name == "Sample Rate Change" && channel {
92    ///       let TagDataType::Float(frequency) = tag.data else {
93    ///         break
94    ///       };
95    ///       result = channel.set_frequency(frequency)?;
96    ///     }
97    ///   }
98    /// }
99    ///```
100    pub fn get_tag(&self, name: Option<&Utf8CStr>, index: c_int) -> Result<Tag> {
101        let mut tag = MaybeUninit::uninit();
102        unsafe {
103            FMOD_Sound_GetTag(
104                self.inner,
105                name.map_or(std::ptr::null(), Utf8CStr::as_ptr),
106                index,
107                tag.as_mut_ptr(),
108            )
109            .to_result()?;
110
111            let tag = tag.assume_init();
112            let tag = Tag::from_ffi(tag);
113            Ok(tag)
114        }
115    }
116}