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.clone(), voice_id.clone());
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.clone().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
99      .create_channel_group (Some ("gui")).unwrap();
100    let sfx = KeyVec::new();
101    Fmod { audition, channel_group, sfx }
102  }
103}
104
105impl Audio        for Fmod { }
106impl Presentation for Fmod {
107  fn with_root (root : View, root_id : NodeId) -> Self {
108    let mut fmod = Fmod::default();
109    match &root.component {
110      view::Component::Sfx (sfx) => {
111        fmod.create_sfx (root_id.clone());
112        fmod.update_sfx (sfx.clone(), root_id);
113      }
114      _ => {}
115    }
116    fmod
117  }
118  fn get_input (&mut self, _ : &mut Vec <view::Input>) { /* no-op */ }
119
120  fn display_view <V : AsRef <View>> (&mut self,
121    _view_tree      : &Tree <V>,
122    display_values : std::vec::Drain <(NodeId, view::Display)>
123  ) {
124    log::trace!("display view...");
125    for (node_id, display) in display_values {
126      self.display_value (node_id, display);
127    }
128    self.audition.update();
129    log::trace!("...display view");
130  }
131}
132
133impl From <view::Sound> for nsys::fmod::sampler::Id {
134  fn from (sound : view::Sound) -> Self {
135    nsys::fmod::sampler::Id (sound.0)
136  }
137}