gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
//! An FMOD audio backend.
//!
//! This backend maps `view::Sound`s to `nsys::fmod::sample::Id`s and
//! `view::component::Sfx` nodes to voices.

use {log, fmod, nsys};
use key_vec::KeyVec;

use crate::Tree;
use crate::tree::NodeId;
use crate::interface::{view, View};
use super::{Audio, Presentation};

pub mod remote;
pub use self::remote::Remote;

#[derive(Debug)]
pub struct Fmod {
  pub audition      : nsys::fmod::Audition,
  pub channel_group : fmod::ChannelGroup,
  pub sfx           : KeyVec <NodeId, nsys::fmod::voice::Id>
}

impl Fmod {
  fn create_sfx (&mut self, node_id : NodeId) {
    log::trace!("create sfx...");
    let voice_id = self.audition.new_voice();
    self.sfx.insert (node_id, voice_id);
    log::trace!("...create sfx");
  }
  fn update_sfx (&mut self, sfx : view::component::Sfx, node_id : NodeId) {
    log::trace!("update sfx...");
    let voice_id = self.sfx.get (&node_id).unwrap();
    let voice    = &mut self.audition.voices[voice_id.0 as usize];
    if let Some ((sound_id, state)) = sfx.playback {
      use view::component::sfx::State;
      let sample_id  = sound_id.into();
      let mut sample = self.audition.sampler.get (sample_id).unwrap().clone();
      let channel_group = Some (self.channel_group.clone());
      match state {
        State::Ready               => voice.cue (&mut sample, channel_group),
        State::Play                => voice.play (&mut sample, channel_group),
        State::Loop                => voice.loop_ (&mut sample, channel_group),
        State::PlayFrom (position) => voice.play_from (&mut sample,
          channel_group, position),
        State::LoopFrom (position) => voice.loop_from (&mut sample,
          channel_group, position)
      }
    } else {
      voice.stop();
    }
    log::trace!("...update sfx");
  }
  fn display_value (&mut self, node_id : NodeId, display : view::Display) {
    match display {
      view::Display::Create (
        View { component: view::Component::Sfx (sfx), ..  },
        node_id,
        ..
      ) => {
        self.create_sfx (node_id.clone());
        self.update_sfx (sfx, node_id);
      }
      view::Display::Update (view::Update::View (view)) => {
        // play sounds
        if let Some (sound_id) = view.appearance.sound {
          let sample_id = sound_id.into();
          // TODO: allow looping sound ?
          let mode = Some (fmod::Mode::LOOP_OFF | fmod::Mode::_2D);
          let _ = self.audition.sampler
            .play (sample_id, mode, Some (&mut self.channel_group));
        }
        // sfx nodes
        match view.component {
          view::Component::Sfx (sfx) => {
            if self.sfx.get (&node_id).is_none() {
              self.create_sfx (node_id.clone());
            }
            self.update_sfx (sfx, node_id);
          }
          _ => {}
        }
      }
      view::Display::Update (view::Update::FocusTop) |
      view::Display::Create (_, _, _) => { }
      view::Display::Destroy => {
        if let Some (voice_id) = self.sfx.remove (&node_id) {
          let _ = self.audition.remove_voice (voice_id).unwrap();
        }
      }
    }
  }
}

impl Default for Fmod {
  fn default() -> Self {
    let mut audition = nsys::fmod::Audition::default();
    let channel_group = audition.system.create_channel_group (Some ("gui")).unwrap();
    let sfx = KeyVec::new();
    Fmod { audition, channel_group, sfx }
  }
}

impl Audio        for Fmod { }
impl Presentation for Fmod {
  fn with_root (root : View, root_id : NodeId) -> Self {
    let mut fmod = Fmod::default();
    match &root.component {
      view::Component::Sfx (sfx) => {
        fmod.create_sfx (root_id.clone());
        fmod.update_sfx (sfx.clone(), root_id);
      }
      _ => {}
    }
    fmod
  }
  fn get_input (&mut self, _ : &mut Vec <view::Input>) { /* no-op */ }

  fn display_view <V : AsRef <View>> (&mut self,
    _view_tree      : &Tree <V>,
    display_values : std::vec::Drain <(NodeId, view::Display)>
  ) {
    log::trace!("display view...");
    for (node_id, display) in display_values {
      self.display_value (node_id, display);
    }
    self.audition.update();
    log::trace!("...display view");
  }
}

impl From <view::Sound> for nsys::fmod::sampler::Id {
  fn from (sound : view::Sound) -> Self {
    nsys::fmod::sampler::Id (sound.0)
  }
}