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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use std::error::Error;
use std::io::Cursor;

use rodio::{Decoder, OutputStream, Sink};

use widgetry::{
    Btn, Checkbox, EventCtx, GfxCtx, HorizontalAlignment, Outcome, Panel, VerticalAlignment,
};

pub struct Music {
    inner: Option<Inner>,
}

impl std::default::Default for Music {
    fn default() -> Music {
        Music::empty()
    }
}

struct Inner {
    // Have to keep this alive for the background thread to continue
    _stream: OutputStream,
    sink: Sink,

    panel: Panel,
}

impl Music {
    pub fn empty() -> Music {
        Music { inner: None }
    }

    pub fn start(ctx: &mut EventCtx, play_music: bool) -> Music {
        match Inner::new(ctx, play_music) {
            Ok(inner) => Music { inner: Some(inner) },
            Err(err) => {
                error!("No music, sorry: {}", err);
                Music::empty()
            }
        }
    }

    pub fn event(&mut self, ctx: &mut EventCtx, play_music: &mut bool) {
        if let Some(ref mut inner) = self.inner {
            match inner.panel.event(ctx) {
                Outcome::Clicked(_) => unreachable!(),
                Outcome::Changed => {
                    if inner.panel.is_checked("play music") {
                        *play_music = true;
                        inner.unmute();
                    } else {
                        *play_music = false;
                        inner.mute();
                    }
                }
                _ => {}
            }
        }
    }

    pub fn draw(&self, g: &mut GfxCtx) {
        if let Some(ref inner) = self.inner {
            inner.panel.draw(g);
        }
    }
}

impl Inner {
    fn new(ctx: &mut EventCtx, play_music: bool) -> Result<Inner, Box<dyn Error>> {
        let (stream, stream_handle) = OutputStream::try_default()?;
        let sink = rodio::Sink::try_new(&stream_handle)?;
        let raw_bytes =
            Cursor::new(include_bytes!("../../data/system/assets/music/jingle_bells.ogg").to_vec());
        sink.append(Decoder::new_looped(raw_bytes)?);
        if !play_music {
            sink.set_volume(0.0);
        }

        let panel = Panel::new(
            Checkbox::new(
                play_music,
                Btn::svg_def("system/assets/tools/volume_off.svg").build(ctx, "play music", None),
                Btn::svg_def("system/assets/tools/volume_on.svg").build(ctx, "mute music", None),
            )
            .named("play music")
            .container(),
        )
        .aligned(
            HorizontalAlignment::LeftInset,
            VerticalAlignment::BottomInset,
        )
        .build(ctx);

        Ok(Inner {
            _stream: stream,
            sink,
            panel,
        })
    }

    fn unmute(&mut self) {
        self.sink.set_volume(1.0);
    }

    fn mute(&mut self) {
        self.sink.set_volume(0.0);
    }
}