gooey/presentation/fmod/
mod.rs

1//! An FMOD audio backend.
2//!
3//! This backend maps `view::Sound`s to `nsys::fmod::sample::Id`s and
4//! `view::component::Sfx` nodes to voices.
5
6use {log, fmod, nsys};
7use key_vec::KeyVec;
8
9use crate::Tree;
10use crate::tree::NodeId;
11use crate::interface::{view, View};
12use super::{Audio, Presentation};
13
14pub mod remote;
15pub use self::remote::Remote;
16
17#[derive(Debug)]
18pub struct Fmod {
19  pub audition      : nsys::fmod::Audition,
20  pub channel_group : fmod::ChannelGroup,
21  pub sfx           : KeyVec <NodeId, nsys::fmod::voice::Id>
22}
23
24impl Fmod {
25  fn create_sfx (&mut self, node_id : NodeId) {
26    log::trace!("create sfx...");
27    let voice_id = self.audition.new_voice();
28    self.sfx.insert (node_id, voice_id);
29    log::trace!("...create sfx");
30  }
31  fn update_sfx (&mut self, sfx : view::component::Sfx, node_id : NodeId) {
32    log::trace!("update sfx...");
33    let voice_id = self.sfx.get (&node_id).unwrap();
34    let voice    = &mut self.audition.voices[voice_id.0 as usize];
35    if let Some ((sound_id, state)) = sfx.playback {
36      use view::component::sfx::State;
37      let sample_id  = sound_id.into();
38      let mut sample = self.audition.sampler.get (sample_id).unwrap().clone();
39      let channel_group = Some (self.channel_group.clone());
40      match state {
41        State::Ready               => voice.cue (&mut sample, channel_group),
42        State::Play                => voice.play (&mut sample, channel_group),
43        State::Loop                => voice.loop_ (&mut sample, channel_group),
44        State::PlayFrom (position) => voice.play_from (&mut sample,
45          channel_group, position),
46        State::LoopFrom (position) => voice.loop_from (&mut sample,
47          channel_group, position)
48      }
49    } else {
50      voice.stop();
51    }
52    log::trace!("...update sfx");
53  }
54  fn display_value (&mut self, node_id : NodeId, display : view::Display) {
55    match display {
56      view::Display::Create (
57        View { component: view::Component::Sfx (sfx), ..  },
58        node_id,
59        ..
60      ) => {
61        self.create_sfx (node_id.clone());
62        self.update_sfx (sfx, node_id);
63      }
64      view::Display::Update (view::Update::View (view)) => {
65        // play sounds
66        if let Some (sound_id) = view.appearance.sound {
67          let sample_id = sound_id.into();
68          // TODO: allow looping sound ?
69          let mode = Some (fmod::Mode::LOOP_OFF | fmod::Mode::_2D);
70          let _ = self.audition.sampler
71            .play (sample_id, mode, Some (&mut self.channel_group));
72        }
73        // sfx nodes
74        match view.component {
75          view::Component::Sfx (sfx) => {
76            if self.sfx.get (&node_id).is_none() {
77              self.create_sfx (node_id.clone());
78            }
79            self.update_sfx (sfx, node_id);
80          }
81          _ => {}
82        }
83      }
84      view::Display::Update (view::Update::FocusTop) |
85      view::Display::Create (_, _, _) => { }
86      view::Display::Destroy => {
87        if let Some (voice_id) = self.sfx.remove (&node_id) {
88          let _ = self.audition.remove_voice (voice_id).unwrap();
89        }
90      }
91    }
92  }
93}
94
95impl Default for Fmod {
96  fn default() -> Self {
97    let mut audition = nsys::fmod::Audition::default();
98    let channel_group = audition.system.create_channel_group (Some ("gui")).unwrap();
99    let sfx = KeyVec::new();
100    Fmod { audition, channel_group, sfx }
101  }
102}
103
104impl Audio        for Fmod { }
105impl Presentation for Fmod {
106  fn with_root (root : View, root_id : NodeId) -> Self {
107    let mut fmod = Fmod::default();
108    match &root.component {
109      view::Component::Sfx (sfx) => {
110        fmod.create_sfx (root_id.clone());
111        fmod.update_sfx (sfx.clone(), root_id);
112      }
113      _ => {}
114    }
115    fmod
116  }
117  fn get_input (&mut self, _ : &mut Vec <view::Input>) { /* no-op */ }
118
119  fn display_view <V : AsRef <View>> (&mut self,
120    _view_tree      : &Tree <V>,
121    display_values : std::vec::Drain <(NodeId, view::Display)>
122  ) {
123    log::trace!("display view...");
124    for (node_id, display) in display_values {
125      self.display_value (node_id, display);
126    }
127    self.audition.update();
128    log::trace!("...display view");
129  }
130}
131
132impl From <view::Sound> for nsys::fmod::sampler::Id {
133  fn from (sound : view::Sound) -> Self {
134    nsys::fmod::sampler::Id (sound.0)
135  }
136}