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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! Provides an interface to output sound to the user's speakers.
//!
//! This departs from the LÖVE API a bit because `SDL2_mixer` is opinionated
//! about the difference between samples and music files, and also makes channel
//! management and such more explicit.
//! This seems a bit awkward but we'll roll with it for now.

use std::fmt;
use std::path;

use sdl2;
use sdl2::mixer::LoaderRWops;

use context::Context;
use util;
use GameError;
use GameResult;

/// An object representing a channel that may be playing a particular Sound.
pub type Channel = sdl2::mixer::Channel;


/// A trait for general operations on sound objects.
pub trait AudioOps {
    fn new_channel() -> Channel;

    fn play_sound(&self, sound: &Sound) -> GameResult<Channel>;

    fn pause(&self);

    fn stop(&self);

    fn resume(&self);

    fn rewind(&self);
}

/// A source of audio data.
pub struct Sound {
    chunk: sdl2::mixer::Chunk,
}

impl Sound {
    /// Load a new Sound
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Sound> {
        let path = path.as_ref();
        let mut buffer: Vec<u8> = Vec::new();
        let rwops = try!(util::rwops_from_path(context, path, &mut buffer));
        // SDL2_mixer SNEAKILY adds this method to RWops.
        let chunk = try!(rwops.load_wav());

        Ok(Sound { chunk: chunk })
    }

    /// Play a sound on the first available `Channel`.
    ///
    /// Returns a `Channel`, which can be used to manipulate the
    /// playback, eg pause, stop, restart, etc.
    pub fn play(&self) -> GameResult<Channel> {
        let channel = sdl2::mixer::channel(-1);
        // This try! is a little redundant but make the
        // GameResult type conversion work right.
        channel.play(&self.chunk, 0)
            .map_err(GameError::from)
    }
}


impl fmt::Debug for Sound {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Sound: {:p}>", self)
    }
}



impl AudioOps for Channel {
    /// Return a new channel that is not playing anything.
    fn new_channel() -> Channel {
        sdl2::mixer::channel(-1)
    }

    /// Plays the given Sound on this `Channel`
    fn play_sound(&self, sound: &Sound) -> GameResult<Channel> {
        let channel = self;
        channel.play(&sound.chunk, 0)
            .map_err(GameError::from)
    }

    /// Pauses playback of the `Channel`
    fn pause(&self) {
        Channel::pause(*self)
    }

    /// Stops whatever the `Channel` is playing.
    fn stop(&self) {
        self.halt()
    }

    /// Resumes playback where it left off (if any).
    fn resume(&self) {
        Channel::resume(*self)
    }

    /// Restarts playing a sound if this channel is currently
    /// playing it.
    fn rewind(&self) {
        if let Some(chunk) = self.get_chunk() {
            self.stop();
            let _ = self.play(&chunk, 0);
        }
    }
}


/// A source of music data.
/// Music is played on a separate dedicated channel from sounds,
/// and also has a separate corpus of decoders than sounds do;
/// see the `SDL2_mixer` documentation for details or use
/// `Context::print_sound_stats()` to print out which decoders
/// are supported for your build.
pub struct Music {
    music: sdl2::mixer::Music,
}


impl Music {
    /// Load the given Music.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Music> {
        let path = path.as_ref();
        let mut buffer: Vec<u8> = Vec::new();
        let rwops = try!(util::rwops_from_path(context, path, &mut buffer));
        // SDL2_mixer SNEAKILY adds this method to RWops.
        let music = try!(rwops.load_music());

        Ok(Music { music: music })
    }
}


impl fmt::Debug for Music {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Music: {:p}>", self)
    }
}

/// Play the given music n times.  -1 loops forever.
pub fn play_music_times(music: &Music, n: i32) -> GameResult<()> {
    music.music
        .play(n)
        .map_err(GameError::from)
}

/// Start playing the given music (looping forever)
pub fn play_music(music: &Music) -> GameResult<()> {
    play_music_times(music, -1)
}

/// Pause currently playing music
pub fn pause_music() {
    sdl2::mixer::Music::pause()
}

/// Resume currently playing music, if any
pub fn resume_music() {
    sdl2::mixer::Music::resume()

}

/// Stop currently playing music
pub fn stop_music() {
    sdl2::mixer::Music::halt()
}

/// Rewind the currently playing music to the beginning.
pub fn rewind_music() {
    sdl2::mixer::Music::rewind()
}