aboutsummaryrefslogtreecommitdiff
path: root/src/classes/audio_clip.rs
blob: 53a265f44a225c1c3f8313e8f7b32b1c1632eef5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use super::streamed_resource::StreamedResource;
use crate::{
    object::{Value, parser::FromValue},
    unityfs::UnityFS,
};
use anyhow::{Result, anyhow, bail};
use log::info;
use serde::Serialize;
use std::io::{Cursor, Read, Seek};

#[derive(Debug, Serialize)]
pub struct AudioClip {
    pub resource: StreamedResource,
    pub channels: i32,
    pub ambisonic: bool,
    pub bits_per_sample: i32,
    pub compression_format: AudioCompressionFormat,
    pub frequency: i32,
    pub is_tracker_format: bool,
    pub legacy_3d: bool,
    pub length: f32,
    pub name: String,
    pub subsound_index: i32,
}

impl FromValue for AudioClip {
    fn from_value(v: Value) -> Result<Self> {
        let mut fields = v.as_class("AudioClip")?;
        Ok(AudioClip {
            resource: fields.field("m_Resource")?,
            ambisonic: fields.field("m_Ambisonic")?,
            bits_per_sample: fields.field("m_BitsPerSample")?,
            compression_format: fields.field("m_CompressionFormat")?,
            channels: fields.field("m_Channels")?,
            frequency: fields.field("m_Frequency")?,
            is_tracker_format: fields.field("m_IsTrackerFormat")?,
            legacy_3d: fields.field("m_Legacy3D")?,
            length: fields.field("m_Length")?,
            name: fields.field("m_Name")?,
            subsound_index: fields.field("m_SubsoundIndex")?,
        })
    }
}

impl AudioClip {
    pub fn read_ogg(&self, fs: &UnityFS<impl Read + Seek>) -> Result<Vec<u8>> {
        let data = self.resource.read(&fs)?;
        match self.compression_format {
            AudioCompressionFormat::Vorbis => {
                info!("reading vorbis FMOD sound bank");
                let bank = fsbex::Bank::new(Cursor::new(data))?;
                assert_eq!(bank.format(), fsbex::AudioFormat::Vorbis);
                assert_eq!(u32::from(bank.num_streams()), 1);
                let stream = bank.into_iter().next().unwrap();

                let mut buf = Cursor::new(Vec::new());
                stream.write(&mut buf)?;

                Ok(buf.into_inner())
            }
            x => todo!("audio format {x:?}"),
        }
    }
}

impl FromValue for AudioCompressionFormat {
    fn from_value(v: Value) -> Result<Self> {
        let x = v
            .as_i32()
            .ok_or(anyhow!("expected i32 AudioCompressionFormat"))?;
        if x < 10 {
            Ok(unsafe { std::mem::transmute(x) })
        } else {
            bail!("AudioCompressionFormat out of range")
        }
    }
}

#[allow(non_camel_case_types)]
#[repr(i32)]
#[derive(Debug, Serialize, PartialEq, Clone, Copy)]

pub enum AudioCompressionFormat {
    PCM,
    Vorbis,
    ADPCM,
    MP3,
    PSMVAG,
    HEVAG,
    XMA,
    AAC,
    GCADPCM,
    ATRAC9,
}