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}