├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── res └── shot.wav └── src ├── audio_controller.rs ├── audio_tags.rs ├── ears.rs ├── examples ├── many_sounds │ └── main.rs ├── record │ └── main.rs └── simple_player │ └── main.rs ├── init.rs ├── internal.rs ├── listener.rs ├── music.rs ├── openal.rs ├── record_context.rs ├── recorder.rs ├── sndfile.rs ├── sndfile_ffi.rs ├── sound.rs ├── sound_data.rs └── states.rs /.gitignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | doc/ 3 | bin/ 4 | /*~ 5 | /.rust 6 | target/ 7 | Cargo.lock 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | env: 3 | global: 4 | - secure: fQxl9WZjIsWWjgyAaTrp5ECuKUpIyZ73z4eIV513pQV3ciy+e18EdzckrWXUV+O6uig6VK8rkR4qv/EogU+zs6zuNwIl1d/DdGA5MB9lhaoO1lEUOGdfFF7LXAkKv57+Ft/tek5rN5cOukV1iZZwlR97P2eaeuSN91411QiYY80= - LD_LIBRARY_PATH=/usr/local/lib 5 | before_install: 6 | - sudo apt-get update 7 | install: 8 | - sudo apt-get install libopenal-dev 9 | - sudo apt-get install libsndfile1-dev 10 | env: 11 | - RUST_TEST_TASKS=1 12 | script: 13 | - rustc --version 14 | - cargo build 15 | - cargo doc 16 | after_script: 17 | - mv target/doc doc 18 | - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "ears" 4 | version = "0.3.0" 5 | authors = ["Jeremy Letang "] 6 | license = "MIT" 7 | keywords = ["audio", "openal", "sndfile"] 8 | readme = "README.md" 9 | 10 | repository = "https://github.com/jeremyletang/ears" 11 | homepage = "https://github.com/jeremyletang/ears" 12 | documentation = "https://github.com/jeremyletang/ears" 13 | description = """ 14 | Easy Api in Rust to play Sounds 15 | """ 16 | 17 | [lib] 18 | name = "ears" 19 | crate-type = ["dylib", "rlib"] 20 | 21 | [[bin]] 22 | 23 | name = "many_sounds" 24 | path = "src/examples/many_sounds/main.rs" 25 | 26 | [[bin]] 27 | 28 | name = "record" 29 | path = "src/examples/record/main.rs" 30 | 31 | [[bin]] 32 | 33 | name = "simple_player" 34 | path = "src/examples/simple_player/main.rs" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Letang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | # this software and associated documentation files (the "Software"), to deal in 7 | # the Software without restriction, including without limitation the rights to 8 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | # the Software, and to permit persons to whom the Software is furnished to do so, 10 | # subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | CARGO_OUT_DIR ?= lib 23 | CARGO_RUSTFLAGS ?= -g -O 24 | 25 | all: ears examples docs 26 | 27 | ears: 28 | mkdir -p $(CARGO_OUT_DIR) 29 | rustc --out-dir=$(CARGO_OUT_DIR) $(CARGO_RUSTFLAGS) src/ears.rs 30 | 31 | docs: 32 | mkdir -p doc 33 | rustdoc -o doc src/ears.rs 34 | 35 | examples: ears 36 | rustc -o bin/many_sounds -L ./lib src/examples/many_sounds/main.rs 37 | rustc -o bin/simple_player -L ./lib src/examples/simple_player/main.rs 38 | rustc -o bin/record -L ./lib src/examples/record/main.rs 39 | 40 | tests: 41 | rustc --test -o bin/ears_tests src/ears.rs 42 | 43 | clean: 44 | rm -rf lib 45 | rm -rf doc 46 | rm -rf bin/many_sounds 47 | rm -rf bin/simple_player 48 | rm -rf bin/record 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ears [![Build Status](https://travis-ci.org/jeremyletang/ears.png?branch=master)](https://travis-ci.org/jeremyletang/ears) 2 | 3 | 4 | __ears__ is a simple library to play Sounds and Musics in Rust. 5 | 6 | __ears__ is build on the top of OpenAL and libsndfile. 7 | 8 | * Provides an access to the OpenAL spatialization functionality in a simple way. 9 | * Accepts a lot of audio formats, thanks to libsndfile. 10 | 11 | # A simple example 12 | 13 | ```Rust 14 | extern crate ears; 15 | use ears::Sound; 16 | 17 | fn main() { 18 | // Create a new Sound. 19 | let snd = Sound::new("path/to/my/sound.ogg").unwrap(); 20 | 21 | // Play the Sound 22 | snd.play(); 23 | 24 | // Wait until the end of the sound 25 | while snd.is_playing() {} 26 | } 27 | ``` 28 | 29 | # Functionalities 30 | 31 | __ears__ provides two ways to play audio files. 32 | 33 | * The Sound class, which represents light sounds who can share a buffer of samples with another Sound. 34 | * The Music class, which is a bigger sound and who can't share sample buffer. 35 | 36 | # Use ears 37 | 38 | Like previously said, __ears__ requires OpenAL and libsndfile. You need to install these two libraries on your system. 39 | 40 | __ears__ compiles against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler. 41 | 42 | __ears__ is built using make, so just type `make` at the root of the __ears__ repository, this command 43 | builds __ears__, examples and the documentation. 44 | 45 | You can build them separately too with the dedicated commands: 46 | 47 | ```Shell 48 | > make ears 49 | > make examples 50 | > make doc 51 | ``` 52 | 53 | then import stuff from __ears__ in your project, you can import all the stuff: 54 | 55 | ```Rust 56 | #[feature(globs)]; 57 | extern crate ears; 58 | 59 | use ears::*; 60 | ``` 61 | 62 | or a specific one: 63 | 64 | ```Rust 65 | extern crate ears; 66 | 67 | use ears::Music; 68 | ``` 69 | -------------------------------------------------------------------------------- /res/shot.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyletang/ears/14b6c0bbf53ca4cfd70ff8884f67fb105d1eb382/res/shot.wav -------------------------------------------------------------------------------- /src/audio_controller.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! The functionnality that a Sound or a Music should provide. 23 | 24 | use states::State; 25 | 26 | /// The functionnality that an Audio Source should provide. 27 | pub trait AudioController { 28 | /// Play or resume the Audio Source. 29 | fn play(&mut self) -> (); 30 | 31 | /// Pause the Audio Source. 32 | fn pause(&mut self) -> (); 33 | 34 | /// Stop the Audio Source. 35 | fn stop(&mut self) -> (); 36 | 37 | /** 38 | * Check if the Audio Source is playing or not. 39 | * 40 | * # Return 41 | * true if the Audio Source is playing, false otherwise. 42 | */ 43 | fn is_playing(&self) -> bool; 44 | 45 | /** 46 | * Get the current state of the Audio Source 47 | * 48 | * # Return 49 | * The state of the Audio Source as a variant of the enum State 50 | */ 51 | fn get_state(&self) -> State; 52 | 53 | /** 54 | * Set the volume of the Audio Source. 55 | * 56 | * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation 57 | * of about -6dB. Each multiplicaton by 2 equals an amplification of about 58 | * +6dB. 59 | * 60 | * # Argument 61 | * * `volume` - The volume of the Audio Source, should be between 0. and 1. 62 | */ 63 | fn set_volume(&mut self, volume: f32) -> (); 64 | 65 | /** 66 | * Get the volume of the Audio Source. 67 | * 68 | * # Return 69 | * The volume of the Audio Source between 0. and 1. 70 | */ 71 | fn get_volume(&self) -> f32; 72 | 73 | /** 74 | * Set the minimal volume for a Audio Source. 75 | * 76 | * The minimum volume allowed for a source, after distance and cone 77 | * attenation is applied (if applicable). 78 | * 79 | * # Argument 80 | * * `min_volume` - The new minimal volume of the Audio Source should be 81 | * between 0. and 1. 82 | */ 83 | fn set_min_volume(&mut self, min_volume: f32) -> (); 84 | 85 | /** 86 | * Get the minimal volume of the Audio Source. 87 | * 88 | * # Return 89 | * The minimal volume of the Audio Source between 0. and 1. 90 | */ 91 | fn get_min_volume(&self) -> f32; 92 | 93 | /** 94 | * Set the maximal volume for a Audio Source. 95 | * 96 | * The maximum volume allowed for a sound, after distance and cone 97 | * attenation is applied (if applicable). 98 | * 99 | * # Argument 100 | * * `max_volume` - The new maximal volume of the Audio Source should be 101 | * between 0. and 1. 102 | */ 103 | fn set_max_volume(&mut self, max_volume: f32) -> (); 104 | 105 | /** 106 | * Get the maximal volume of the Audio Source. 107 | * 108 | * # Return 109 | * The maximal volume of the Audio Source between 0. and 1. 110 | */ 111 | fn get_max_volume(&self) -> f32; 112 | 113 | /** 114 | * Set the Audio Source looping or not 115 | * 116 | * The default looping is false. 117 | * 118 | * # Arguments 119 | * `looping` - The new looping state. 120 | */ 121 | fn set_looping(&mut self, looping: bool) -> (); 122 | 123 | /** 124 | * Check if the Audio Source is looping or not 125 | * 126 | * # Return 127 | * True if the Audio Source is looping, false otherwise. 128 | */ 129 | fn is_looping(&self) -> bool; 130 | 131 | /** 132 | * Set the pitch of the source. 133 | * 134 | * A multiplier for the frequency (sample rate) of the source's buffer. 135 | * 136 | * Default pitch is 1.0. 137 | * 138 | * # Argument 139 | * * `new_pitch` - The new pitch of the Audio Source in the range 140 | * [0.5 - 2.0] 141 | */ 142 | fn set_pitch(&mut self, pitch: f32) -> (); 143 | 144 | /** 145 | * Set the pitch of the source. 146 | * 147 | * # Return 148 | * The pitch of the Audio Source in the range [0.5 - 2.0] 149 | */ 150 | fn get_pitch(&self) -> f32; 151 | 152 | /** 153 | * Set the position of the Audio Source relative to the listener or absolute. 154 | * 155 | * Default position is absolute. 156 | * 157 | * # Argument 158 | * `relative` - True to set Audio Source relative to the 159 | * listener false to set the Audio Source position absolute. 160 | */ 161 | fn set_relative(&mut self, relative: bool) -> (); 162 | 163 | /** 164 | * Is the Audio Source relative to the listener or not ? 165 | * 166 | * # Return 167 | * True if the Audio Source is relative to the listener false otherwise 168 | */ 169 | fn is_relative(&mut self) -> bool; 170 | 171 | /** 172 | * Set the Audio Source location in three dimensional space. 173 | * 174 | * OpenAL, like OpenGL, uses a right handed coordinate system, where in a 175 | * frontal default view X (thumb) points right, Y points up (index finger), 176 | * and Z points towards the viewer/camera (middle finger). 177 | * To switch from a left handed coordinate system, flip the sign on the Z 178 | * coordinate. 179 | * 180 | * Default position is [0., 0., 0.]. 181 | * 182 | * # Argument 183 | * * `position` - A three dimensional vector of f32 containing the 184 | * position of the listener [x, y, z]. 185 | */ 186 | fn set_position(&mut self, position: [f32, ..3]) -> (); 187 | 188 | /** 189 | * Get the position of the Audio Source in three dimensional space. 190 | * 191 | * # Return 192 | * A three dimensional vector of f32 containing the position of the 193 | * listener [x, y, z]. 194 | */ 195 | fn get_position(&self) -> [f32, ..3]; 196 | 197 | /** 198 | * Set the direction of the Audio Source. 199 | * 200 | * Specifies the current direction in local space. 201 | * 202 | * The default direction is: [0., 0., 0.] 203 | * 204 | * # Argument 205 | * `direction` - The new direction of the Audio Source. 206 | */ 207 | fn set_direction(&mut self, direction: [f32, ..3]) -> (); 208 | 209 | /** 210 | * Get the direction of the Audio Source. 211 | * 212 | * # Return 213 | * The current direction of the Audio Source. 214 | */ 215 | fn get_direction(&self) -> [f32, ..3]; 216 | 217 | /** 218 | * Set the maximum distance of the Audio Source. 219 | * 220 | * The distance above which the source is not attenuated any further with a 221 | * clamped distance model, or where attenuation reaches 0.0 gain for linear 222 | * distance models with a default rolloff factor. 223 | * 224 | * The default maximum distance is +inf. 225 | * 226 | * # Argument 227 | * `max_distance` - The new maximum distance in the range [0., +inf] 228 | */ 229 | fn set_max_distance(&mut self, max_distance: f32) -> (); 230 | 231 | /** 232 | * Get the maximum distance of the Audio Source. 233 | * 234 | * # Return 235 | * The maximum distance of the Audio Source in the range [0., +inf] 236 | */ 237 | fn get_max_distance(&self) -> f32; 238 | 239 | /** 240 | * Set the reference distance of the Audio Source. 241 | * 242 | * The distance in units that no attenuation occurs. 243 | * At 0.0, no distance attenuation ever occurs on non-linear 244 | * attenuation models. 245 | * 246 | * The default distance reference is 1. 247 | * 248 | * # Argument 249 | * * `ref_distance` - The new reference distance of the Audio Source. 250 | */ 251 | fn set_reference_distance(&mut self, ref_distance: f32) -> (); 252 | 253 | /** 254 | * Get the reference distance of the Audio Source. 255 | * 256 | * # Return 257 | * The current reference distance of the Audio Source. 258 | */ 259 | fn get_reference_distance(&self) -> f32; 260 | 261 | /** 262 | * Set the attenuation of a Audio Source. 263 | * 264 | * Multiplier to exaggerate or diminish distance attenuation. 265 | * At 0.0, no distance attenuation ever occurs. 266 | * 267 | * The default attenuation is 1. 268 | * 269 | * # Arguments 270 | * `attenuation` - The new attenuation for the Audio Source in the 271 | * range [0., 1.]. 272 | */ 273 | fn set_attenuation(&mut self, attenuation: f32) -> (); 274 | 275 | /** 276 | * Get the attenuation of a Sound. 277 | * 278 | * # Return 279 | * The current attenuation for the Audio Source in the range [0., 1.]. 280 | */ 281 | fn get_attenuation(&self) -> f32; 282 | } 283 | -------------------------------------------------------------------------------- /src/audio_tags.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! The tags extracted from an audio file. 23 | 24 | use sndfile::SndFile; 25 | use sndfile::StringSoundType::{ 26 | Title, 27 | Copyright, 28 | Software, 29 | Artist, 30 | Comment, 31 | Date, 32 | Album, 33 | License, 34 | TrackNumber, 35 | Genre 36 | }; 37 | 38 | /** 39 | * Structure containing the tags of a sound. 40 | * 41 | * If the tags doesn't exist in the sound file, the string is "". 42 | */ 43 | #[deriving(Clone, Show, PartialEq)] 44 | pub struct Tags { 45 | /// The title of the sound as a String 46 | pub title: String, 47 | /// The Copyright of the sound as a String 48 | pub copyright: String, 49 | /// The name of the software used to create the sound as a String 50 | pub software: String, 51 | /// The name of the artist of the sound as a String 52 | pub artist: String, 53 | /// A comment as a String 54 | pub comment: String, 55 | /// The creation date of the sound as a String 56 | pub date: String, 57 | /// The name of the album where the sound come from as a String 58 | pub album: String, 59 | /// The license of the sound as a String 60 | pub license: String, 61 | /// The tracknumber of the sound as a String 62 | pub track_number: String, 63 | /// The genre of the sound as a String 64 | pub genre: String 65 | } 66 | 67 | pub fn empty() -> Tags { 68 | Tags { 69 | title: "".to_string(), 70 | copyright: "".to_string(), 71 | software: "".to_string(), 72 | artist: "".to_string(), 73 | comment: "".to_string(), 74 | date: "".to_string(), 75 | album: "".to_string(), 76 | license: "".to_string(), 77 | track_number: "".to_string(), 78 | genre: "".to_string() 79 | } 80 | } 81 | 82 | pub fn get_sound_tags(file: &SndFile) -> Tags { 83 | Tags { 84 | title: file.get_string(Title).unwrap_or("".to_string()), 85 | copyright: file.get_string(Copyright).unwrap_or("".to_string()), 86 | software: file.get_string(Software).unwrap_or("".to_string()), 87 | artist: file.get_string(Artist).unwrap_or("".to_string()), 88 | comment: file.get_string(Comment).unwrap_or("".to_string()), 89 | date: file.get_string(Date).unwrap_or("".to_string()), 90 | album: file.get_string(Album).unwrap_or("".to_string()), 91 | license: file.get_string(License).unwrap_or("".to_string()), 92 | track_number: file.get_string(TrackNumber).unwrap_or("".to_string()), 93 | genre: file.get_string(Genre).unwrap_or("".to_string()) 94 | } 95 | } 96 | 97 | /// AudioTags trait implemented by all struct who can provides audio. 98 | pub trait AudioTags{ 99 | /// Get the tags of the audio source. 100 | fn get_tags(&self) -> Tags; 101 | } 102 | -------------------------------------------------------------------------------- /src/ears.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | /*! 23 | # ears 24 | 25 | __ears__ is a simple library for play Sounds and Musics in Rust. 26 | 27 | __ears__ is build on the top of OpenAL and libsndfile. 28 | 29 | * Provide anaccess to the OpenAL spatialization functionality in a simple way. 30 | * Accept a lot of audio formats thanks to libsndfile. 31 | 32 | # A simple example 33 | 34 | ```Rust 35 | extern crate ears; 36 | use ears::Sound; 37 | 38 | fn main() { 39 | // Create a new Sound. 40 | let snd = Sound::new("path/to/my/sound.ogg").unwrap(); 41 | 42 | // Play the Sound 43 | snd.play(); 44 | 45 | // Wait until the end of the sound 46 | while snd.is_playing() {} 47 | } 48 | ``` 49 | 50 | # Functionnality 51 | 52 | __ears__ provide two way for play audio files. 53 | 54 | * The Sound class, which represent light sounds who can share a buffer of samples with another Sound. 55 | * The Music class, which is a bigger sound and who can't share sample buffer. 56 | 57 | # Use ears 58 | 59 | As said before, __ears__ require OpenAL and libsndfile, you need to install these two librarieson your system. 60 | 61 | __ears__ compiles against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler. 62 | 63 | __ears__ is built using make, so just type `make` at the root of the __ears__ repository, this command 64 | build __ears__, the examples, and the documentation. 65 | 66 | You can build them separately too with the dedicated commands: 67 | 68 | ```Shell 69 | > make ears 70 | > make examples 71 | > make doc 72 | ``` 73 | 74 | then import stuff from __ears__ in your project, you can import all the stuff : 75 | 76 | ```Rust 77 | #[feature(globs)]; 78 | extern crate ears; 79 | 80 | use ears::*; 81 | ``` 82 | 83 | or a specific one: 84 | 85 | ```Rust 86 | extern crate ears; 87 | 88 | use ears::Music; 89 | ``` 90 | */ 91 | 92 | #![crate_name = "ears"] 93 | #![desc = "Easy Api in Rust for Sounds"] 94 | #![license = "MIT"] 95 | #![crate_type = "dylib"] 96 | #![crate_type = "rlib"] 97 | #![allow(dead_code, unused_attributes)] 98 | #![feature(macro_rules)] 99 | #![feature(unsafe_destructor)] 100 | 101 | extern crate libc; 102 | 103 | // Reexport public API 104 | pub use einit::{init, init_in}; 105 | pub use music::Music; 106 | pub use sound::Sound; 107 | pub use states::State; 108 | pub use sound_data::SoundData; 109 | pub use audio_controller::AudioController; 110 | pub use audio_tags::{AudioTags, Tags}; 111 | pub use recorder::Recorder; 112 | pub use record_context::RecordContext; 113 | 114 | 115 | // Hidden internal bindings 116 | mod internal; 117 | mod openal; 118 | mod sndfile; 119 | 120 | // The public ears API 121 | 122 | #[path = "init.rs"] 123 | mod einit; 124 | pub mod listener; 125 | mod sound; 126 | mod music; 127 | mod sound_data; 128 | mod states; 129 | mod audio_controller; 130 | mod audio_tags; 131 | mod recorder; 132 | mod record_context; 133 | -------------------------------------------------------------------------------- /src/examples/many_sounds/main.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #![crate_name = "many_sounds"] 23 | 24 | extern crate ears; 25 | 26 | use std::io::timer::sleep; 27 | use std::time::Duration; 28 | use std::task; 29 | 30 | use ears::{Sound, AudioController}; 31 | 32 | fn main() -> () { 33 | // call ears_init() function to ensure that the ears context is not destroyed by a task. 34 | ears::init(); 35 | 36 | let mut i = 0u; 37 | 38 | while i < 20 { 39 | task::spawn(proc() { 40 | let mut snd2 = Sound::new("../res/shot.wav").expect("Error on Sound loading."); 41 | snd2.play(); 42 | while snd2.is_playing() {} 43 | }); 44 | i += 1; 45 | sleep(Duration::milliseconds(150i64)); 46 | } 47 | 48 | // Wait until the last sound is played, the main task own the ears context, 49 | // so we should kepp it alive 50 | sleep(Duration::milliseconds(900i64)); 51 | } -------------------------------------------------------------------------------- /src/examples/record/main.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #![crate_name = "record"] 23 | 24 | extern crate ears; 25 | 26 | use std::time::Duration; 27 | use std::io::timer::sleep; 28 | 29 | fn main() -> () { 30 | // call ears_init() function to ensure that the ears context is not destroyed by a task. 31 | ears::init(); 32 | 33 | // initialize the RecordContext 34 | let ctxt = ears::init_in().expect("initialization error !"); 35 | 36 | // Create a new Recorder using the RecordContext 37 | let mut recorder = ears::Recorder::new(ctxt); 38 | recorder.start(); 39 | sleep(Duration::milliseconds(5000i64)); 40 | recorder.stop(); 41 | match recorder.save_to_file("hello") { 42 | true => println!("Save okay !"), 43 | false => println!("Cannot save ...") 44 | } 45 | } -------------------------------------------------------------------------------- /src/examples/simple_player/main.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #![crate_name = "simple_player"] 23 | 24 | extern crate ears; 25 | 26 | use std::io::stdin; 27 | use std::io::stdio::flush; 28 | 29 | use ears::{Music, AudioController}; 30 | use ears::State::{Playing, Stopped, Paused}; 31 | 32 | fn main() { 33 | 34 | // Read the inputs 35 | let mut stdin = stdin(); 36 | 37 | print!("Insert the path to an audio file : "); 38 | flush(); 39 | 40 | let mut line = stdin.read_line().unwrap(); 41 | unsafe { line.as_mut_vec().pop(); } 42 | 43 | // Try to create the music 44 | let mut music = match Music::new(line.as_slice()) { 45 | Some(music) => music, 46 | None => panic!("Cannot load the music.") 47 | }; 48 | 49 | // Play it 50 | music.play(); 51 | 52 | loop { 53 | // Make your choice 54 | println!("Commands :\n\tPlay : l\n\tPause : p\n\tStop : s\n\tExit : x\n"); 55 | match stdin.read_line().unwrap().as_slice() { 56 | "l\n" => music.play(), 57 | "p\n" => music.pause(), 58 | "s\n" => music.stop(), 59 | "x\n" => { music.stop(); break; }, 60 | _ => println!("Unknwon command.") 61 | } 62 | match music.get_state() { 63 | Playing => println!("State : Playing"), 64 | Stopped => println!("State : Stopped"), 65 | Paused => println!("State : Paused"), 66 | _ => unreachable!() 67 | }; 68 | } 69 | println!("Goodbye!"); 70 | } -------------------------------------------------------------------------------- /src/init.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | /*! 23 | * __ears__ initialization (optional). 24 | * 25 | * This module provide a unique function to initialize __ears__. 26 | * Use this function in the case of you don't use __ears__ for the first time 27 | * in you program in the main task. This prevent that the context was created 28 | * and destroyed in a another task. 29 | */ 30 | 31 | use record_context::RecordContext; 32 | use internal::OpenAlData; 33 | 34 | /** 35 | * Initialize the internal context 36 | * 37 | * # Return 38 | * true if initialization is made with success, false otherwise 39 | * 40 | * # Example 41 | * ```Rust 42 | * match ears::init() { 43 | * true => { do_stuff() }, 44 | * false => panic!("ears init error") 45 | * } 46 | * ``` 47 | */ 48 | pub fn init() -> bool { 49 | match OpenAlData::check_al_context() { 50 | Ok(_) => true, 51 | Err(err) => { println!("{}", err); false } 52 | } 53 | } 54 | 55 | /** 56 | * Initialize the input device context 57 | * 58 | * # Return 59 | * true if initialization is made with success, false otherwise 60 | * 61 | * # Example 62 | * ```Rust 63 | * match ears::init_in() { 64 | * Some(rc) => { do_stuff() }, 65 | * None => panic!("ears init input error") 66 | * } 67 | * ``` 68 | */ 69 | pub fn init_in() -> Option { 70 | match OpenAlData::check_al_input_context() { 71 | Ok(ctxt) => Some(ctxt), 72 | Err(err) => { println!("{}", err); None } 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod test { 78 | #![allow(non_snake_case)] 79 | 80 | use init; 81 | use init_in; 82 | 83 | #[test] 84 | fn test_init_ears_OK() -> () { 85 | assert_eq!(init(), true) 86 | } 87 | 88 | #[test] 89 | #[ignore] 90 | fn test_init_in_with_normal_init_OK() -> () { 91 | init(); 92 | assert!(init_in().is_some()) 93 | } 94 | 95 | #[test] 96 | #[ignore] 97 | fn test_init_in_alone_OK() -> () { 98 | assert!(init_in().is_some()) 99 | } 100 | 101 | #[test] 102 | fn test_init_in_in_another_task_OK() -> () { 103 | init(); 104 | spawn(proc() { 105 | assert_eq!(init_in(), None) 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/internal.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Internal class to handle OpenAl context and device. 23 | //! 24 | //! Work as a Singleton, check_al_context must be called before each OpenAl object 25 | //! to be sure that the context is created. 26 | 27 | #![macro_escape] 28 | #![allow(raw_pointer_deriving)] 29 | 30 | use std::cell::RefCell; 31 | use std::ptr; 32 | use openal::ffi; 33 | use record_context; 34 | use record_context::RecordContext; 35 | 36 | thread_local!(static AL_CONTEXT: RefCell> = RefCell::new(box OpenAlData::default())) 37 | 38 | #[deriving(Clone)] 39 | pub struct OpenAlData { 40 | pub al_context: *mut ffi::ALCcontext, 41 | pub al_device: *mut ffi::ALCdevice, 42 | pub al_capt_device: *mut ffi::ALCdevice 43 | } 44 | 45 | impl OpenAlData { 46 | /// Create a new OpenAlData struct 47 | /// 48 | /// Private method. 49 | fn new() -> Result { 50 | let device = unsafe { ffi::alcOpenDevice(ptr::null_mut()) }; 51 | if device.is_null() { 52 | return Err("Internal error: cannot open the default device.".to_string()); 53 | } 54 | let context = unsafe { ffi::alcCreateContext(device, ptr::null_mut()) }; 55 | if context.is_null() { 56 | return Err("Internal error: cannot create the OpenAL context.".to_string()); 57 | } 58 | if unsafe { ffi::alcMakeContextCurrent(context) } == ffi::ALC_FALSE { 59 | return Err("Internal error: cannot make the OpenAL context current.".to_string()); 60 | } 61 | 62 | Ok( 63 | OpenAlData { 64 | al_context: context, 65 | al_device: device, 66 | al_capt_device: ptr::null_mut() 67 | } 68 | ) 69 | } 70 | 71 | fn default() -> OpenAlData { 72 | OpenAlData { 73 | al_context: ptr::null_mut(), 74 | al_device: ptr::null_mut(), 75 | al_capt_device: ptr::null_mut() 76 | } 77 | } 78 | 79 | fn is_default(&self) -> bool { 80 | if self.al_context.is_null() && 81 | self.al_device.is_null() && 82 | self.al_capt_device.is_null() { 83 | false 84 | } else { 85 | true 86 | } 87 | } 88 | 89 | /// Check if the context is created. 90 | /// 91 | /// This function check is the OpenAl context is already created. 92 | /// If context doesn't exist, create it, and store it in a local_data, 93 | /// else get it from the local data and return it. 94 | /// 95 | /// # Return 96 | /// A result containing nothing if the OpenAlData struct exist, 97 | /// otherwise an error message. 98 | pub fn check_al_context() -> Result<(), String> { 99 | if unsafe { ffi::alcGetCurrentContext().is_not_null() } { 100 | return Ok(()) 101 | } 102 | AL_CONTEXT.with(|f| { 103 | let is_def = f.borrow_mut().is_default(); 104 | if is_def { 105 | match OpenAlData::new() { 106 | Ok(al_data) => { 107 | *f.borrow_mut() = box al_data; Ok(()) 108 | }, 109 | Err(err) => Err(err) 110 | } 111 | } else { 112 | Ok(()) 113 | } 114 | }) 115 | } 116 | 117 | fn is_input_context_init() -> Result { 118 | // let is_some = AL_CONTEXT.get().is_some(); 119 | AL_CONTEXT.with(|f| { 120 | let is_def = f.borrow_mut().is_default(); 121 | if !is_def { 122 | let mut new_context = f.borrow_mut(); 123 | if new_context.al_capt_device.is_not_null() { 124 | Ok(record_context::new(new_context.al_capt_device)) 125 | } else { 126 | if "ALC_EXT_CAPTURE".with_c_str(|c_str| unsafe { 127 | ffi::alcIsExtensionPresent(new_context.al_device, c_str) }) == ffi::ALC_FALSE { 128 | return Err("Error: no input device available on your system.".to_string()) 129 | } else { 130 | new_context.al_capt_device = unsafe { 131 | ffi::alcCaptureOpenDevice(ptr::null_mut(), 132 | 44100, 133 | ffi::AL_FORMAT_MONO16, 134 | 44100) }; 135 | if new_context.al_capt_device.is_null() { 136 | return Err("Internal error: cannot open the default capture device.".to_string()) 137 | } else { 138 | let cap_device = new_context.al_capt_device; 139 | return Ok(record_context::new(cap_device)) 140 | } 141 | } 142 | Err("Error: you must request the input context, \ 143 | in the task where you initialize ears.".to_string()) 144 | } 145 | } else { 146 | Err("Error: you must request the input context, \ 147 | in the task where you initialize ears.".to_string()) 148 | } 149 | }) 150 | } 151 | 152 | /// Check if the input context is created. 153 | /// 154 | /// This function check if the input OpenAl context is already created. 155 | /// The input openAL context need the normal AL context + its own extension. 156 | /// So check if the context exist first, then load the input extension. 157 | /// 158 | /// # Return 159 | /// A result containing nothing if the OpenAlData struct exist, 160 | /// otherwise an error message. 161 | pub fn check_al_input_context() -> Result { 162 | if unsafe { ffi::alcGetCurrentContext().is_not_null() } { 163 | OpenAlData::is_input_context_init() 164 | } else { 165 | match OpenAlData::check_al_context() { 166 | Ok(_) => OpenAlData::is_input_context_init(), 167 | Err(err) => Err(err) 168 | } 169 | } 170 | } 171 | } 172 | 173 | impl Drop for OpenAlData { 174 | fn drop(&mut self) { 175 | unsafe { 176 | ffi::alcDestroyContext(self.al_context); 177 | if self.al_capt_device.is_not_null() { 178 | ffi::alcCaptureCloseDevice(self.al_capt_device); 179 | } 180 | ffi::alcCloseDevice(self.al_device); 181 | } 182 | } 183 | } 184 | 185 | macro_rules! check_openal_context( 186 | ($def_ret:expr) => ( 187 | match OpenAlData::check_al_context() { 188 | Ok(_) => {}, 189 | Err(err) => { println!("{}", err); return $def_ret; } 190 | } 191 | ); 192 | ) 193 | -------------------------------------------------------------------------------- /src/listener.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Module for manage the listener in the scene. 23 | 24 | use internal::OpenAlData; 25 | use openal::{ffi, al}; 26 | 27 | /** 28 | * Set the global volume of the scene. 29 | * 30 | * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation 31 | * of about -6dB. Each multiplicaton by 2 equals an amplification of about 32 | * +6dB. 33 | * 34 | * # Argument 35 | * * `volume` - The global volume for the scene, should be between 0. and 1. 36 | * 37 | * # Example 38 | * ```Rust 39 | * # use ears::listener; 40 | * listener::set_volume(0.7f32); 41 | * ``` 42 | */ 43 | pub fn set_volume(volume: f32) -> () { 44 | check_openal_context!(()); 45 | al::alListenerf(ffi::AL_GAIN, volume); 46 | } 47 | 48 | /** 49 | * Get the global volume of the scene. 50 | * 51 | * # Return 52 | * The global volume of the scene between 0. and 1. 53 | * 54 | * # Example 55 | * ```Rust 56 | * # use ears::listener; 57 | * let vol = listener::get_volume(); 58 | * println!("Global volume: {}", vol); 59 | * ``` 60 | */ 61 | pub fn get_volume() -> f32 { 62 | check_openal_context!(0.); 63 | 64 | let mut volume : f32 = 0.; 65 | al::alGetListenerf(ffi::AL_GAIN, &mut volume); 66 | volume 67 | } 68 | 69 | /** 70 | * Set the listener location in three dimensional space. 71 | * 72 | * OpenAL, like OpenGL, uses a right handed coordinate system, where in a 73 | * frontal default view X (thumb) points right, Y points up (index finger), and 74 | * Z points towards the viewer/camera (middle finger). 75 | * To switch from a left handed coordinate system, flip the sign on the Z 76 | * coordinate. 77 | * 78 | * Default is [0., 0., 0.]. 79 | * 80 | * # Argument 81 | * * `position` - A three dimensional vector of f32 containing the position 82 | * of the listener [x, y, z]. 83 | * 84 | * # Example 85 | * ```Rust 86 | * # use ears::listener; 87 | * listener::set_position([45, 90, 35]); 88 | */ 89 | pub fn set_position(position: [f32, ..3]) -> () { 90 | check_openal_context!(()); 91 | al::alListenerfv(ffi::AL_POSITION, &position[0]); 92 | } 93 | 94 | /** 95 | * Get the location of the listener in three dimensional space. 96 | * 97 | * # Return 98 | * A three dimensional vector of f32 containing the position 99 | * of the listener [x, y, z]. 100 | * 101 | * # Example 102 | * ```Rust 103 | * # use ears::listener; 104 | * let pos = listener::get_position(); 105 | * println!("Listener position: {}", &pos); 106 | * ``` 107 | */ 108 | pub fn get_position() -> [f32, ..3] { 109 | check_openal_context!([0., ..3]); 110 | 111 | let mut position: [f32, ..3] = [0., ..3]; 112 | al::alGetListenerfv(ffi::AL_POSITION, &mut position[0]); 113 | position 114 | } 115 | 116 | /** 117 | * Set the orientation of the listener. 118 | * 119 | * Default orientation is : at[0.0, 0.0, -1.0] - up[0.0, 1.0, 0.0] 120 | * 121 | * # Arguments 122 | * * `orientation_at` - The front as a three dimensional vector [x, y, z]. 123 | * * `orientation_up` - The top as a three dimensional vector [x, y, z]. 124 | * 125 | * # Example 126 | * ```Rust 127 | * # use ears::listener; 128 | * listener::set_orientation([0.3f32, -0.4f32, 0.9f32], [0.7f32, 0.3f32, 0.8f32]); 129 | * ``` 130 | */ 131 | pub fn set_orientation(orientation_at: [f32, ..3], orientation_up : [f32, ..3]) { 132 | check_openal_context!(()); 133 | let orientation: [f32, ..6] = [orientation_at[0], orientation_at[1], 134 | orientation_at[2], orientation_up[0], 135 | orientation_up[1], orientation_up[2]]; 136 | al::alListenerfv(ffi::AL_ORIENTATION, &orientation[0]); 137 | } 138 | 139 | /** 140 | * Get the orientation of the listener. 141 | * 142 | * # Return 143 | * A tuple containing the orientation as two three dimensional vector [x, y, z]. 144 | * 145 | * # Example 146 | * ```Rust 147 | * # use ears::listener; 148 | * let (at, up) = listener::get_orientation(); 149 | * println!("At orientation: {}", &at); 150 | * println!("Up orientation: {}", &up); 151 | * ``` 152 | */ 153 | pub fn get_orientation() -> ([f32, ..3], [f32, ..3]) { 154 | check_openal_context!(([0., ..3], [0., ..3])); 155 | let mut orientation: [f32, ..6] = [0., ..6]; 156 | al::alGetListenerfv(ffi::AL_ORIENTATION, &mut orientation[0]); 157 | ([orientation[0], orientation[1], orientation[2]], 158 | [orientation[3], orientation[4], orientation[5]]) 159 | } 160 | 161 | #[cfg(test)] 162 | mod test { 163 | use listener::{set_volume, set_position, set_orientation, 164 | get_volume, get_position, get_orientation}; 165 | 166 | #[test] 167 | pub fn listener_set_volume() -> () { 168 | set_volume(0.77); 169 | assert_eq!(get_volume(), 0.77); 170 | } 171 | 172 | // untill https://github.com/rust-lang/rust/issues/7622 is not used, slice comparsion is used 173 | 174 | #[test] 175 | pub fn listener_set_position() -> () { 176 | set_position([50f32, 150f32, 234f32]); 177 | let res = get_position(); 178 | assert_eq!([res[0], res[1], res[2]][], [50f32, 150f32, 234f32][]) 179 | } 180 | 181 | #[test] 182 | pub fn listener_set_orientation() -> () { 183 | set_orientation([50., 150., 234.], [277., 125., 71.]); 184 | match get_orientation() { 185 | ([x1, y1, z1], [x2, y2, z2]) => { 186 | assert_eq!([x1, y1, z1][], [50f32, 150f32, 234f32][]); 187 | assert_eq!([x2, y2, z2][], [277f32, 125f32, 71f32][]) 188 | } 189 | } 190 | } 191 | } 192 | 193 | -------------------------------------------------------------------------------- /src/music.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Play Music easily. 23 | 24 | use std::io::timer::sleep; 25 | use std::mem; 26 | use std::task; 27 | use std::time::Duration; 28 | use libc::c_void; 29 | use std::vec::Vec; 30 | 31 | use internal::OpenAlData; 32 | use openal::{ffi, al}; 33 | use sndfile::{SndInfo, SndFile}; 34 | use sndfile::OpenMode::Read; 35 | use sndfile::SeekMode::SeekSet; 36 | use states::State; 37 | use states::State::{Initial, Playing, Paused, Stopped}; 38 | use audio_controller::AudioController; 39 | use audio_tags::{Tags, AudioTags, get_sound_tags}; 40 | 41 | /** 42 | * Play Music easily. 43 | * 44 | * Simple class to play musics easily in 2 lines. 45 | * 46 | * The musics are played in them own task and load the samples progressively 47 | * using circular buffers. 48 | * They are not associated to a MusicData like Musics. 49 | * 50 | * # Examples 51 | * ``` 52 | * extern crate ears; 53 | * use ears::Music; 54 | * 55 | * fn main() -> () { 56 | * // Load a Music 57 | * let msc = Music::new("path/to/my/Music.flac").unwrap(); 58 | * 59 | * // Play it 60 | * msc.play(); 61 | * } 62 | * ``` 63 | */ 64 | pub struct Music { 65 | /// The internal OpenAL source identifier 66 | al_source: u32, 67 | /// The internal OpenAL buffers 68 | al_buffers: [u32, ..2], 69 | /// The file open with libmscfile 70 | file: Option>, 71 | /// Information of the file 72 | file_infos: SndInfo, 73 | /// Quantity of sample to read each time 74 | sample_to_read: i32, 75 | /// Format of the sample 76 | sample_format: i32, 77 | /// Audio tags 78 | sound_tags: Tags 79 | } 80 | 81 | impl Music { 82 | /** 83 | * Create a new Music 84 | * 85 | * # Argument 86 | * * `path` - The path of the file to load the music 87 | * 88 | * # Return 89 | * An Option containing Some(Music) on success, None otherwise 90 | */ 91 | pub fn new(path: &str) -> Option { 92 | // Check that OpenAL is launched 93 | check_openal_context!(None); 94 | // Retrieve File and Music datas 95 | let file = match SndFile::new(path, Read) { 96 | Ok(file) => box file, 97 | Err(err) => { println!("{}", err); return None; } 98 | }; 99 | let infos = file.get_sndinfo(); 100 | 101 | // create the source and the buffers 102 | let mut source_id = 0; 103 | let mut buffer_ids = [0, ..2]; 104 | // create the source 105 | al::alGenSources(1, &mut source_id); 106 | // create the buffers 107 | al::alGenBuffers(2, &mut buffer_ids[0]); 108 | 109 | // Retrieve format informations 110 | let format = match al::get_channels_format(infos.channels) { 111 | Some(fmt) => fmt, 112 | None => { 113 | println!("Internal error : unrecognized format."); 114 | return None; 115 | } 116 | }; 117 | 118 | // Check if there is OpenAL internal error 119 | match al::openal_has_error() { 120 | Some(err) => { println!("{}", err); return None; }, 121 | None => {} 122 | }; 123 | 124 | let sound_tags = get_sound_tags(&*file); 125 | 126 | Some( Music { 127 | al_source: source_id, 128 | al_buffers: buffer_ids, 129 | file: Some(file), 130 | file_infos: infos, 131 | sample_to_read: 50000, 132 | sample_format: format, 133 | sound_tags: sound_tags 134 | }) 135 | } 136 | 137 | fn process_music(&mut self) -> () { 138 | let (chan, port) = channel(); 139 | let sample_t_r = self.sample_to_read; 140 | let sample_rate = self.file_infos.samplerate; 141 | let sample_format = self.sample_format; 142 | let al_source = self.al_source; 143 | let al_buffers = self.al_buffers; 144 | 145 | // create buff 146 | let mut samples = Vec::from_elem(sample_t_r as uint, 0i16); 147 | 148 | // full buff1 149 | let mut len = mem::size_of::() * 150 | self.file.as_mut().unwrap().read_i16(samples.as_mut_slice(), sample_t_r as i64) as uint; 151 | al::alBufferData(al_buffers[0], 152 | sample_format, 153 | samples.as_ptr() as *mut c_void, 154 | len as i32, 155 | sample_rate); 156 | 157 | // full buff2 158 | samples.clear(); 159 | len = mem::size_of::() * 160 | self.file.as_mut().unwrap().read_i16(samples.as_mut_slice(), sample_t_r as i64) as uint; 161 | al::alBufferData(al_buffers[1], 162 | sample_format, 163 | samples.as_ptr() as *mut c_void, 164 | len as i32, 165 | sample_rate); 166 | 167 | // Queue the buffers 168 | al::alSourceQueueBuffers(al_source, 2, &al_buffers[0]); 169 | 170 | // Launche the Music 171 | al::alSourcePlay(al_source); 172 | 173 | task::spawn(proc() { 174 | match OpenAlData::check_al_context() { 175 | Ok(_) => {}, 176 | Err(err) => { println!("{}", err);} 177 | }; 178 | 179 | let mut file: Box = port.recv(); 180 | let mut samples = Vec::from_elem(sample_t_r as uint, 0i16); 181 | let mut status = ffi::AL_PLAYING; 182 | let mut i = 0; 183 | let mut buf = 0; 184 | let mut read; 185 | 186 | while status != ffi::AL_STOPPED { 187 | // wait a bit 188 | sleep(Duration::milliseconds(50i64)); 189 | if status == ffi::AL_PLAYING { 190 | al::alGetSourcei(al_source, 191 | ffi::AL_BUFFERS_PROCESSED, 192 | &mut i); 193 | if i != 0 { 194 | samples.clear(); 195 | al::alSourceUnqueueBuffers(al_source, 1, &mut buf); 196 | read = file.read_i16(samples.as_mut_slice(), sample_t_r as i64) * 197 | mem::size_of::() as i64; 198 | al::alBufferData(buf, 199 | sample_format, 200 | samples.as_ptr() as *mut c_void, 201 | read as i32, 202 | sample_rate); 203 | al::alSourceQueueBuffers(al_source, 1, &buf); 204 | } 205 | } 206 | // Get source status 207 | status = al::alGetState(al_source); 208 | } 209 | al::alSourcei(al_source, ffi::AL_BUFFER, 0); 210 | }); 211 | let file = self.file.as_ref().unwrap().clone(); 212 | chan.send(file); 213 | } 214 | 215 | } 216 | 217 | impl AudioTags for Music { 218 | /** 219 | * Get the tags of a Sound. 220 | * 221 | * # Return 222 | * A borrowed pointer to the internal struct SoundTags 223 | */ 224 | fn get_tags(&self) -> Tags { 225 | self.sound_tags.clone() 226 | } 227 | } 228 | 229 | impl AudioController for Music { 230 | /** 231 | * Play or resume the Music. 232 | */ 233 | fn play(&mut self) -> () { 234 | check_openal_context!(()); 235 | 236 | match self.get_state() { 237 | Paused => { al::alSourcePlay(self.al_source); return; }, 238 | _ => { 239 | if self.is_playing() { 240 | al::alSourceStop(self.al_source); 241 | // wait a bit for openal terminate 242 | sleep(Duration::milliseconds(50i64)); 243 | } 244 | self.file.as_mut().unwrap().seek(0, SeekSet); 245 | self.process_music(); 246 | } 247 | } 248 | } 249 | 250 | /** 251 | * Pause the Music. 252 | */ 253 | fn pause(&mut self) -> () { 254 | check_openal_context!(()); 255 | 256 | al::alSourcePause(self.al_source) 257 | } 258 | 259 | /** 260 | * Stop the Music. 261 | */ 262 | fn stop(&mut self) -> () { 263 | check_openal_context!(()); 264 | 265 | al::alSourceStop(self.al_source); 266 | sleep(Duration::milliseconds(50i64)); 267 | } 268 | 269 | /** 270 | * Check if the Music is playing or not. 271 | * 272 | * # Return 273 | * True if the Music is playing, false otherwise. 274 | */ 275 | fn is_playing(&self) -> bool { 276 | match self.get_state() { 277 | Playing => true, 278 | _ => false 279 | } 280 | } 281 | 282 | /** 283 | * Get the current state of the Music 284 | * 285 | * # Return 286 | * The state of the music as a variant of the enum State 287 | */ 288 | fn get_state(&self) -> State { 289 | check_openal_context!(Initial); 290 | 291 | let state = al::alGetState(self.al_source); 292 | 293 | match state { 294 | ffi::AL_INITIAL => Initial, 295 | ffi::AL_PLAYING => Playing, 296 | ffi::AL_PAUSED => Paused, 297 | ffi::AL_STOPPED => Stopped, 298 | _ => unreachable!() 299 | } 300 | } 301 | 302 | /** 303 | * Set the volume of the Music. 304 | * 305 | * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation 306 | * of about -6dB. Each multiplicaton by 2 equals an amplification of about 307 | * +6dB. 308 | * 309 | * # Argument 310 | * * `volume` - The volume of the Music, should be between 0. and 1. 311 | */ 312 | fn set_volume(&mut self, volume: f32) -> () { 313 | check_openal_context!(()); 314 | 315 | al::alSourcef(self.al_source, ffi::AL_GAIN, volume); 316 | } 317 | 318 | /** 319 | * Get the volume of the Music. 320 | * 321 | * # Return 322 | * The volume of the Music between 0. and 1. 323 | */ 324 | fn get_volume(&self) -> f32 { 325 | check_openal_context!(0.); 326 | 327 | let mut volume : f32 = 0.; 328 | al::alGetSourcef(self.al_source, ffi::AL_GAIN, &mut volume); 329 | volume 330 | } 331 | 332 | /** 333 | * Set the minimal volume for a Music. 334 | * 335 | * The minimum volume allowed for a music, after distance and cone 336 | * attenation is applied (if applicable). 337 | * 338 | * # Argument 339 | * * `min_volume` - The new minimal volume of the Music should be 340 | * between 0. and 1. 341 | */ 342 | fn set_min_volume(&mut self, min_volume: f32) -> () { 343 | check_openal_context!(()); 344 | 345 | al::alSourcef(self.al_source, ffi::AL_MIN_GAIN, min_volume); 346 | } 347 | 348 | /** 349 | * Get the minimal volume of the Music. 350 | * 351 | * # Return 352 | * The minimal volume of the Music between 0. and 1. 353 | */ 354 | fn get_min_volume(&self) -> f32 { 355 | check_openal_context!(0.); 356 | 357 | let mut volume : f32 = 0.; 358 | al::alGetSourcef(self.al_source, ffi::AL_MIN_GAIN, &mut volume); 359 | volume 360 | } 361 | 362 | /** 363 | * Set the maximal volume for a Music. 364 | * 365 | * The maximum volume allowed for a Music, after distance and cone 366 | * attenation is applied (if applicable). 367 | * 368 | * # Argument 369 | * * `max_volume` - The new maximal volume of the Music should be 370 | * between 0. and 1. 371 | */ 372 | fn set_max_volume(&mut self, max_volume: f32) -> () { 373 | check_openal_context!(()); 374 | 375 | al::alSourcef(self.al_source, ffi::AL_MAX_GAIN, max_volume); 376 | } 377 | 378 | /** 379 | * Get the maximal volume of the Music. 380 | * 381 | * # Return 382 | * The maximal volume of the Music between 0. and 1. 383 | */ 384 | fn get_max_volume(&self) -> f32 { 385 | check_openal_context!(0.); 386 | 387 | let mut volume : f32 = 0.; 388 | al::alGetSourcef(self.al_source, ffi::AL_MAX_GAIN, &mut volume); 389 | volume 390 | } 391 | 392 | /** 393 | * Set the Music looping or not 394 | * 395 | * The default looping is false. 396 | * 397 | * # Arguments 398 | * `looping` - The new looping state. 399 | */ 400 | fn set_looping(&mut self, looping: bool) -> () { 401 | check_openal_context!(()); 402 | 403 | match looping { 404 | true => al::alSourcei(self.al_source, 405 | ffi::AL_LOOPING, 406 | ffi::ALC_TRUE as i32), 407 | false => al::alSourcei(self.al_source, 408 | ffi::AL_LOOPING, 409 | ffi::ALC_FALSE as i32) 410 | }; 411 | } 412 | 413 | /** 414 | * Check if the Music is looping or not 415 | * 416 | * # Return 417 | * True if the Music is looping, false otherwise. 418 | */ 419 | fn is_looping(&self) -> bool { 420 | check_openal_context!(false); 421 | 422 | let mut boolean = 0; 423 | al::alGetSourcei(self.al_source, ffi::AL_LOOPING, &mut boolean); 424 | match boolean as i8 { 425 | ffi::ALC_TRUE => true, 426 | ffi::ALC_FALSE => false, 427 | _ => unreachable!() 428 | } 429 | } 430 | 431 | /** 432 | * Set the pitch of the Music. 433 | * 434 | * A multiplier for the frequency (sample rate) of the Music's buffer. 435 | * 436 | * Default pitch is 1.0. 437 | * 438 | * # Argument 439 | * * `new_pitch` - The new pitch of the Music in the range [0.5 - 2.0] 440 | */ 441 | fn set_pitch(&mut self, pitch: f32) -> () { 442 | check_openal_context!(()); 443 | 444 | al::alSourcef(self.al_source, ffi::AL_PITCH, pitch) 445 | } 446 | 447 | /** 448 | * Set the pitch of the Music. 449 | * 450 | * # Return 451 | * The pitch of the Music in the range [0.5 - 2.0] 452 | */ 453 | fn get_pitch(&self) -> f32 { 454 | check_openal_context!(0.); 455 | 456 | let mut pitch = 0.; 457 | al::alGetSourcef(self.al_source, ffi::AL_PITCH, &mut pitch); 458 | pitch 459 | } 460 | 461 | /** 462 | * Set the position of the Music relative to the listener or absolute. 463 | * 464 | * Default position is absolute. 465 | * 466 | * # Argument 467 | * `relative` - True to set Music relative to the listener false to set the 468 | * Music position absolute. 469 | */ 470 | fn set_relative(&mut self, relative: bool) -> () { 471 | check_openal_context!(()); 472 | 473 | match relative { 474 | true => al::alSourcei(self.al_source, 475 | ffi::AL_SOURCE_RELATIVE, 476 | ffi::ALC_TRUE as i32), 477 | false => al::alSourcei(self.al_source, 478 | ffi::AL_SOURCE_RELATIVE, 479 | ffi::ALC_FALSE as i32) 480 | }; 481 | } 482 | 483 | /** 484 | * Is the Music relative to the listener or not ? 485 | * 486 | * # Return 487 | * True if the Music is relative to the listener false otherwise 488 | */ 489 | fn is_relative(&mut self) -> bool { 490 | check_openal_context!(false); 491 | 492 | let mut boolean = 0; 493 | al::alGetSourcei(self.al_source, ffi::AL_SOURCE_RELATIVE, &mut boolean); 494 | match boolean as i8 { 495 | ffi::ALC_TRUE => true, 496 | ffi::ALC_FALSE => false, 497 | _ => unreachable!() 498 | } 499 | } 500 | 501 | /** 502 | * Set the Music location in three dimensional space. 503 | * 504 | * OpenAL, like OpenGL, uses a right handed coordinate system, where in a 505 | * frontal default view X (thumb) points right, Y points up (index finger), 506 | * and Z points towards the viewer/camera (middle finger). 507 | * To switch from a left handed coordinate system, flip the sign on the Z 508 | * coordinate. 509 | * 510 | * Default position is [0., 0., 0.]. 511 | * 512 | * # Argument 513 | * * `position` - A three dimensional vector of f32 containing the position 514 | * of the listener [x, y, z]. 515 | */ 516 | fn set_position(&mut self, position: [f32, ..3]) -> () { 517 | check_openal_context!(()); 518 | 519 | al::alSourcefv(self.al_source, ffi::AL_POSITION, &position[0]); 520 | } 521 | 522 | /** 523 | * Get the position of the Music in three dimensional space. 524 | * 525 | * # Return 526 | * A three dimensional vector of f32 containing the position of the 527 | * listener [x, y, z]. 528 | */ 529 | fn get_position(&self) -> [f32, ..3] { 530 | check_openal_context!([0., ..3]); 531 | 532 | let mut position : [f32, ..3] = [0., ..3]; 533 | al::alGetSourcefv(self.al_source, ffi::AL_POSITION, &mut position[0]); 534 | position 535 | } 536 | 537 | /** 538 | * Set the direction of the Music. 539 | * 540 | * Specifies the current direction in local space. 541 | * 542 | * The default direction is: [0., 0., 0.] 543 | * 544 | * # Argument 545 | * `direction` - The new direction of the Music. 546 | */ 547 | fn set_direction(&mut self, direction: [f32, ..3]) -> () { 548 | check_openal_context!(()); 549 | 550 | al::alSourcefv(self.al_source, ffi::AL_DIRECTION, &direction[0]); 551 | } 552 | 553 | /** 554 | * Get the direction of the Music. 555 | * 556 | * # Return 557 | * The current direction of the Music. 558 | */ 559 | fn get_direction(&self) -> [f32, ..3] { 560 | check_openal_context!([0., ..3]); 561 | 562 | let mut direction : [f32, ..3] = [0., ..3]; 563 | al::alGetSourcefv(self.al_source, ffi::AL_DIRECTION, &mut direction[0]); 564 | direction 565 | } 566 | 567 | /** 568 | * Set the maximum distance of the Music. 569 | * 570 | * The distance above which the source is not attenuated any further with a 571 | * clamped distance model, or where attenuation reaches 0.0 gain for linear 572 | * distance models with a default rolloff factor. 573 | * 574 | * The default maximum distance is +inf. 575 | * 576 | * # Argument 577 | * `max_distance` - The new maximum distance in the range [0., +inf] 578 | */ 579 | fn set_max_distance(&mut self, max_distance: f32) -> () { 580 | check_openal_context!(()); 581 | 582 | al::alSourcef(self.al_source, ffi::AL_MAX_DISTANCE, max_distance); 583 | } 584 | 585 | /** 586 | * Get the maximum distance of the Music. 587 | * 588 | * # Return 589 | * The maximum distance of the Music in the range [0., +inf] 590 | */ 591 | fn get_max_distance(&self) -> f32 { 592 | check_openal_context!(0.); 593 | 594 | let mut max_distance = 0.; 595 | al::alGetSourcef(self.al_source, ffi::AL_MAX_DISTANCE, &mut max_distance); 596 | max_distance 597 | } 598 | 599 | /** 600 | * Set the reference distance of the Music. 601 | * 602 | * The distance in units that no attenuation occurs. 603 | * At 0.0, no distance attenuation ever occurs on non-linear 604 | * attenuation models. 605 | * 606 | * The default distance reference is 1. 607 | * 608 | * # Argument 609 | * * `ref_distance` - The new reference distance of the Music. 610 | */ 611 | fn set_reference_distance(&mut self, ref_distance: f32) -> () { 612 | check_openal_context!(()); 613 | 614 | al::alSourcef(self.al_source, ffi::AL_REFERENCE_DISTANCE, ref_distance); 615 | } 616 | 617 | /** 618 | * Get the reference distance of the Music. 619 | * 620 | * # Return 621 | * The current reference distance of the Music. 622 | */ 623 | fn get_reference_distance(&self) -> f32 { 624 | check_openal_context!(1.); 625 | 626 | let mut ref_distance = 0.; 627 | al::alGetSourcef(self.al_source, 628 | ffi::AL_REFERENCE_DISTANCE, 629 | &mut ref_distance); 630 | ref_distance 631 | } 632 | 633 | /** 634 | * Set the attenuation of a Music. 635 | * 636 | * Multiplier to exaggerate or diminish distance attenuation. 637 | * At 0.0, no distance attenuation ever occurs. 638 | * 639 | * The default attenuation is 1. 640 | * 641 | * # Arguments 642 | * `attenuation` - The new attenuation for the Music in the range [0., 1.]. 643 | */ 644 | fn set_attenuation(&mut self, attenuation: f32) -> () { 645 | check_openal_context!(()); 646 | 647 | al::alSourcef(self.al_source, ffi::AL_ROLLOFF_FACTOR, attenuation); 648 | } 649 | 650 | /** 651 | * Get the attenuation of a Music. 652 | * 653 | * # Return 654 | * The current attenuation for the Music in the range [0., 1.]. 655 | */ 656 | fn get_attenuation(&self) -> f32 { 657 | check_openal_context!(1.); 658 | 659 | let mut attenuation = 0.; 660 | al::alGetSourcef(self.al_source, 661 | ffi::AL_ROLLOFF_FACTOR, 662 | &mut attenuation); 663 | attenuation 664 | } 665 | } 666 | 667 | 668 | impl Drop for Music { 669 | /// Destroy all the resources of the Music. 670 | fn drop(&mut self) -> () { 671 | unsafe { 672 | al::alSourcei(self.al_source, ffi::AL_BUFFER, 0); 673 | ffi::alDeleteBuffers(2, &mut self.al_buffers[0]); 674 | ffi::alDeleteSources(1, &mut self.al_source); 675 | } 676 | } 677 | } 678 | 679 | #[cfg(test)] 680 | mod test { 681 | #![allow(non_snake_case)] 682 | 683 | use music::Music; 684 | use states::State::{Playing, Paused, Stopped}; 685 | use audio_controller::AudioController; 686 | 687 | #[test] 688 | fn music_create_OK() -> () { 689 | let msc = Music::new("res/shot.wav"); 690 | 691 | match msc { 692 | Some(_) => {}, 693 | None => panic!() 694 | } 695 | } 696 | 697 | #[test] 698 | fn music_create_FAIL() -> () { 699 | let msc = Music::new("toto.wav"); 700 | 701 | match msc { 702 | Some(_) => panic!(), 703 | None => {} 704 | } 705 | } 706 | 707 | #[test] 708 | #[ignore] 709 | fn music_play_OK() -> () { 710 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 711 | 712 | msc.play(); 713 | assert_eq!(msc.get_state() as i32, Playing as i32); 714 | msc.stop(); 715 | } 716 | 717 | #[test] 718 | #[ignore] 719 | fn music_pause_OK() -> () { 720 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 721 | 722 | msc.play(); 723 | msc.pause(); 724 | assert_eq!(msc.get_state() as i32, Paused as i32); 725 | msc.stop(); 726 | } 727 | 728 | #[test] 729 | fn music_stop_OK() -> () { 730 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 731 | 732 | msc.play(); 733 | msc.stop(); 734 | assert_eq!(msc.get_state() as i32, Stopped as i32); 735 | msc.stop(); 736 | } 737 | 738 | 739 | #[test] 740 | #[ignore] 741 | fn music_is_playing_TRUE() -> () { 742 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 743 | 744 | msc.play(); 745 | assert_eq!(msc.is_playing(), true); 746 | msc.stop(); 747 | } 748 | 749 | #[test] 750 | #[ignore] 751 | fn music_is_playing_FALSE() -> () { 752 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 753 | 754 | assert_eq!(msc.is_playing(), false); 755 | msc.stop(); 756 | } 757 | 758 | #[test] 759 | fn music_set_volume_OK() -> () { 760 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 761 | 762 | msc.set_volume(0.7); 763 | assert_eq!(msc.get_volume(), 0.7); 764 | } 765 | 766 | #[test] 767 | fn music_set_min_volume_OK() -> () { 768 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 769 | 770 | msc.set_min_volume(0.1); 771 | assert_eq!(msc.get_min_volume(), 0.1); 772 | } 773 | 774 | #[test] 775 | fn music_set_max_volume_OK() -> () { 776 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 777 | 778 | msc.set_max_volume(0.9); 779 | assert_eq!(msc.get_max_volume(), 0.9); 780 | } 781 | 782 | #[test] 783 | fn music_is_looping_TRUE() -> () { 784 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 785 | 786 | msc.set_looping(true); 787 | assert_eq!(msc.is_looping(), true); 788 | } 789 | 790 | #[test] 791 | fn music_is_looping_FALSE() -> () { 792 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 793 | 794 | msc.set_looping(false); 795 | assert_eq!(msc.is_looping(), false); 796 | } 797 | 798 | #[test] 799 | fn music_set_pitch_OK() -> () { 800 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 801 | 802 | msc.set_pitch(1.5); 803 | assert_eq!(msc.get_pitch(), 1.5); 804 | } 805 | 806 | #[test] 807 | fn music_set_relative_TRUE() -> () { 808 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 809 | 810 | msc.set_relative(true); 811 | assert_eq!(msc.is_relative(), true); 812 | } 813 | 814 | #[test] 815 | fn music_set_relative_FALSE() -> () { 816 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 817 | 818 | msc.set_relative(false); 819 | assert_eq!(msc.is_relative(), false); 820 | } 821 | 822 | // untill https://github.com/rust-lang/rust/issues/7622 is not fixed, slice comparsion is used 823 | 824 | #[test] 825 | fn music_set_position_OK() -> () { 826 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 827 | 828 | msc.set_position([50., 150., 250.]); 829 | let res = msc.get_position(); 830 | assert_eq!([res[0], res[1], res[2]][], [50f32, 150f32, 250f32][]); 831 | } 832 | 833 | #[test] 834 | fn music_set_direction_OK() -> () { 835 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 836 | 837 | msc.set_direction([50., 150., 250.]); 838 | let res = msc.get_direction(); 839 | assert_eq!([res[0], res[1], res[2]][], [50f32, 150f32, 250f32][]); 840 | } 841 | 842 | #[test] 843 | fn music_set_max_distance() -> () { 844 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 845 | 846 | msc.set_max_distance(70.); 847 | assert_eq!(msc.get_max_distance(), 70.); 848 | } 849 | 850 | #[test] 851 | fn music_set_reference_distance() -> () { 852 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 853 | 854 | msc.set_reference_distance(70.); 855 | assert_eq!(msc.get_reference_distance(), 70.); 856 | } 857 | 858 | #[test] 859 | fn music_set_attenuation() -> () { 860 | let mut msc = Music::new("res/shot.wav").expect("Cannot create Music"); 861 | 862 | msc.set_attenuation(0.5f32); 863 | println!("{}", &msc.get_attenuation()); 864 | assert_eq!(&msc.get_attenuation(), &0.5f32); 865 | } 866 | } 867 | -------------------------------------------------------------------------------- /src/openal.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | /*! 23 | * Minimal binding for OpenAL. 24 | * Bind only functions which are needed by lib sailor 25 | */ 26 | 27 | #![allow(dead_code, non_snake_case)] 28 | 29 | #[link(name = "openal")] 30 | extern {} 31 | 32 | pub mod ffi { 33 | 34 | use libc::{c_char, c_void}; 35 | 36 | /// OpenAL types 37 | pub type ALCboolean = c_char; 38 | pub const ALC_TRUE: ALCboolean = 1; 39 | pub const ALC_FALSE: ALCboolean = 0; 40 | 41 | /// Sound modifier 42 | pub const AL_GAIN: i32 = 0x100A; 43 | pub const AL_PITCH: i32 = 0x1003; 44 | pub const AL_SOURCE_RELATIVE: i32 = 0x202; 45 | pub const AL_POSITION: i32 = 0x1004; 46 | pub const AL_ORIENTATION: i32 = 0x100F; 47 | pub const AL_DIRECTION: i32 = 0x1005; 48 | pub const AL_LOOPING: i32 = 0x1007; 49 | pub const AL_MIN_GAIN: i32 = 0x100D; 50 | pub const AL_MAX_GAIN: i32 = 0x100E; 51 | pub const AL_MAX_DISTANCE: i32 = 0x1023; 52 | pub const AL_REFERENCE_DISTANCE: i32 = 0x1020; 53 | pub const AL_ROLLOFF_FACTOR: i32 = 0x1021; 54 | 55 | /// Sound format 56 | pub const AL_FORMAT_MONO16: i32 = 0x1101; 57 | pub const AL_FORMAT_STEREO16: i32 = 0x1103; 58 | pub const AL_FORMAT_51CHN16: i32 = 0x120B; 59 | pub const AL_FORMAT_61CHN16: i32 = 0x120E; 60 | pub const AL_FORMAT_71CHN16: i32 = 0x1211; 61 | pub const AL_FORMAT_QUAD16: i32 = 0x1205; 62 | 63 | /// Source params 64 | pub const AL_BUFFER: i32 = 0x1009; 65 | pub const AL_BUFFERS_PROCESSED: i32 = 0x1016; 66 | pub const AL_BUFFERS_QUEUED: i32 = 0x1015; 67 | 68 | /// Error identifiers 69 | pub const AL_NO_ERROR: i32 = 0; 70 | pub const AL_INVALID_NAME: i32 = 0xA001; 71 | pub const AL_INVALID_ENUM: i32 = 0xA002; 72 | pub const AL_INVALID_VALUE: i32 = 0xA003; 73 | pub const AL_INVALID_OPERATION: i32 = 0xA004; 74 | pub const AL_OUT_OF_MEMORY : i32 = 0xA005; 75 | 76 | /// Source states 77 | pub const AL_SOURCE_STATE: i32 = 0x1010; 78 | pub const AL_INITIAL: i32 = 0x1011; 79 | pub const AL_PLAYING: i32 = 0x1012; 80 | pub const AL_PAUSED: i32 = 0x1013; 81 | pub const AL_STOPPED: i32 = 0x1014; 82 | 83 | /// ALC 84 | pub const ALC_CAPTURE_SAMPLES : i32 = 0x312; 85 | 86 | 87 | extern "C" { 88 | /// Context functions 89 | pub fn alcCreateContext(device: *mut ALCdevice, attrlist: *mut i32) -> *mut ALCcontext; 90 | pub fn alcMakeContextCurrent(context: *mut ALCcontext) -> ALCboolean; 91 | pub fn alcDestroyContext(context: *mut ALCcontext); 92 | pub fn alcGetCurrentContext() -> *mut ALCcontext; 93 | 94 | /// Device functions 95 | pub fn alcOpenDevice(devicename: *mut c_char) -> *mut ALCdevice; 96 | pub fn alcCloseDevice(device: *mut ALCdevice) -> ALCboolean; 97 | 98 | /// Listener functions 99 | pub fn alListenerf(param: i32, value: f32) -> (); 100 | pub fn alListener3f(param: i32, value1: f32, value2: f32, value3: f32) -> (); 101 | pub fn alGetListenerf(param: i32, value: *mut f32) -> (); 102 | pub fn alGetListener3f(param: f32, value1: *mut f32, value2: *mut f32, value3: *mut f32) -> (); 103 | pub fn alListenerfv(param: i32, values: *const f32) -> (); 104 | pub fn alGetListenerfv(param: i32, values: *mut f32) -> (); 105 | 106 | /// Sources functions 107 | pub fn alGenSources(n: i32, sources: *mut u32) -> (); 108 | pub fn alDeleteSources(n: i32, buffers: *mut u32) -> (); 109 | pub fn alSourcei(source: u32, param: i32, value: i32) -> (); 110 | pub fn alSourcef(source: u32, param: i32, value: f32) -> (); 111 | pub fn alSourcePlay(source: u32) -> (); 112 | pub fn alSourcePause(source: u32) -> (); 113 | pub fn alSourceStop(source: u32) -> (); 114 | pub fn alGetSourcei(source: u32, param: i32, value: *mut i32) -> (); 115 | pub fn alGetSourcef(source: u32, param: i32, value: *mut f32) -> (); 116 | pub fn alSourcefv(source: u32, param: i32, value: *const f32) -> (); 117 | pub fn alGetSourcefv(source: u32, param: i32, value: *mut f32) -> (); 118 | pub fn alSourceQueueBuffers(source: u32, nb: i32, buffers: *const u32) -> (); 119 | pub fn alSourceUnqueueBuffers(source: u32, nb: i32, buffers: *mut u32) -> (); 120 | 121 | /// Sound capture functions 122 | pub fn alcCaptureCloseDevice(device: *mut ALCdevice) -> ALCboolean; 123 | pub fn alcCaptureOpenDevice(device: *mut c_char, sample_rate: i32, format: i32, buffer_size: i32) -> *mut ALCdevice; 124 | pub fn alcCaptureStart(devide: *mut ALCdevice); 125 | pub fn alcCaptureStop(devide: *mut ALCdevice); 126 | pub fn alcGetIntegerv(devide: *mut ALCdevice, param: i32, size: i32, values: *mut i32); 127 | pub fn alcCaptureSamples(devide: *mut ALCdevice, buffer: *mut c_void,sample: i32); 128 | 129 | /// extension check 130 | pub fn alcIsExtensionPresent(device: *mut ALCdevice, extension: *const c_char) -> ALCboolean; 131 | 132 | /// Buffers functions 133 | pub fn alGenBuffers(n: i32, buffers: *mut u32) -> (); 134 | pub fn alDeleteBuffers(n: i32, buffers: *mut u32); 135 | pub fn alBufferData(buffer: u32, format: i32, data: *mut c_void, size: i32, freq: i32) -> (); 136 | 137 | /// Error 138 | pub fn alGetError() -> i32; 139 | } 140 | 141 | #[repr(C)] 142 | pub struct ALCdevice; 143 | #[repr(C)] 144 | pub struct ALCcontext; 145 | } 146 | 147 | pub mod al { 148 | 149 | use super::ffi; 150 | use libc::c_void; 151 | 152 | pub fn alBufferData(buffer: u32, format: i32, data: *mut c_void, size: i32, freq: i32) -> () { 153 | unsafe { ffi::alBufferData(buffer, format, data, size, freq); } 154 | } 155 | 156 | pub fn alSourceQueueBuffers(source: u32, nb: i32, buffers: *const u32) -> () { 157 | unsafe { ffi::alSourceQueueBuffers(source, nb, buffers); } 158 | } 159 | 160 | pub fn alSourcePlay(source: u32) -> () { 161 | unsafe { ffi::alSourcePlay(source); } 162 | } 163 | 164 | pub fn alGetSourcei(source: u32, param: i32, value: *mut i32) -> () { 165 | unsafe { ffi::alGetSourcei(source, param, value); } 166 | } 167 | 168 | pub fn alGetSourcef(source: u32, param: i32, value: *mut f32) -> () { 169 | unsafe { ffi::alGetSourcef(source, param, value); } 170 | } 171 | 172 | pub fn alGetState(source: u32) -> i32 { 173 | let mut i = 0; 174 | unsafe { ffi::alGetSourcei(source, ffi::AL_SOURCE_STATE, &mut i); } 175 | i 176 | } 177 | 178 | pub fn alSourcei(source: u32, param: i32, value: i32) -> () { 179 | unsafe { ffi::alSourcei(source, param, value); } 180 | } 181 | 182 | pub fn alSourcef(source: u32, param: i32, value: f32) -> () { 183 | unsafe { ffi::alSourcef(source, param, value); } 184 | } 185 | 186 | pub fn alSourcePause(source: u32) -> () { 187 | unsafe { ffi::alSourcePause(source); } 188 | } 189 | 190 | pub fn alSourceStop(source: u32) -> () { 191 | unsafe { ffi::alSourceStop(source); } 192 | } 193 | 194 | pub fn alSourceUnqueueBuffers(source: u32, nb: i32, buffers: *mut u32) -> () { 195 | unsafe { ffi::alSourceUnqueueBuffers(source, nb, buffers); } 196 | } 197 | 198 | pub fn alGenSources(n: i32, sources: *mut u32) -> () { 199 | unsafe {ffi::alGenSources(n, sources); } 200 | } 201 | 202 | pub fn alSourcefv(source: u32, param: i32, value: *const f32) -> () { 203 | unsafe { ffi::alSourcefv(source, param, value); } 204 | } 205 | 206 | pub fn alGetSourcefv(source: u32, param: i32, value: *mut f32) -> () { 207 | unsafe { ffi::alGetSourcefv(source, param, value); } 208 | } 209 | 210 | pub fn alGenBuffers(n: i32, buffers: *mut u32) -> () { 211 | unsafe { ffi::alGenBuffers(n, buffers); } 212 | } 213 | 214 | pub fn alListenerf(param: i32, value: f32) -> () { 215 | unsafe { ffi::alListenerf(param, value); } 216 | } 217 | 218 | pub fn alListener3f(param: i32, value1: f32, value2: f32, value3: f32) -> () { 219 | unsafe { ffi::alListener3f(param, value1, value2, value3); } 220 | } 221 | 222 | pub fn alGetListenerf(param: i32, value: *mut f32) -> () { 223 | unsafe { ffi::alGetListenerf(param, value); } 224 | } 225 | 226 | pub fn alGetListener3f(param: f32, value1: *mut f32, value2: *mut f32, value3: *mut f32) -> () { 227 | unsafe { ffi::alGetListener3f(param, value1, value2, value3); } 228 | } 229 | 230 | pub fn alListenerfv(param: i32, values: *const f32) -> () { 231 | unsafe { ffi::alListenerfv(param, values); } 232 | } 233 | 234 | pub fn alGetListenerfv(param: i32, values: *mut f32) -> () { 235 | unsafe { ffi::alGetListenerfv(param, values); } 236 | } 237 | 238 | pub fn openal_has_error() -> Option { 239 | match unsafe { ffi::alGetError() } { 240 | ffi::AL_NO_ERROR => None, 241 | ffi::AL_INVALID_NAME => Some("OpenAL error : Invalid name paramater passed to AL call.".to_string()), 242 | ffi::AL_INVALID_ENUM => Some("OpenAL error : Invalid enum parameter passed to AL call.".to_string()), 243 | ffi::AL_INVALID_VALUE => Some("OpenAL error : Invalid value parameter passed to AL call.".to_string()), 244 | ffi::AL_INVALID_OPERATION => Some("OpenAL error : Illegal AL call.".to_string()), 245 | ffi::AL_OUT_OF_MEMORY => Some("OpenAL error : Not enough memory.".to_string()), 246 | _ => Some("OpenAL internal error : Unknow error.".to_string()) 247 | } 248 | } 249 | 250 | pub fn get_channels_format(channels : i32) -> Option { 251 | match channels { 252 | 1 => Some(ffi::AL_FORMAT_MONO16), 253 | 2 => Some(ffi::AL_FORMAT_STEREO16), 254 | 4 => Some(ffi::AL_FORMAT_QUAD16), 255 | 5 => Some(ffi::AL_FORMAT_51CHN16), 256 | 6 => Some(ffi::AL_FORMAT_61CHN16), 257 | 7 => Some(ffi::AL_FORMAT_71CHN16), 258 | _ => return None 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/record_context.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2014 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #![allow(raw_pointer_deriving)] 23 | 24 | use openal::ffi; 25 | 26 | /// The context needed to initialize a new Recorder 27 | #[deriving(Clone, PartialEq, Show, Copy)] 28 | pub struct RecordContext { 29 | capt_device: *mut ffi::ALCdevice 30 | } 31 | 32 | pub fn new(capt_device: *mut ffi::ALCdevice) -> RecordContext { 33 | RecordContext { 34 | capt_device: capt_device 35 | } 36 | } 37 | 38 | pub fn get(ctxt: RecordContext) -> *mut ffi::ALCdevice { 39 | ctxt.capt_device 40 | } 41 | -------------------------------------------------------------------------------- /src/recorder.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Record audio 23 | 24 | use std::{task, mem}; 25 | use std::vec::Vec; 26 | 27 | use record_context::RecordContext; 28 | use record_context; 29 | use openal::ffi; 30 | use sndfile::{SndInfo, SndFile}; 31 | use sndfile::OpenMode::Write; 32 | use sndfile::FormatType::{FormatWav, FormatPcm16}; 33 | 34 | /** 35 | * Record audio 36 | * 37 | * This class provide easy audio recording using. The Recorder allow the user 38 | * to record sound, then save it in a file, or create a SoundData object to play the 39 | * recorded sound in the same program. 40 | * A special context, RecordContext is needed to create the Recorder object. 41 | * The Recorder work in it's own task. 42 | * 43 | * # Examples 44 | * ```Rust 45 | * extern mod ears; 46 | * use ears::{RecordContext, Recorder}; 47 | * 48 | * fn main() -> () { 49 | * // Create a new context to record audio 50 | * let context = ears::init_in().unwrap(); 51 | * // Create the recorder 52 | * let recorder = Recorder::new(context); 53 | * // Start to record something 54 | * recorder.start(); 55 | * 56 | * // Do some other stuff here // 57 | * 58 | * // Stop the recorder 59 | * recorder.stop(); 60 | * // Then store the recorded data in a file 61 | * recorder.save_to_file("hello_file"); 62 | * } 63 | * ``` 64 | */ 65 | pub struct Recorder { 66 | ctxt: RecordContext, 67 | stop_sender: Option>, 68 | data_receiver: Option>>, 69 | samples: Vec 70 | } 71 | 72 | impl Recorder { 73 | /// Create a new audio recorder 74 | pub fn new(record_context: RecordContext) -> Recorder { 75 | Recorder { 76 | ctxt: record_context, 77 | stop_sender: None, 78 | data_receiver: None, 79 | samples: Vec::new() 80 | 81 | } 82 | } 83 | 84 | pub fn start(&mut self) { 85 | let (stop_sender, stop_receiver) = channel(); 86 | let (data_sender, data_receiver) = channel(); 87 | let r_c = self.ctxt.clone(); 88 | 89 | self.stop_sender = Some(stop_sender); 90 | self.data_receiver = Some(data_receiver); 91 | 92 | task::spawn(proc() { 93 | let mut terminate = false; 94 | let ctxt = record_context::get(r_c); 95 | unsafe { ffi::alcCaptureStart(ctxt); } 96 | let mut available_samples = 0; 97 | let mut samples: Vec = Vec::new(); 98 | 99 | while !terminate { 100 | unsafe { 101 | ffi::alcGetIntegerv(ctxt, 102 | ffi::ALC_CAPTURE_SAMPLES, 103 | 1, 104 | &mut available_samples) 105 | }; 106 | 107 | if available_samples != 0 { 108 | let tmp_buf = 109 | Vec::from_elem(available_samples as uint, 0i16); 110 | unsafe { 111 | ffi::alcCaptureSamples(ctxt, 112 | mem::transmute(&tmp_buf.as_slice()[0]), 113 | available_samples); 114 | } 115 | samples.extend(tmp_buf.into_iter()); 116 | } 117 | 118 | match stop_receiver.try_recv() { 119 | Ok(_) => { 120 | unsafe { ffi::alcCaptureStop(ctxt); } 121 | terminate = true; 122 | }, 123 | _ => {} 124 | } 125 | } 126 | data_sender.send(samples); 127 | }); 128 | } 129 | 130 | pub fn stop(&mut self) -> bool { 131 | match self.stop_sender { 132 | Some(ref s_c) => { 133 | s_c.send(true); 134 | match self.data_receiver { 135 | Some(ref d_p) => { 136 | self.samples = d_p.recv(); 137 | true 138 | }, 139 | None => false 140 | } 141 | }, 142 | None => false 143 | } 144 | } 145 | 146 | pub fn save_to_file(&mut self, filename: &str) -> bool { 147 | if self.samples.len() == 0 { 148 | false 149 | } else { 150 | let infos = box SndInfo { 151 | frames : self.samples.len() as i64, 152 | samplerate : 44100, 153 | channels : 1, 154 | format : (FormatPcm16 | FormatWav) as i32, 155 | sections : 0, 156 | seekable : 0 157 | }; 158 | 159 | let mut file_ext = String::from_str(filename); 160 | file_ext.push_str(".wav"); 161 | match SndFile::new_with_info(file_ext.as_slice(), Write, infos) { 162 | Ok(mut f) => { 163 | let len = self.samples.len() as i64; 164 | f.write_i16(self.samples.as_mut_slice(), len); 165 | f.close(); 166 | true 167 | }, 168 | Err(e) => { println!("{}", e); false } 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/sndfile.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | /*! 23 | * Libsndfile is a library designed to allow the reading and writing of many 24 | * different sampled sound file formats (such as MS Windows WAV and 25 | * the Apple/SGI AIFF format) through one standard library interface. 26 | * 27 | * During read and write operations, formats are seamlessly converted between the 28 | * format the application program has requested or supplied and the file's data 29 | * format. The application programmer can remain blissfully unaware of issues 30 | * such as file endian-ness and data format 31 | */ 32 | 33 | #![allow(dead_code)] 34 | 35 | use std::ptr; 36 | use std::c_str::CString; 37 | 38 | #[doc(hidden)] 39 | #[cfg(any(target_os="macos", target_os="linux", target_os="win32"))] 40 | mod libsndfile { 41 | #[link(name = "sndfile")] 42 | extern {} 43 | } 44 | 45 | #[doc(hidden)] 46 | #[path = "sndfile_ffi.rs"] 47 | mod ffi; 48 | 49 | /// The SndInfo structure is for passing data between the calling 50 | /// function and the library when opening a file for reading or writing. 51 | #[repr(C)] 52 | #[deriving(Clone)] 53 | pub struct SndInfo { 54 | pub frames : i64, 55 | pub samplerate : i32, 56 | pub channels : i32, 57 | pub format : i32, 58 | pub sections : i32, 59 | pub seekable : i32 60 | } 61 | 62 | /// Modes availables for the open function. 63 | /// 64 | /// * Read - Read only mode 65 | /// * Write - Write only mode 66 | /// * ReadWrite - Read and Write mode 67 | #[deriving(Copy)] 68 | pub enum OpenMode { 69 | Read = ffi::SFM_READ as int, 70 | Write = ffi::SFM_WRITE as int, 71 | ReadWrite = ffi::SFM_RDWR as int 72 | } 73 | 74 | /// Type of strings available for method get_string() 75 | #[deriving(Copy)] 76 | pub enum StringSoundType { 77 | Title = ffi::SF_STR_TITLE as int, 78 | Copyright = ffi::SF_STR_COPYRIGHT as int, 79 | Software = ffi::SF_STR_SOFTWARE as int, 80 | Artist = ffi::SF_STR_ARTIST as int, 81 | Comment = ffi::SF_STR_COMMENT as int, 82 | Date = ffi::SF_STR_DATE as int, 83 | Album = ffi::SF_STR_ALBUM as int, 84 | License = ffi::SF_STR_LICENSE as int, 85 | TrackNumber = ffi::SF_STR_TRACKNUMBER as int, 86 | Genre = ffi::SF_STR_GENRE as int 87 | } 88 | 89 | /// Types of error who can be return by API functions 90 | #[repr(C)] 91 | #[deriving(Copy)] 92 | pub enum Error { 93 | NoError = ffi::SF_ERR_NO_ERROR as int, 94 | UnrecognizedFormat = ffi::SF_ERR_UNRECOGNISED_FORMAT as int, 95 | SystemError = ffi::SF_ERR_SYSTEM as int, 96 | MalformedFile = ffi::SF_ERR_MALFORMED_FILE as int, 97 | UnsupportedEncoding = ffi::SF_ERR_UNSUPPORTED_ENCODING as int, 98 | } 99 | 100 | 101 | /// Enum to set the offset with method seek 102 | /// 103 | /// * SeekSet - The offset is set to the start of the audio data plus offset (multichannel) frames. 104 | /// * SeekCur - The offset is set to its current location plus offset (multichannel) frames. 105 | /// * SeekEnd - The offset is set to the end of the data plus offset (multichannel) frames. 106 | #[deriving(Copy)] 107 | pub enum SeekMode { 108 | SeekSet = ffi::SEEK_SET as int, 109 | SeekCur = ffi::SEEK_CUR as int, 110 | SeekEnd = ffi::SEEK_END as int 111 | } 112 | 113 | /// Enum who contains the list of the supported audio format 114 | /// 115 | /// * FormatWav - Microsoft WAV format (little endian) 116 | /// * FormatAiff - Apple/SGI AIFF format (big endian) 117 | /// * FormatAu - Sun/NeXT AU format (big endian) 118 | /// * FormatRaw - RAW PCM data 119 | /// * FormatPaf - Ensoniq PARIS file format 120 | /// * FormatSvx - Amiga IFF / SVX8 / SV16 format 121 | /// * FormatNist - Sphere NIST format 122 | /// * FormatVoc - VOC files 123 | /// * FormatIrcam - Berkeley/IRCAM/CARL 124 | /// * FormatW64 - Sonic Foundry's 64 bit RIFF/WAV 125 | /// * FormatMat4 - Matlab (tm) V4.2 / GNU Octave 2.0 126 | /// * FormatMat5 - Matlab (tm) V5.0 / GNU Octave 2.1 127 | /// * FormatPvf - Portable Voice Format 128 | /// * FormatXi - Fasttracker 2 Extended Instrument 129 | /// * FormatHtk - HMM Tool Kit format 130 | /// * FormatSds - Midi Sample Dump Standard 131 | /// * FormatAvr - Audio Visual Research 132 | /// * FormatWavex - MS WAVE with WAVEFORMATEX 133 | /// * FormatSd2 - Sound Designer 2 134 | /// * FormatFlac - FLAC lossless file format 135 | /// * FormatCaf - Core Audio File format 136 | /// * FormatWve - Psion WVE format 137 | /// * FormatOgg - Xiph OGG container 138 | /// * FormatMpc2k - Akai MPC 2000 sampler 139 | /// * FormatRf64 - RF64 WAV file 140 | /// * FormatPcmS8 - Signed 8 bit data 141 | /// * FormatPcm16 - Signed 16 bit data 142 | /// * FormatPcm24 - Signed 24 bit data 143 | /// * FormatPcm32 - Signed 32 bit data 144 | /// * FormatPcmU8 - Unsigned 8 bit data (WAV and RAW only) 145 | /// * FormatFloat - 32 bit float data 146 | /// * FormatDouble - 64 bit float data 147 | /// * FormatUlaw - U-Law encoded 148 | /// * FormatAlaw - A-Law encoded 149 | /// * FormatImaAdpcm - IMA ADPCM 150 | /// * FormatApcm - Microsoft ADPCM 151 | /// * FormatGsm610 - GSM 6.10 encoding 152 | /// * FormatVoxAdpcm - Oki Dialogic ADPCM encoding 153 | /// * FormatG72132 - 32kbs G721 ADPCM encoding 154 | /// * FormatG72324 - 24kbs G723 ADPCM encoding 155 | /// * FormatG72340 - 40kbs G723 ADPCM encoding 156 | /// * FormatDww12 - 12 bit Delta Width Variable Word encoding 157 | /// * FormatDww16 - 16 bit Delta Width Variable Word encoding 158 | /// * FormatDww24 - 24 bit Delta Width Variable Word encoding 159 | /// * FormatDwwN - N bit Delta Width Variable Word encoding 160 | /// * FormatDpcm8 - 8 bit differential PCM (XI only) 161 | /// * FormatDpcm16 - 16 bit differential PCM (XI only) 162 | /// * FormatVorbis - Xiph Vorbis encoding 163 | /// * EndianFile - Default file endian-ness 164 | /// * EndianLittle - Force little endian-ness 165 | /// * EndianBig - Force big endian-ness 166 | /// * EndianCpu - Force CPU endian-ness 167 | #[repr(C)] 168 | #[deriving(Show, Clone, PartialOrd, PartialEq, Copy)] 169 | pub enum FormatType { 170 | FormatWav = ffi::SF_FORMAT_WAV as int, 171 | FormatAiff = ffi::SF_FORMAT_AIFF as int, 172 | FormatAu = ffi::SF_FORMAT_AU as int, 173 | FormatRaw = ffi::SF_FORMAT_RAW as int, 174 | FormatPaf = ffi::SF_FORMAT_PAF as int, 175 | FormatSvx = ffi::SF_FORMAT_SVX as int, 176 | FormatNist = ffi::SF_FORMAT_NIST as int, 177 | FormatVoc = ffi::SF_FORMAT_VOC as int, 178 | FormatIrcam = ffi::SF_FORMAT_IRCAM as int, 179 | FormatW64 = ffi::SF_FORMAT_W64 as int, 180 | FormatMat4 = ffi::SF_FORMAT_MAT4 as int, 181 | FormatMat5 = ffi::SF_FORMAT_MAT5 as int, 182 | FormatPvf = ffi::SF_FORMAT_PVF as int, 183 | FormatXi = ffi::SF_FORMAT_XI as int, 184 | FormatHtk = ffi::SF_FORMAT_HTK as int, 185 | FormatSds = ffi::SF_FORMAT_SDS as int, 186 | FormatAvr = ffi::SF_FORMAT_AVR as int, 187 | FormatWavex = ffi::SF_FORMAT_WAVEX as int, 188 | FormatSd2 = ffi::SF_FORMAT_SD2 as int, 189 | FormatFlac = ffi::SF_FORMAT_FLAC as int, 190 | FormatCaf = ffi::SF_FORMAT_CAF as int, 191 | FormatWve = ffi::SF_FORMAT_WVE as int, 192 | FormatOgg = ffi::SF_FORMAT_OGG as int, 193 | FormatMpc2k = ffi::SF_FORMAT_MPC2K as int, 194 | FormatRf64 = ffi::SF_FORMAT_RF64 as int, 195 | FormatPcmS8 = ffi::SF_FORMAT_PCM_S8 as int, 196 | FormatPcm16 = ffi::SF_FORMAT_PCM_16 as int, 197 | FormatPcm24 = ffi::SF_FORMAT_PCM_24 as int, 198 | FormatPcm32 = ffi::SF_FORMAT_PCM_32 as int, 199 | FormatPcmU8 = ffi::SF_FORMAT_PCM_U8 as int, 200 | FormatFloat = ffi::SF_FORMAT_FLOAT as int, 201 | FormatDouble = ffi::SF_FORMAT_DOUBLE as int, 202 | FormatUlaw = ffi::SF_FORMAT_ULAW as int, 203 | FormatAlaw = ffi::SF_FORMAT_ALAW as int, 204 | FormatImaAdpcm = ffi::SF_FORMAT_IMA_ADPCM as int, 205 | FormatApcm = ffi::SF_FORMAT_MS_ADPCM as int, 206 | FormatGsm610 = ffi::SF_FORMAT_GSM610 as int, 207 | FormatVoxAdpcm = ffi::SF_FORMAT_VOX_ADPCM as int, 208 | FormatG72132 = ffi::SF_FORMAT_G721_32 as int, 209 | FormatG72324 = ffi::SF_FORMAT_G723_24 as int, 210 | FormatG72340 = ffi::SF_FORMAT_G723_40 as int, 211 | FormatDww12 = ffi::SF_FORMAT_DWVW_12 as int, 212 | FormatDww16 = ffi::SF_FORMAT_DWVW_16 as int, 213 | FormatDww24 = ffi::SF_FORMAT_DWVW_24 as int, 214 | FormatDwwN = ffi::SF_FORMAT_DWVW_N as int, 215 | FormatDpcm8 = ffi::SF_FORMAT_DPCM_8 as int, 216 | FormatDpcm16 = ffi::SF_FORMAT_DPCM_16 as int, 217 | FormatVorbis = ffi::SF_FORMAT_VORBIS as int, 218 | EndianFile = ffi::SF_ENDIAN_FILE as int, 219 | EndianLittle = ffi::SF_ENDIAN_LITTLE as int, 220 | EndianBig = ffi::SF_ENDIAN_BIG as int, 221 | EndianCpu = ffi::SF_ENDIAN_CPU as int, 222 | FormatSubMask = ffi::SF_FORMAT_SUBMASK as int, 223 | FormatTypeMask = ffi::SF_FORMAT_TYPEMASK as int, 224 | } 225 | 226 | impl BitOr for FormatType { 227 | fn bitor(&self, _rhs: &FormatType) -> int { 228 | (*self as int) | (*_rhs as int) 229 | } 230 | } 231 | 232 | /// SndFile object, used to load/store sound from a file path or an fd. 233 | pub struct SndFile { 234 | handle : *mut ffi::SNDFILE, 235 | info : Box 236 | } 237 | 238 | impl Clone for SndFile { 239 | fn clone(&self) -> SndFile { 240 | SndFile { 241 | handle : self.handle, 242 | info : self.info.clone() 243 | } 244 | } 245 | } 246 | 247 | impl SndFile { 248 | /** 249 | * Construct SndFile object with the path to the music and a mode to open it. 250 | * 251 | * # Arguments 252 | * * path - The path to load the music 253 | * * mode - The mode to open the music 254 | * 255 | * Return Ok() containing the SndFile on success, a string representation of 256 | * the error otherwise. 257 | */ 258 | pub fn new(path : &str, mode : OpenMode) -> Result { 259 | let mut info = box SndInfo { 260 | frames : 0, 261 | samplerate : 0, 262 | channels : 0, 263 | format : 0, 264 | sections : 0, 265 | seekable : 0 266 | }; 267 | let tmp_sndfile = path.with_c_str(|c_path| { 268 | unsafe {ffi::sf_open(c_path as *mut i8, mode as i32, &mut *info) } 269 | }); 270 | if tmp_sndfile.is_null() { 271 | Err(unsafe { 272 | CString::new(ffi::sf_strerror(ptr::null_mut()) as *const i8, false).as_str().unwrap().to_string() 273 | }) 274 | } else { 275 | Ok(SndFile { 276 | handle : tmp_sndfile, 277 | info : info 278 | }) 279 | } 280 | } 281 | 282 | /** 283 | * Construct SndFile object with the path to the music and a mode to open it. 284 | * 285 | * # Arguments 286 | * * path - The path to load the music 287 | * * mode - The mode to open the music 288 | * * info - The SndInfo to pass to the file 289 | * 290 | * Return Ok() containing the SndFile on success, a string representation of 291 | * the error otherwise. 292 | */ 293 | pub fn new_with_info(path : &str, mode : OpenMode, mut info: Box) -> Result { 294 | let tmp_sndfile = path.with_c_str(|c_path| { 295 | unsafe {ffi::sf_open(c_path as *mut i8, mode as i32, &mut *info) } 296 | }); 297 | if tmp_sndfile.is_null() { 298 | Err(unsafe { 299 | CString::new(ffi::sf_strerror(ptr::null_mut()) as *const i8, false).as_str().unwrap().to_string() 300 | }) 301 | } else { 302 | Ok(SndFile { 303 | handle : tmp_sndfile, 304 | info : info 305 | }) 306 | } 307 | } 308 | 309 | /** 310 | * Construct SndFile object with the fd of the file containing the music 311 | * and a mode to open it. 312 | * 313 | * # Arguments 314 | * * fd - The fd to load the music 315 | * * mode - The mode to open the music 316 | * * close_desc - Should SndFile close the fd at exit? 317 | * 318 | * Return Ok() containing the SndFile on success, a string representation 319 | * of the error otherwise. 320 | */ 321 | pub fn new_with_fd(fd : i32, 322 | mode : OpenMode, 323 | close_desc : bool) 324 | -> Result { 325 | let mut info = box SndInfo { 326 | frames : 0, 327 | samplerate : 0, 328 | channels : 0, 329 | format : 0, 330 | sections : 0, 331 | seekable : 0 332 | }; 333 | let tmp_sndfile = match close_desc { 334 | true => unsafe { 335 | ffi::sf_open_fd(fd, mode as i32, &mut *info, ffi::SF_TRUE) 336 | }, 337 | false => unsafe { 338 | ffi::sf_open_fd(fd, mode as i32, &mut *info, ffi::SF_FALSE) 339 | } 340 | }; 341 | if tmp_sndfile.is_null() { 342 | Err(unsafe { 343 | CString::new(ffi::sf_strerror(ptr::null_mut()) as *const i8, false).as_str().unwrap().to_string() 344 | }) 345 | } else { 346 | Ok(SndFile { 347 | handle : tmp_sndfile, 348 | info : info 349 | }) 350 | } 351 | } 352 | 353 | /// Return the SndInfo struct of the current music. 354 | pub fn get_sndinfo(&self) -> SndInfo { 355 | *self.info.clone() 356 | } 357 | 358 | /** 359 | * Retrieve a tag contained by the music. 360 | * 361 | * # Argument 362 | * * string_type - The type of the tag to retrieve 363 | * 364 | * Return Some(String) if the tag is found, None otherwise. 365 | */ 366 | pub fn get_string(&self, string_type : StringSoundType) -> Option { 367 | let c_string = unsafe { 368 | ffi::sf_get_string(self.handle, string_type as i32) 369 | }; 370 | if c_string.is_null() { 371 | None 372 | } else { 373 | Some(unsafe { 374 | CString::new(c_string as *const i8, false).as_str().unwrap().to_string() 375 | }) 376 | } 377 | } 378 | 379 | /** 380 | * Set a tag on the music file. 381 | * 382 | * # Arguments 383 | * * string_type - The type of the tag to set 384 | * * string - The string to set. 385 | * 386 | * Return NoError on success, an other error code otherwise 387 | */ 388 | pub fn set_string(&mut self, 389 | string_type : StringSoundType, 390 | string : String) -> Error { 391 | unsafe { 392 | ffi::sf_set_string(self.handle, 393 | string_type as i32, 394 | string.to_c_str().into_inner() as *mut i8) 395 | } 396 | } 397 | 398 | /** 399 | * Check if the format of the SndInfo struct is valid. 400 | * 401 | * # Argument 402 | * * info - The SndInfo struct to test 403 | * 404 | * Return true if the struct is valid, false otherwise. 405 | */ 406 | pub fn check_format<'r>(info : &'r mut SndInfo) -> bool { 407 | match unsafe {ffi::sf_format_check(info) } { 408 | ffi::SF_TRUE => true, 409 | ffi::SF_FALSE => false, 410 | _ => unreachable!() 411 | } 412 | } 413 | 414 | 415 | /** 416 | * Close the SndFile object. 417 | * 418 | * This function must be called before the exist of the program to destroy 419 | * all the resources. 420 | * 421 | * Return NoError if destruction success, an other error code otherwise. 422 | */ 423 | pub fn close(&self) -> Error { 424 | unsafe { 425 | ffi::sf_close(self.handle) 426 | } 427 | } 428 | 429 | /** 430 | * If the file is opened Write or ReadWrite, call the operating system's 431 | * function to force the writing of all file cache buffers to disk. 432 | * If the file is opened Read no action is taken. 433 | */ 434 | pub fn write_sync(&mut self) -> () { 435 | unsafe { 436 | ffi::sf_write_sync(self.handle) 437 | } 438 | } 439 | 440 | pub fn seek(&mut self, frames : i64, whence : SeekMode) -> i64{ 441 | unsafe { 442 | ffi::sf_seek(self.handle, frames, whence as i32) 443 | } 444 | } 445 | 446 | /** 447 | * Read items of type i16 448 | * 449 | * # Arguments 450 | * * array - The array to fill with the items. 451 | * * items - The max capacity of the array. 452 | * 453 | * Return the count of items. 454 | */ 455 | pub fn read_i16<'r>(&'r mut self, array : &'r mut [i16], items : i64) -> i64 { 456 | unsafe { 457 | ffi::sf_read_short(self.handle, array.as_mut_ptr(), items) 458 | } 459 | } 460 | 461 | /** 462 | * Read items of type i32 463 | * 464 | * # Arguments 465 | * * array - The array to fill with the items. 466 | * * items - The max capacity of the array. 467 | * 468 | * Return the count of items. 469 | */ 470 | pub fn read_i32<'r>(&'r mut self, array : &'r mut [i32], items : i64) -> i64 { 471 | unsafe { 472 | ffi::sf_read_int(self.handle, array.as_mut_ptr(), items) 473 | } 474 | } 475 | 476 | /** 477 | * Read items of type f32 478 | * 479 | * # Arguments 480 | * * array - The array to fill with the items. 481 | * * items - The max capacity of the array. 482 | * 483 | * Return the count of items. 484 | */ 485 | pub fn read_f32<'r>(&'r mut self, array : &'r mut [f32], items : i64) -> i64 { 486 | unsafe { 487 | ffi::sf_read_float(self.handle, array.as_mut_ptr(), items) 488 | } 489 | } 490 | 491 | /** 492 | * Read items of type f64 493 | * 494 | * # Arguments 495 | * * array - The array to fill with the items. 496 | * * items - The max capacity of the array. 497 | * 498 | * Return the count of items. 499 | */ 500 | pub fn read_f64<'r>(&'r mut self, array : &'r mut [f64], items : i64) -> i64 { 501 | unsafe { 502 | ffi::sf_read_double(self.handle, array.as_mut_ptr(), items) 503 | } 504 | } 505 | 506 | /** 507 | * Read frames of type i16 508 | * 509 | * # Arguments 510 | * * array - The array to fill with the frames. 511 | * * items - The max capacity of the array. 512 | * 513 | * Return the count of frames. 514 | */ 515 | pub fn readf_i16<'r>(&'r mut self, array : &'r mut [i16], frames : i64) -> i64 { 516 | unsafe { 517 | ffi::sf_readf_short(self.handle, array.as_mut_ptr(), frames) 518 | } 519 | } 520 | 521 | /** 522 | * Read frames of type i32 523 | * 524 | * # Arguments 525 | * * array - The array to fill with the frames. 526 | * * items - The max capacity of the array. 527 | * 528 | * Return the count of frames. 529 | */ 530 | pub fn readf_i32<'r>(&'r mut self, array : &'r mut [i32], frames : i64) -> i64 { 531 | unsafe { 532 | ffi::sf_readf_int(self.handle, array.as_mut_ptr(), frames) 533 | } 534 | } 535 | 536 | /** 537 | * Read frames of type f32 538 | * 539 | * # Arguments 540 | * * array - The array to fill with the frames. 541 | * * items - The max capacity of the array. 542 | * 543 | * Return the count of frames. 544 | */ 545 | pub fn readf_f32<'r>(&'r mut self, array : &'r mut [f32], frames : i64) -> i64 { 546 | unsafe { 547 | ffi::sf_readf_float(self.handle, array.as_mut_ptr(), frames) 548 | } 549 | } 550 | 551 | /** 552 | * Read frames of type f64 553 | * 554 | * # Arguments 555 | * * array - The array to fill with the frames. 556 | * * items - The max capacity of the array. 557 | * 558 | * Return the count of frames. 559 | */ 560 | pub fn readf_f64<'r>(&'r mut self, array : &'r mut [f64], frames : i64) -> i64 { 561 | unsafe { 562 | ffi::sf_readf_double(self.handle, array.as_mut_ptr(), frames) 563 | } 564 | } 565 | 566 | /** 567 | * Write items of type i16 568 | * 569 | * # Arguments 570 | * * array - The array of items to write. 571 | * * items - The number of items to write. 572 | * 573 | * Return the count of wrote items. 574 | */ 575 | pub fn write_i16<'r>(&'r mut self, array : &'r mut [i16], items : i64) -> i64 { 576 | unsafe { 577 | ffi::sf_write_short(self.handle, array.as_mut_ptr(), items) 578 | } 579 | } 580 | 581 | /** 582 | * Write items of type i32 583 | * 584 | * # Arguments 585 | * * array - The array of items to write. 586 | * * items - The number of items to write. 587 | * 588 | * Return the count of wrote items. 589 | */ 590 | pub fn write_i32<'r>(&'r mut self, array : &'r mut [i32], items : i64) -> i64 { 591 | unsafe { 592 | ffi::sf_write_int(self.handle, array.as_mut_ptr(), items) 593 | } 594 | } 595 | 596 | /** 597 | * Write items of type f32 598 | * 599 | * # Arguments 600 | * * array - The array of items to write. 601 | * * items - The number of items to write. 602 | * 603 | * Return the count of wrote items. 604 | */ 605 | pub fn write_f32<'r>(&'r mut self, array : &'r mut [f32], items : i64) -> i64 { 606 | unsafe { 607 | ffi::sf_write_float(self.handle, array.as_mut_ptr(), items) 608 | } 609 | } 610 | 611 | /** 612 | * Write items of type f64 613 | * 614 | * # Arguments 615 | * * array - The array of items to write. 616 | * * items - The number of items to write. 617 | * 618 | * Return the count of wrote items. 619 | */ 620 | pub fn write_f64<'r>(&'r mut self, array : &'r mut [f64], items : i64) -> i64 { 621 | unsafe { 622 | ffi::sf_write_double(self.handle, array.as_mut_ptr(), items) 623 | } 624 | } 625 | 626 | /** 627 | * Write frames of type i16 628 | * 629 | * # Arguments 630 | * * array - The array of frames to write. 631 | * * items - The number of frames to write. 632 | * 633 | * Return the count of wrote frames. 634 | */ 635 | pub fn writef_i16<'r>(&'r mut self, array : &'r mut [i16], frames : i64) -> i64 { 636 | unsafe { 637 | ffi::sf_writef_short(self.handle, array.as_mut_ptr(), frames) 638 | } 639 | } 640 | 641 | /** 642 | * Write frames of type i32 643 | * 644 | * # Arguments 645 | * * array - The array of frames to write. 646 | * * items - The number of frames to write. 647 | * 648 | * Return the count of wrote frames. 649 | */ 650 | pub fn writef_i32<'r>(&'r mut self, array : &'r mut [i32], frames : i64) -> i64 { 651 | unsafe { 652 | ffi::sf_writef_int(self.handle, array.as_mut_ptr(), frames) 653 | } 654 | } 655 | 656 | /** 657 | * Write frames of type f32 658 | * 659 | * # Arguments 660 | * * array - The array of frames to write. 661 | * * items - The number of frames to write. 662 | * 663 | * Return the count of wrote frames. 664 | */ 665 | pub fn writef_f32<'r>(&'r mut self, array : &'r mut [f32], frames : i64) -> i64 { 666 | unsafe { 667 | ffi::sf_writef_float(self.handle, array.as_mut_ptr(), frames) 668 | } 669 | } 670 | 671 | /** 672 | * Write frames of type f64 673 | * 674 | * # Arguments 675 | * * array - The array of frames to write. 676 | * * items - The number of frames to write. 677 | * 678 | * Return the count of wrote frames. 679 | */ 680 | pub fn writef_f64<'r>(&'r mut self, array : &'r mut [f64], frames : i64) -> i64 { 681 | unsafe { 682 | ffi::sf_writef_double(self.handle, array.as_mut_ptr(), frames) 683 | } 684 | } 685 | 686 | /** 687 | * Get the last error 688 | * 689 | * Return the last error as a variant of the enum Error. 690 | */ 691 | pub fn error(&self) -> Error { 692 | unsafe { 693 | ffi::sf_error(self.handle) 694 | } 695 | } 696 | 697 | /** 698 | * Get the last error as a string 699 | * 700 | * Return an owned str containing the last error. 701 | */ 702 | pub fn string_error(&self) -> String { 703 | unsafe { 704 | CString::new(ffi::sf_strerror(self.handle) as *const i8, false).as_str().unwrap().to_string() 705 | } 706 | } 707 | 708 | /** 709 | * Get an error as a string from a variant of enum Error 710 | * 711 | * Return an owned str containing the error. 712 | */ 713 | pub fn error_number(error_num : Error) -> String { 714 | unsafe { 715 | CString::new(ffi::sf_error_number(error_num as i32) as *const i8, false).as_str().unwrap().to_string() 716 | } 717 | } 718 | 719 | } 720 | 721 | -------------------------------------------------------------------------------- /src/sndfile_ffi.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | #![allow(dead_code, non_camel_case_types)] 23 | 24 | use super::{SndInfo, Error}; 25 | use libc::{c_char, c_void}; 26 | 27 | pub type SF_MODE = i32; 28 | pub const SFM_READ : SF_MODE = 0x10; 29 | pub const SFM_WRITE : SF_MODE = 0x20; 30 | pub const SFM_RDWR : SF_MODE = 0x30; 31 | 32 | pub type SF_ERR = i32; 33 | pub const SF_ERR_NO_ERROR : SF_ERR = 0; 34 | pub const SF_ERR_UNRECOGNISED_FORMAT : SF_ERR = 1; 35 | pub const SF_ERR_SYSTEM : SF_ERR = 2; 36 | pub const SF_ERR_MALFORMED_FILE : SF_ERR = 3; 37 | pub const SF_ERR_UNSUPPORTED_ENCODING : SF_ERR = 4; 38 | 39 | pub type SF_STR = i32; 40 | pub const SF_STR_TITLE : SF_STR = 0x01; 41 | pub const SF_STR_COPYRIGHT : SF_STR = 0x02; 42 | pub const SF_STR_SOFTWARE : SF_STR = 0x03; 43 | pub const SF_STR_ARTIST : SF_STR = 0x04; 44 | pub const SF_STR_COMMENT : SF_STR = 0x05; 45 | pub const SF_STR_DATE : SF_STR = 0x06; 46 | pub const SF_STR_ALBUM : SF_STR = 0x07; 47 | pub const SF_STR_LICENSE : SF_STR = 0x08; 48 | pub const SF_STR_TRACKNUMBER : SF_STR = 0x09; 49 | pub const SF_STR_GENRE : SF_STR = 0x10; 50 | 51 | pub type SF_BOOL = i32; 52 | pub const SF_FALSE : SF_BOOL = 0; 53 | pub const SF_TRUE : SF_BOOL = 1; 54 | 55 | pub type SEEK_MODE = i32; 56 | pub const SEEK_SET : SEEK_MODE = 0; 57 | pub const SEEK_CUR : SEEK_MODE = 1; 58 | pub const SEEK_END : SEEK_MODE = 2; 59 | 60 | pub type FORMAT_TYPE = i32; 61 | pub const SF_FORMAT_WAV : FORMAT_TYPE = 0x010000; /// Microsoft WAV format (little endian) 62 | pub const SF_FORMAT_AIFF : FORMAT_TYPE = 0x020000; /// Apple/SGI AIFF format (big endian) 63 | pub const SF_FORMAT_AU : FORMAT_TYPE = 0x030000; /// Sun/NeXT AU format (big endian) 64 | pub const SF_FORMAT_RAW : FORMAT_TYPE = 0x040000; /// RAW PCM data 65 | pub const SF_FORMAT_PAF : FORMAT_TYPE = 0x050000; /// Ensoniq PARIS file format 66 | pub const SF_FORMAT_SVX : FORMAT_TYPE = 0x060000; /// Amiga IFF / SVX8 / SV16 format 67 | pub const SF_FORMAT_NIST : FORMAT_TYPE = 0x070000; /// Sphere NIST format 68 | pub const SF_FORMAT_VOC : FORMAT_TYPE = 0x080000; /// VOC files 69 | pub const SF_FORMAT_IRCAM : FORMAT_TYPE = 0x0A0000; /// Berkeley/IRCAM/CARL 70 | pub const SF_FORMAT_W64 : FORMAT_TYPE = 0x0B0000; /// Sonic Foundry's 64 bit RIFF/WAV 71 | pub const SF_FORMAT_MAT4 : FORMAT_TYPE = 0x0C0000; /// Matlab (tm) V4.2 / GNU Octave 2.0 72 | pub const SF_FORMAT_MAT5 : FORMAT_TYPE = 0x0D0000; /// Matlab (tm) V5.0 / GNU Octave 2.1 73 | pub const SF_FORMAT_PVF : FORMAT_TYPE = 0x0E0000; /// Portable Voice Format 74 | pub const SF_FORMAT_XI : FORMAT_TYPE = 0x0F0000; /// Fasttracker 2 Extended Instrument 75 | pub const SF_FORMAT_HTK : FORMAT_TYPE = 0x100000; /// HMM Tool Kit format 76 | pub const SF_FORMAT_SDS : FORMAT_TYPE = 0x110000; /// Midi Sample Dump Standard 77 | pub const SF_FORMAT_AVR : FORMAT_TYPE = 0x120000; /// Audio Visual Research 78 | pub const SF_FORMAT_WAVEX : FORMAT_TYPE = 0x130000; /// MS WAVE with WAVEFORMATEX 79 | pub const SF_FORMAT_SD2 : FORMAT_TYPE = 0x160000; /// Sound Designer 2 80 | pub const SF_FORMAT_FLAC : FORMAT_TYPE = 0x170000; /// FLAC lossless file format 81 | pub const SF_FORMAT_CAF : FORMAT_TYPE = 0x180000; /// Core Audio File format 82 | pub const SF_FORMAT_WVE : FORMAT_TYPE = 0x190000; /// Psion WVE format 83 | pub const SF_FORMAT_OGG : FORMAT_TYPE = 0x200000; /// Xiph OGG container 84 | pub const SF_FORMAT_MPC2K : FORMAT_TYPE = 0x210000; /// Akai MPC 2000 sampler 85 | pub const SF_FORMAT_RF64 : FORMAT_TYPE = 0x220000; /// RF64 WAV file 86 | /* Subtypes from here on. */ 87 | pub const SF_FORMAT_PCM_S8 : FORMAT_TYPE = 0x0001; /// Signed 8 bit data 88 | pub const SF_FORMAT_PCM_16 : FORMAT_TYPE = 0x0002; /// Signed 16 bit data 89 | pub const SF_FORMAT_PCM_24 : FORMAT_TYPE = 0x0003; /// Signed 24 bit data 90 | pub const SF_FORMAT_PCM_32 : FORMAT_TYPE = 0x0004; /// Signed 32 bit data 91 | pub const SF_FORMAT_PCM_U8 : FORMAT_TYPE = 0x0005; /// Unsigned 8 bit data (WAV and RAW only) 92 | pub const SF_FORMAT_FLOAT : FORMAT_TYPE = 0x0006; /// 32 bit float data 93 | pub const SF_FORMAT_DOUBLE : FORMAT_TYPE = 0x0007; /// 64 bit float data 94 | pub const SF_FORMAT_ULAW : FORMAT_TYPE = 0x0010; /// U-Law encoded 95 | pub const SF_FORMAT_ALAW : FORMAT_TYPE = 0x0011; /// A-Law encoded 96 | pub const SF_FORMAT_IMA_ADPCM : FORMAT_TYPE = 0x0012; /// IMA ADPCM 97 | pub const SF_FORMAT_MS_ADPCM : FORMAT_TYPE = 0x0013; /// Microsoft ADPCM 98 | pub const SF_FORMAT_GSM610 : FORMAT_TYPE = 0x0020; /// GSM 6.10 encoding 99 | pub const SF_FORMAT_VOX_ADPCM : FORMAT_TYPE = 0x0021; /// Oki Dialogic ADPCM encoding 100 | pub const SF_FORMAT_G721_32 : FORMAT_TYPE = 0x0030; /// 32kbs G721 ADPCM encoding 101 | pub const SF_FORMAT_G723_24 : FORMAT_TYPE = 0x0031; /// 24kbs G723 ADPCM encoding 102 | pub const SF_FORMAT_G723_40 : FORMAT_TYPE = 0x0032; /// 40kbs G723 ADPCM encoding 103 | pub const SF_FORMAT_DWVW_12 : FORMAT_TYPE = 0x0040; /// 12 bit Delta Width Variable Word encoding 104 | pub const SF_FORMAT_DWVW_16 : FORMAT_TYPE = 0x0041; /// 16 bit Delta Width Variable Word encoding 105 | pub const SF_FORMAT_DWVW_24 : FORMAT_TYPE = 0x0042; /// 24 bit Delta Width Variable Word encoding 106 | pub const SF_FORMAT_DWVW_N : FORMAT_TYPE = 0x0043; /// N bit Delta Width Variable Word encoding 107 | pub const SF_FORMAT_DPCM_8 : FORMAT_TYPE = 0x0050; /// 8 bit differential PCM (XI only) 108 | pub const SF_FORMAT_DPCM_16 : FORMAT_TYPE = 0x0051; /// 16 bit differential PCM (XI only) 109 | pub const SF_FORMAT_VORBIS : FORMAT_TYPE = 0x0060; /// Xiph Vorbis encoding 110 | 111 | /* Endian-ness options. */ 112 | 113 | pub const SF_ENDIAN_FILE : FORMAT_TYPE = 0x00000000; /// Default file endian-ness 114 | pub const SF_ENDIAN_LITTLE : FORMAT_TYPE = 0x10000000; /// Force little endian-ness 115 | pub const SF_ENDIAN_BIG : FORMAT_TYPE = 0x20000000; /// Force big endian-ness 116 | pub const SF_ENDIAN_CPU : FORMAT_TYPE = 0x30000000; /// Force CPU endian-ness 117 | 118 | pub const SF_FORMAT_SUBMASK : FORMAT_TYPE = 0x0000FFFF; 119 | pub const SF_FORMAT_TYPEMASK : FORMAT_TYPE = 0x0FFF0000; 120 | pub const SF_FORMAT_ENDMASK : FORMAT_TYPE = 0x30000000; 121 | 122 | pub type SNDFILE = c_void; 123 | 124 | #[repr(C)] 125 | pub struct FormatInfo { 126 | pub format : i32, 127 | pub name : *mut c_char, 128 | pub extension : *mut c_char 129 | } 130 | 131 | extern "C" { 132 | pub fn sf_open(path : *mut c_char, mode : SF_MODE, info : *mut SndInfo) -> *mut SNDFILE; 133 | pub fn sf_open_fd(fd : i32, mode : SF_MODE, info : *mut SndInfo, close_desc : SF_BOOL) -> *mut SNDFILE; 134 | pub fn sf_format_check(info : *mut SndInfo) -> SF_BOOL; 135 | 136 | pub fn sf_seek(sndfile : *mut SNDFILE, frames : i64, whence : i32) -> i64; 137 | pub fn sf_command(sndfile : *mut SNDFILE, cmd : i32, data : *mut c_void, datasize : i32) -> Error; 138 | 139 | pub fn sf_error(sndfile : *mut SNDFILE) -> Error; 140 | pub fn sf_strerror(sndfile : *mut SNDFILE) -> *mut c_char; 141 | pub fn sf_error_number(errnum : i32) -> *mut c_char; 142 | 143 | pub fn sf_perror(sndfile : *mut SNDFILE) -> Error; 144 | pub fn sf_error_str(sndfile : *mut SNDFILE, string : *mut c_char, len : i64) ; 145 | 146 | pub fn sf_close(sndfile : *mut SNDFILE) -> Error; 147 | pub fn sf_write_sync(sndfile : *mut SNDFILE) -> (); 148 | 149 | pub fn sf_read_short(sndfile : *mut SNDFILE, ptr : *mut i16, items : i64) -> i64; 150 | pub fn sf_read_int(sndfile : *mut SNDFILE, ptr : *mut i32, items : i64) -> i64; 151 | pub fn sf_read_float(sndfile : *mut SNDFILE, ptr : *mut f32, items : i64) -> i64; 152 | pub fn sf_read_double(sndfile : *mut SNDFILE, ptr : *mut f64, items : i64) -> i64; 153 | 154 | pub fn sf_readf_short(sndfile : *mut SNDFILE, ptr : *mut i16, frames : i64) -> i64; 155 | pub fn sf_readf_int(sndfile : *mut SNDFILE, ptr : *mut i32, frames : i64) -> i64; 156 | pub fn sf_readf_float(sndfile : *mut SNDFILE, ptr : *mut f32, frames : i64) -> i64; 157 | pub fn sf_readf_double(sndfile : *mut SNDFILE, ptr : *mut f64, frames : i64) -> i64; 158 | 159 | pub fn sf_write_short(sndfile : *mut SNDFILE, ptr : *mut i16, items : i64) -> i64; 160 | pub fn sf_write_int(sndfile : *mut SNDFILE, ptr : *mut i32, items : i64) -> i64; 161 | pub fn sf_write_float(sndfile : *mut SNDFILE, ptr : *mut f32, items : i64) -> i64; 162 | pub fn sf_write_double(sndfile : *mut SNDFILE, ptr : *mut f64, items : i64) -> i64; 163 | 164 | pub fn sf_writef_short(sndfile : *mut SNDFILE, ptr : *mut i16, frames : i64) -> i64; 165 | pub fn sf_writef_int(sndfile : *mut SNDFILE, ptr : *mut i32, frames : i64) -> i64; 166 | pub fn sf_writef_float(sndfile : *mut SNDFILE, ptr : *mut f32, frames : i64) -> i64; 167 | pub fn sf_writef_double(sndfile : *mut SNDFILE, ptr : *mut f64, frames : i64) -> i64; 168 | 169 | pub fn sf_read_raw(sndfile : *mut SNDFILE, ptr : *mut c_void, bytes : i64) -> i64; 170 | pub fn sf_write_raw(sndfile : *mut SNDFILE, ptr : *mut c_void, bytes : i64) -> i64; 171 | 172 | pub fn sf_get_string(sndfile : *mut SNDFILE, str_type : i32) -> *mut c_char; 173 | pub fn sf_set_string(sndfile : *mut SNDFILE, str_type : i32, string : *mut c_char) -> Error; 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/sound.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Play Sounds easily. 23 | 24 | use std::rc::Rc; 25 | use std::cell::RefCell; 26 | 27 | use internal::OpenAlData; 28 | use sound_data::{mod, SoundData}; 29 | use openal::{ffi, al}; 30 | use states::State; 31 | use states::State::{Initial, Playing, Paused, Stopped}; 32 | use audio_controller::AudioController; 33 | use audio_tags::{AudioTags, Tags}; 34 | 35 | /** 36 | * Play Sounds easily. 37 | * 38 | * Simple class to play sound easily in 2 lines, Sounds are really ligth 39 | * objects, the sounds data are entirely load in memory and can be share between 40 | * Sounds using the SoundData object. 41 | * 42 | * # Examples 43 | * ```Rust 44 | * extern crate ears; 45 | * use ears::Sound; 46 | * 47 | * fn main() -> () { 48 | * // Create a Sound whith the path of the sound file. 49 | * let snd = Sound::new("path/to/my/sound.ogg").unwrap(); 50 | * 51 | * // Play it 52 | * snd.play(); 53 | * 54 | * // Wait until the sound is playing 55 | * while snd.is_playing() {} 56 | * } 57 | * ``` 58 | */ 59 | pub struct Sound { 60 | /// The internal OpenAl source identifier 61 | al_source: u32, 62 | /// The SoundData associated to the Sound. 63 | sound_data: Rc> 64 | } 65 | 66 | impl Sound { 67 | /** 68 | * Default constructor for Sound struct. 69 | * 70 | * Create a new struct and an associated SoundData. 71 | * 72 | * # Argument 73 | * `path` - The path of the sound file to create the SoundData. 74 | * 75 | * # Return 76 | * An Option with Some(Sound) if the Sound is created properly, or None if 77 | * un error has occured. 78 | * 79 | * # Example 80 | * ```Rust 81 | * let snd = match Sound::new("path/to/the/sound.ogg") { 82 | * Some(snd) => snd, 83 | * None => panic!("Cannot load the sound from a file !") 84 | * }; 85 | * ``` 86 | */ 87 | pub fn new(path: &str) -> Option { 88 | check_openal_context!(None); 89 | 90 | let s_data = match SoundData::new(path) { 91 | Some(s_d) => Rc::new(RefCell::new(s_d)), 92 | None => return None 93 | }; 94 | 95 | Sound::new_with_data(s_data) 96 | } 97 | 98 | /** 99 | * Create a new struct with a SoundData to associate. 100 | * 101 | * # Argument 102 | * `sound_data` - The sound_data to associate to the Sound. 103 | * 104 | * # Return 105 | * An Option with Some(Sound) if the Sound is created properly, or None if 106 | * un error has occured. 107 | * 108 | * # Example 109 | * ```Rust 110 | * use ears::SoundData; 111 | * use std::rc::Rc; 112 | * use std::cell::RefCell; 113 | * 114 | * let snd_data = match SoundData::new("path/to/the/sound.ogg") { 115 | * Some(snd_data) => Rc::new(RefCell::new(snd_data)), 116 | * None => panic!("Cannot create the sound data !") 117 | * }; 118 | * let snd = match Sound::new_with_data(snd_data) { 119 | * Some(snd) => snd, 120 | * None => panic!("Cannot create a sound using a sound data !") 121 | * } 122 | * ``` 123 | */ 124 | pub fn new_with_data(sound_data: Rc>) -> Option { 125 | check_openal_context!(None); 126 | 127 | let mut source_id = 0; 128 | // create the source 129 | al::alGenSources(1, &mut source_id); 130 | // set the buffer 131 | al::alSourcei(source_id, 132 | ffi::AL_BUFFER, 133 | sound_data::get_buffer(&*sound_data 134 | .borrow_mut()) as i32); 135 | 136 | // Check if there is OpenAL internal error 137 | match al::openal_has_error() { 138 | Some(err) => { println!("{}", err); return None; }, 139 | None => {} 140 | }; 141 | 142 | Some(Sound { 143 | al_source: source_id, 144 | sound_data: sound_data 145 | }) 146 | } 147 | 148 | /** 149 | * Get the sound datas. 150 | * 151 | * # Return 152 | * The SoundData associated to this Sound. 153 | * 154 | * # Example 155 | * ```Rust 156 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 157 | * let snd_data = snd.get_datas(); 158 | * ``` 159 | */ 160 | pub fn get_datas(&self) -> Rc> { 161 | self.sound_data.clone() 162 | } 163 | 164 | /** 165 | * Set the sound datas. 166 | * 167 | * Doesn't work if the sound is currently playing. 168 | * 169 | * # Argument 170 | * `sound_data` - The new sound_data 171 | * 172 | * # Example 173 | * ```Rust 174 | * let snd1 = Sound::new("path/to/the/sound.ogg").unwrap(); 175 | * let snd2 = Sound::new("other/path/to/the/sound.ogg").unwrap(); 176 | * let snd_data = snd1.get_datas(); 177 | * snd2.set_datas(snd_data); 178 | * ``` 179 | */ 180 | pub fn set_datas(&mut self, sound_data: Rc>) { 181 | check_openal_context!(()); 182 | 183 | if self.is_playing() { 184 | return; 185 | } 186 | 187 | // set the buffer 188 | al::alSourcei(self.al_source, 189 | ffi::AL_BUFFER, 190 | sound_data::get_buffer(&*sound_data 191 | .borrow()) as i32); 192 | 193 | self.sound_data = sound_data 194 | } 195 | } 196 | 197 | impl AudioTags for Sound { 198 | /** 199 | * Get the tags of a Sound. 200 | * 201 | * # Return 202 | * A borrowed pointer to the internal struct SoundTags 203 | */ 204 | fn get_tags(&self) -> Tags { 205 | (*self.sound_data).borrow().get_tags().clone() 206 | } 207 | } 208 | 209 | impl AudioController for Sound { 210 | /** 211 | * Play or resume the Sound. 212 | * 213 | * # Example 214 | * ```Rust 215 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 216 | * snd.play(); 217 | * ``` 218 | */ 219 | fn play(&mut self) -> () { 220 | check_openal_context!(()); 221 | 222 | al::alSourcePlay(self.al_source); 223 | 224 | match al::openal_has_error() { 225 | None => {}, 226 | Some(err) => println!("{}", err) 227 | } 228 | } 229 | 230 | /** 231 | * Pause the Sound. 232 | * 233 | * # Example 234 | * ```Rust 235 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 236 | * snd.play(); 237 | * snd.pause(); 238 | * snd.play(); // the sound restart at the moment of the pause 239 | * ``` 240 | */ 241 | fn pause(&mut self) -> () { 242 | check_openal_context!(()); 243 | 244 | al::alSourcePause(self.al_source) 245 | } 246 | 247 | /** 248 | * Stop the Sound. 249 | * 250 | * # Example 251 | * ```Rust 252 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 253 | * snd.play(); 254 | * snd.stop(); 255 | * snd.play(); // the sound restart at the begining 256 | * ``` 257 | */ 258 | fn stop(&mut self) -> () { 259 | check_openal_context!(()); 260 | 261 | al::alSourceStop(self.al_source) 262 | } 263 | 264 | /** 265 | * Check if the Sound is playing or not. 266 | * 267 | * # Return 268 | * True if the Sound is playing, false otherwise. 269 | * 270 | * # Example 271 | * ```Rust 272 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 273 | * snd.play(); 274 | * if snd.is_playing() { 275 | * println!("Sound is Playing !"); 276 | * } else { 277 | * println!("Sound is Pause or Stopped !"); 278 | * } 279 | * ``` 280 | */ 281 | fn is_playing(&self) -> bool { 282 | match self.get_state() { 283 | Playing => true, 284 | _ => false 285 | } 286 | } 287 | 288 | /** 289 | * Get the current state of the Sound 290 | * 291 | * # Return 292 | * The state of the sound as a variant of the enum State 293 | * 294 | * # Example 295 | * ```Rust 296 | * let snd = Sound::new("path/to/the/sound.ogg").unwrap(); 297 | * match snd.get_state() { 298 | * ears::Initial => println!("Sound has never been played"), 299 | * ears::Playing => println!("Sound is playing !"), 300 | * ears::Paused => println!("Sound is paused !"), 301 | * ears::Stopped => println!("Sound is stopped !") 302 | * } 303 | * ``` 304 | */ 305 | fn get_state(&self) -> State { 306 | check_openal_context!(Initial); 307 | 308 | // Get the source state 309 | let mut state : i32 = 0; 310 | al::alGetSourcei(self.al_source, ffi::AL_SOURCE_STATE, &mut state); 311 | 312 | match state { 313 | ffi::AL_INITIAL => Initial, 314 | ffi::AL_PLAYING => Playing, 315 | ffi::AL_PAUSED => Paused, 316 | ffi::AL_STOPPED => Stopped, 317 | _ => unreachable!() 318 | } 319 | 320 | } 321 | 322 | /** 323 | * Set the volume of the Sound. 324 | * 325 | * A value of 1.0 means unattenuated. Each division by 2 equals an 326 | * attenuation of about -6dB. Each multiplicaton by 2 equals an 327 | * amplification of about +6dB. 328 | * 329 | * # Argument 330 | * * `volume` - The volume of the Sound, should be between 0. and 1. 331 | */ 332 | fn set_volume(&mut self, volume: f32) -> () { 333 | check_openal_context!(()); 334 | 335 | al::alSourcef(self.al_source, ffi::AL_GAIN, volume); 336 | } 337 | 338 | /** 339 | * Get the volume of the Sound. 340 | * 341 | * # Return 342 | * The volume of the Sound between 0. and 1. 343 | */ 344 | fn get_volume(&self) -> f32 { 345 | check_openal_context!(0.); 346 | 347 | let mut volume : f32 = 0.; 348 | al::alGetSourcef(self.al_source, ffi::AL_GAIN, &mut volume); 349 | volume 350 | } 351 | 352 | /** 353 | * Set the minimal volume for a Sound. 354 | * 355 | * The minimum volume allowed for a source, after distance and cone 356 | * attenation is applied (if applicable). 357 | * 358 | * # Argument 359 | * * `min_volume` - The new minimal volume of the Sound should be between 360 | * 0. and 1. 361 | */ 362 | fn set_min_volume(&mut self, min_volume: f32) -> () { 363 | check_openal_context!(()); 364 | 365 | al::alSourcef(self.al_source, ffi::AL_MIN_GAIN, min_volume); 366 | } 367 | 368 | /** 369 | * Get the minimal volume of the Sound. 370 | * 371 | * # Return 372 | * The minimal volume of the Sound between 0. and 1. 373 | */ 374 | fn get_min_volume(&self) -> f32 { 375 | check_openal_context!(0.); 376 | 377 | let mut volume : f32 = 0.; 378 | al::alGetSourcef(self.al_source, ffi::AL_MIN_GAIN, &mut volume); 379 | volume 380 | } 381 | 382 | /** 383 | * Set the maximal volume for a Sound. 384 | * 385 | * The maximum volume allowed for a sound, after distance and cone 386 | * attenation is applied (if applicable). 387 | * 388 | * # Argument 389 | * * `max_volume` - The new maximal volume of the Sound should be between 390 | * 0. and 1. 391 | */ 392 | fn set_max_volume(&mut self, max_volume: f32) -> () { 393 | check_openal_context!(()); 394 | 395 | al::alSourcef(self.al_source, ffi::AL_MAX_GAIN, max_volume); 396 | } 397 | 398 | /** 399 | * Get the maximal volume of the Sound. 400 | * 401 | * # Return 402 | * The maximal volume of the Sound between 0. and 1. 403 | */ 404 | fn get_max_volume(&self) -> f32 { 405 | check_openal_context!(0.); 406 | 407 | let mut volume : f32 = 0.; 408 | al::alGetSourcef(self.al_source, ffi::AL_MAX_GAIN, &mut volume); 409 | volume 410 | } 411 | 412 | /** 413 | * Set the Sound looping or not 414 | * 415 | * The default looping is false. 416 | * 417 | * # Arguments 418 | * `looping` - The new looping state. 419 | */ 420 | fn set_looping(&mut self, looping: bool) -> () { 421 | check_openal_context!(()); 422 | 423 | match looping { 424 | true => al::alSourcei(self.al_source, 425 | ffi::AL_LOOPING, 426 | ffi::ALC_TRUE as i32), 427 | false => al::alSourcei(self.al_source, 428 | ffi::AL_LOOPING, 429 | ffi::ALC_FALSE as i32) 430 | }; 431 | } 432 | 433 | /** 434 | * Check if the Sound is looping or not 435 | * 436 | * # Return 437 | * true if the Sound is looping, false otherwise. 438 | */ 439 | fn is_looping(&self) -> bool { 440 | check_openal_context!(false); 441 | 442 | let mut boolean = 0; 443 | al::alGetSourcei(self.al_source, ffi::AL_LOOPING, &mut boolean); 444 | 445 | match boolean as i8 { 446 | ffi::ALC_TRUE => true, 447 | ffi::ALC_FALSE => false, 448 | _ => unreachable!() 449 | } 450 | } 451 | 452 | /** 453 | * Set the pitch of the source. 454 | * 455 | * A multiplier for the frequency (sample rate) of the source's buffer. 456 | * 457 | * Default pitch is 1.0. 458 | * 459 | * # Argument 460 | * * `new_pitch` - The new pitch of the sound in the range [0.5 - 2.0] 461 | */ 462 | fn set_pitch(&mut self, pitch: f32) -> () { 463 | check_openal_context!(()); 464 | 465 | al::alSourcef(self.al_source, ffi::AL_PITCH, pitch) 466 | } 467 | 468 | /** 469 | * Set the pitch of the source. 470 | * 471 | * # Return 472 | * The pitch of the sound in the range [0.5 - 2.0] 473 | */ 474 | fn get_pitch(&self) -> f32 { 475 | check_openal_context!(0.); 476 | 477 | let mut pitch = 0.; 478 | al::alGetSourcef(self.al_source, ffi::AL_PITCH, &mut pitch); 479 | pitch 480 | } 481 | 482 | /** 483 | * Set the position of the sound relative to the listener or absolute. 484 | * 485 | * Default position is absolute. 486 | * 487 | * # Argument 488 | * `relative` - True to set sound relative to the listener false to set the 489 | * sound position absolute. 490 | */ 491 | fn set_relative(&mut self, relative: bool) -> () { 492 | check_openal_context!(()); 493 | 494 | match relative { 495 | true => al::alSourcei(self.al_source, 496 | ffi::AL_SOURCE_RELATIVE, 497 | ffi::ALC_TRUE as i32), 498 | false => al::alSourcei(self.al_source, 499 | ffi::AL_SOURCE_RELATIVE, 500 | ffi::ALC_FALSE as i32) 501 | }; 502 | } 503 | 504 | /** 505 | * Is the sound relative to the listener or not ? 506 | * 507 | * # Return 508 | * True if the sound is relative to the listener false otherwise 509 | */ 510 | fn is_relative(&mut self) -> bool { 511 | check_openal_context!(false); 512 | 513 | let mut boolean = 0; 514 | al::alGetSourcei(self.al_source, ffi::AL_SOURCE_RELATIVE, &mut boolean); 515 | 516 | match boolean as i8 { 517 | ffi::ALC_TRUE => true, 518 | ffi::ALC_FALSE => false, 519 | _ => unreachable!() 520 | } 521 | } 522 | 523 | /** 524 | * Set the Sound location in three dimensional space. 525 | * 526 | * OpenAL, like OpenGL, uses a right handed coordinate system, where in a 527 | * frontal default view X (thumb) points right, Y points up (index finger), 528 | * and Z points towards the viewer/camera (middle finger). 529 | * To switch from a left handed coordinate system, flip the sign on the Z 530 | * coordinate. 531 | * 532 | * Default position is [0., 0., 0.]. 533 | * 534 | * # Argument 535 | * * `position` - A three dimensional vector of f32 containing the position 536 | * of the listener [x, y, z]. 537 | */ 538 | fn set_position(&mut self, position: [f32, ..3]) -> () { 539 | check_openal_context!(()); 540 | 541 | al::alSourcefv(self.al_source, ffi::AL_POSITION, &position[0]); 542 | } 543 | 544 | /** 545 | * Get the position of the Sound in three dimensional space. 546 | * 547 | * # Return 548 | * A three dimensional vector of f32 containing the position of the 549 | * listener [x, y, z]. 550 | */ 551 | fn get_position(&self) -> [f32, ..3] { 552 | check_openal_context!([0., ..3]); 553 | 554 | let mut position : [f32, ..3] = [0., ..3]; 555 | al::alGetSourcefv(self.al_source, ffi::AL_POSITION, &mut position[0]); 556 | position 557 | } 558 | 559 | /** 560 | * Set the direction of the Sound. 561 | * 562 | * Specifies the current direction in local space. 563 | * 564 | * The default direction is: [0., 0., 0.] 565 | * 566 | * # Argument 567 | * `direction` - The new direction of the Sound. 568 | */ 569 | fn set_direction(&mut self, direction: [f32, ..3]) -> () { 570 | check_openal_context!(()); 571 | 572 | al::alSourcefv(self.al_source, ffi::AL_DIRECTION, &direction[0]); 573 | } 574 | 575 | /** 576 | * Get the direction of the Sound. 577 | * 578 | * # Return 579 | * The current direction of the Sound. 580 | */ 581 | fn get_direction(&self) -> [f32, ..3] { 582 | check_openal_context!([0., ..3]); 583 | 584 | let mut direction : [f32, ..3] = [0., ..3]; 585 | al::alGetSourcefv(self.al_source, ffi::AL_DIRECTION, &mut direction[0]); 586 | direction 587 | } 588 | 589 | /** 590 | * Set the maximum distance of the Sound. 591 | * 592 | * The distance above which the source is not attenuated any further with a 593 | * clamped distance model, or where attenuation reaches 0.0 gain for linear 594 | * distance models with a default rolloff factor. 595 | * 596 | * The default maximum distance is +inf. 597 | * 598 | * # Argument 599 | * `max_distance` - The new maximum distance in the range [0., +inf] 600 | */ 601 | fn set_max_distance(&mut self, max_distance: f32) -> () { 602 | check_openal_context!(()); 603 | 604 | al::alSourcef(self.al_source, ffi::AL_MAX_DISTANCE, max_distance); 605 | } 606 | 607 | /** 608 | * Get the maximum distance of the Sound. 609 | * 610 | * # Return 611 | * The maximum distance of the Sound in the range [0., +inf] 612 | */ 613 | fn get_max_distance(&self) -> f32 { 614 | check_openal_context!(0.); 615 | 616 | let mut max_distance = 0.; 617 | al::alGetSourcef(self.al_source, 618 | ffi::AL_MAX_DISTANCE, 619 | &mut max_distance); 620 | max_distance 621 | } 622 | 623 | /** 624 | * Set the reference distance of the Sound. 625 | * 626 | * The distance in units that no attenuation occurs. 627 | * At 0.0, no distance attenuation ever occurs on non-linear attenuation 628 | * models. 629 | * 630 | * The default distance reference is 1. 631 | * 632 | * # Argument 633 | * * `ref_distance` - The new reference distance of the Sound. 634 | */ 635 | fn set_reference_distance(&mut self, ref_distance: f32) -> () { 636 | check_openal_context!(()); 637 | 638 | al::alSourcef(self.al_source, ffi::AL_REFERENCE_DISTANCE, ref_distance); 639 | } 640 | 641 | /** 642 | * Get the reference distance of the Sound. 643 | * 644 | * # Return 645 | * The current reference distance of the Sound. 646 | */ 647 | fn get_reference_distance(&self) -> f32 { 648 | check_openal_context!(1.); 649 | 650 | let mut ref_distance = 0.; 651 | al::alGetSourcef(self.al_source, 652 | ffi::AL_REFERENCE_DISTANCE, 653 | &mut ref_distance); 654 | ref_distance 655 | } 656 | 657 | /** 658 | * Set the attenuation of a Sound. 659 | * 660 | * Multiplier to exaggerate or diminish distance attenuation. 661 | * At 0.0, no distance attenuation ever occurs. 662 | * 663 | * The default attenuation is 1. 664 | * 665 | * # Arguments 666 | * `attenuation` - The new attenuation for the sound in the range [0., 1.]. 667 | */ 668 | fn set_attenuation(&mut self, attenuation: f32) -> () { 669 | check_openal_context!(()); 670 | 671 | al::alSourcef(self.al_source, ffi::AL_ROLLOFF_FACTOR, attenuation); 672 | } 673 | 674 | /** 675 | * Get the attenuation of a Sound. 676 | * 677 | * # Return 678 | * The current attenuation for the sound in the range [0., 1.]. 679 | */ 680 | fn get_attenuation(&self) -> f32 { 681 | check_openal_context!(1.); 682 | 683 | let mut attenuation = 0.; 684 | al::alGetSourcef(self.al_source, 685 | ffi::AL_ROLLOFF_FACTOR, 686 | &mut attenuation); 687 | attenuation 688 | } 689 | 690 | } 691 | 692 | #[unsafe_destructor] 693 | impl Drop for Sound { 694 | ///Destroy all the resources attached to the Sound. 695 | fn drop(&mut self) -> () { 696 | unsafe { 697 | ffi::alDeleteSources(1, &mut self.al_source); 698 | } 699 | } 700 | } 701 | 702 | #[cfg(test)] 703 | mod test { 704 | #![allow(non_snake_case)] 705 | 706 | use sound::Sound; 707 | use states::State::{Playing, Paused, Stopped}; 708 | use audio_controller::AudioController; 709 | 710 | #[test] 711 | fn sound_create_OK() -> () { 712 | let snd = Sound::new("res/shot.wav"); 713 | println!("YOUHOU"); 714 | match snd { 715 | Some(_) => {}, 716 | None => panic!() 717 | } 718 | } 719 | 720 | #[test] 721 | fn sound_create_FAIL() -> () { 722 | let snd = Sound::new("toto.wav"); 723 | 724 | match snd { 725 | Some(_) => panic!(), 726 | None => {} 727 | } 728 | } 729 | 730 | #[test] 731 | fn sound_play_OK() -> () { 732 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 733 | 734 | snd.play(); 735 | assert_eq!(snd.get_state() as i32, Playing as i32); 736 | snd.stop(); 737 | } 738 | 739 | #[test] 740 | fn sound_pause_OK() -> () { 741 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 742 | 743 | snd.play(); 744 | snd.pause(); 745 | assert_eq!(snd.get_state() as i32, Paused as i32); 746 | snd.stop(); 747 | } 748 | 749 | #[test] 750 | fn sound_stop_OK() -> () { 751 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 752 | 753 | snd.play(); 754 | snd.stop(); 755 | assert_eq!(snd.get_state() as i32, Stopped as i32); 756 | snd.stop(); 757 | } 758 | 759 | #[test] 760 | fn sound_is_playing_TRUE() -> () { 761 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 762 | 763 | snd.play(); 764 | assert_eq!(snd.is_playing(), true); 765 | snd.stop(); 766 | } 767 | 768 | #[test] 769 | fn sound_is_playing_FALSE() -> () { 770 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 771 | 772 | assert_eq!(snd.is_playing(), false); 773 | snd.stop(); 774 | } 775 | 776 | #[test] 777 | fn sound_set_volume_OK() -> () { 778 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 779 | 780 | snd.set_volume(0.7); 781 | assert_eq!(snd.get_volume(), 0.7); 782 | } 783 | 784 | // should fail > 1. 785 | // #[test] 786 | // #[should_fail] 787 | // fn sound_set_volume_high_FAIL() -> () { 788 | // let mut snd = Sound::new("shot.wav").expect("Cannot create sound"); 789 | 790 | // snd.set_volume(10.9); 791 | // assert_eq!(snd.get_volume(), 10.9); 792 | // } 793 | 794 | #[test] 795 | #[should_fail] 796 | fn sound_set_volume_low_FAIL() -> () { 797 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 798 | 799 | snd.set_volume(-1.); 800 | assert_eq!(snd.get_volume(), -1.); 801 | } 802 | 803 | #[test] 804 | fn sound_set_min_volume_OK() -> () { 805 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 806 | 807 | snd.set_min_volume(0.1); 808 | assert_eq!(snd.get_min_volume(), 0.1); 809 | } 810 | 811 | #[test] 812 | #[should_fail] 813 | fn sound_set_min_volume_high_FAIL() -> () { 814 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 815 | 816 | snd.set_min_volume(10.9); 817 | assert_eq!(snd.get_min_volume(), 10.9); 818 | } 819 | 820 | #[test] 821 | #[should_fail] 822 | fn sound_set_min_volume_low_FAIL() -> () { 823 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 824 | 825 | snd.set_min_volume(-1.); 826 | assert_eq!(snd.get_min_volume(), -1.); 827 | } 828 | 829 | #[test] 830 | fn sound_set_max_volume_OK() -> () { 831 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 832 | 833 | snd.set_max_volume(0.9); 834 | assert_eq!(snd.get_max_volume(), 0.9); 835 | } 836 | 837 | #[test] 838 | #[should_fail] 839 | fn sound_set_max_volume_high_FAIL() -> () { 840 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 841 | 842 | snd.set_max_volume(10.9); 843 | assert_eq!(snd.get_max_volume(), 10.9); 844 | } 845 | 846 | #[test] 847 | #[should_fail] 848 | fn sound_set_max_volume_low_FAIL() -> () { 849 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 850 | 851 | snd.set_max_volume(-1.); 852 | assert_eq!(snd.get_max_volume(), -1.); 853 | } 854 | 855 | #[test] 856 | fn sound_is_looping_TRUE() -> () { 857 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 858 | 859 | snd.set_looping(true); 860 | assert_eq!(snd.is_looping(), true); 861 | } 862 | 863 | #[test] 864 | fn sound_is_looping_FALSE() -> () { 865 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 866 | 867 | snd.set_looping(false); 868 | assert_eq!(snd.is_looping(), false); 869 | } 870 | 871 | #[test] 872 | fn sound_set_pitch_OK() -> () { 873 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 874 | 875 | snd.set_pitch(1.5); 876 | assert_eq!(snd.get_pitch(), 1.5); 877 | } 878 | 879 | #[test] 880 | #[should_fail] 881 | fn sound_set_pitch_too_low_FAIL() -> () { 882 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 883 | 884 | snd.set_pitch(-1.); 885 | assert_eq!(snd.get_pitch(), -1.); 886 | } 887 | 888 | // shoud fail > 2. 889 | // #[test] 890 | // #[should_fail] 891 | // fn sound_set_pitch_too_high_FAIL() -> () { 892 | // let mut snd = Sound::new("shot.wav").expect("Cannot create sound"); 893 | 894 | // snd.set_pitch(3.); 895 | // assert_eq!(snd.get_pitch(), 3.); 896 | // } 897 | 898 | #[test] 899 | fn sound_set_relative_TRUE() -> () { 900 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 901 | 902 | snd.set_relative(true); 903 | assert_eq!(snd.is_relative(), true); 904 | } 905 | 906 | #[test] 907 | fn sound_set_relative_FALSE() -> () { 908 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 909 | 910 | snd.set_relative(false); 911 | assert_eq!(snd.is_relative(), false); 912 | } 913 | 914 | // untill https://github.com/rust-lang/rust/issues/7622 is not fixed, slice comparsion is used 915 | 916 | #[test] 917 | fn sound_set_position_OK() -> () { 918 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 919 | 920 | snd.set_position([50f32, 150f32, 250f32]); 921 | let res = snd.get_position(); 922 | assert_eq!([res[0], res[1], res[2]][], [50f32, 150f32, 250f32][]); 923 | } 924 | 925 | #[test] 926 | fn sound_set_direction_OK() -> () { 927 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 928 | 929 | snd.set_direction([50f32, 150f32, 250f32]); 930 | let res = snd.get_direction(); 931 | assert_eq!([res[0], res[1], res[2]][], [50f32, 150f32, 250f32][]); 932 | } 933 | 934 | 935 | #[test] 936 | fn sound_set_max_distance_OK() -> () { 937 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 938 | 939 | snd.set_max_distance(70.); 940 | assert_eq!(snd.get_max_distance(), 70.); 941 | } 942 | 943 | #[test] 944 | #[should_fail] 945 | fn sound_set_max_distance_FAIL() -> () { 946 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 947 | 948 | snd.set_max_distance(-1.); 949 | assert_eq!(snd.get_max_distance(), -1.); 950 | } 951 | 952 | #[test] 953 | fn sound_set_reference_distance_OK() -> () { 954 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 955 | 956 | snd.set_reference_distance(70.); 957 | assert_eq!(snd.get_reference_distance(), 70.); 958 | } 959 | 960 | #[test] 961 | #[should_fail] 962 | fn sound_set_reference_distance_FAIL() -> () { 963 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 964 | 965 | snd.set_reference_distance(-1.); 966 | assert_eq!(snd.get_reference_distance(), -1.); 967 | } 968 | 969 | #[test] 970 | fn sound_set_attenuation_OK() -> () { 971 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 972 | 973 | snd.set_attenuation(0.5f32); 974 | assert_eq!(snd.get_attenuation(), 0.5f32); 975 | } 976 | 977 | #[test] 978 | #[should_fail] 979 | fn sound_set_attenuation_FAIL() -> () { 980 | let mut snd = Sound::new("res/shot.wav").expect("Cannot create sound"); 981 | 982 | snd.set_attenuation(-1.); 983 | assert_eq!(snd.get_attenuation(), -1.); 984 | } 985 | } 986 | -------------------------------------------------------------------------------- /src/sound_data.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! The datas extracted from a sound file. 23 | 24 | use std::mem; 25 | use libc::c_void; 26 | use std::vec::Vec; 27 | 28 | use openal::{ffi, al}; 29 | use sndfile::{SndFile, SndInfo}; 30 | use sndfile::OpenMode::Read; 31 | use internal::OpenAlData; 32 | use audio_tags::{Tags, AudioTags, get_sound_tags}; 33 | 34 | /** 35 | * Samples extracted from a file. 36 | * 37 | * SoundDatas are made to be shared between several Sound and played in the same 38 | * time. 39 | * 40 | * # Example 41 | * ``` 42 | * extern crate ears; 43 | * use ears::{Sound, SoundData}; 44 | * 45 | * fn main() -> () { 46 | * // Create a SoundData 47 | * let snd_data = Rc::new(RefCell::new(SoundData::new("path/to/my/sound.wav") 48 | * .unwrap())); 49 | * 50 | * // Create two Sound with the same SoundData 51 | * let snd1 = Sound::new_with_data(snd_data.clone()).unwrap(); 52 | * let snd2 = Sound::new_with_data(snd_data.clone()).unwrap(); 53 | * 54 | * // Play the sounds 55 | * snd1.play(); 56 | * snd2.play(); 57 | * 58 | * // Wait until snd2 is playing 59 | * while snd2.is_playing() {} 60 | * } 61 | * ``` 62 | */ 63 | pub struct SoundData { 64 | /// The SoundTags who contains all the information of the sound 65 | sound_tags: Tags, 66 | /// The sndfile samples information 67 | snd_info: SndInfo, 68 | /// The total samples count of the Sound 69 | nb_sample: i64, 70 | /// The OpenAl internal identifier for the buffer 71 | al_buffer: u32 72 | } 73 | 74 | impl SoundData { 75 | /** 76 | * Create a new SoundData. 77 | * 78 | * The SoundData contains all the information extracted from the 79 | * file: samples and tags. 80 | * It's an easy way to share the same samples between man Sounds objects. 81 | * 82 | * # Arguments 83 | * * `path` - The path of the file to load 84 | * 85 | * # Return 86 | * An Option with Some(SoundData) if the SoundData is create, or None if 87 | * an error has occured. 88 | */ 89 | pub fn new(path: &str) -> Option { 90 | check_openal_context!(None); 91 | 92 | let mut file; 93 | 94 | match SndFile::new(path, Read) { 95 | Ok(file_) => file = file_, 96 | Err(err) => { println!("{}", err); return None; } 97 | }; 98 | 99 | let infos = file.get_sndinfo(); 100 | 101 | let nb_sample = infos.channels as i64 * infos.frames; 102 | 103 | let mut samples = Vec::from_elem(nb_sample as uint, 0i16); 104 | file.read_i16(samples.as_mut_slice(), nb_sample as i64); 105 | 106 | let mut buffer_id = 0; 107 | let len = mem::size_of::() * (samples.len()); 108 | 109 | // Retrieve format informations 110 | let format = match al::get_channels_format(infos.channels) { 111 | Some(fmt) => fmt, 112 | None => { 113 | println!("Internal error : unrecognized format."); 114 | return None; 115 | } 116 | }; 117 | 118 | al::alGenBuffers(1, &mut buffer_id); 119 | al::alBufferData(buffer_id, 120 | format, 121 | samples.as_ptr() as *mut c_void, 122 | len as i32, 123 | infos.samplerate); 124 | 125 | match al::openal_has_error() { 126 | Some(err) => { println!("{}", err); return None; }, 127 | None => {} 128 | }; 129 | 130 | let sound_data = SoundData { 131 | sound_tags : get_sound_tags(&file), 132 | snd_info : infos, 133 | nb_sample : nb_sample, 134 | al_buffer : buffer_id 135 | }; 136 | file.close(); 137 | 138 | Some(sound_data) 139 | } 140 | } 141 | 142 | 143 | /** 144 | * Get the sound file infos. 145 | * 146 | * # Return 147 | * The struct SndInfo. 148 | */ 149 | pub fn get_sndinfo<'r>(s_data: &'r SoundData) -> &'r SndInfo { 150 | &s_data.snd_info 151 | } 152 | 153 | /** 154 | * Get the OpenAL identifier of the samples buffer. 155 | * 156 | * # Return 157 | * The OpenAL internal identifier for the samples buffer of the sound. 158 | */ 159 | #[doc(hidden)] 160 | pub fn get_buffer(s_data: &SoundData) -> u32 { 161 | s_data.al_buffer 162 | } 163 | 164 | impl AudioTags for SoundData { 165 | /** 166 | * Get the tags of a Sound. 167 | * 168 | * # Return 169 | * A borrowed pointer to the internal struct SoundTags 170 | */ 171 | fn get_tags(&self) -> Tags { 172 | self.sound_tags.clone() 173 | } 174 | } 175 | 176 | impl Drop for SoundData { 177 | /// Destroy all the resources attached to the SoundData 178 | fn drop(&mut self) -> () { 179 | unsafe { 180 | ffi::alDeleteBuffers(1, &mut self.al_buffer); 181 | } 182 | } 183 | } 184 | 185 | #[cfg(test)] 186 | mod test { 187 | #![allow(non_snake_case)] 188 | 189 | #[allow(unused_variables)] 190 | use sound_data::SoundData; 191 | 192 | #[test] 193 | fn sounddata_create_OK() -> () { 194 | #![allow(unused_variables)] 195 | let snd_data = SoundData::new("res/shot.wav").unwrap(); 196 | 197 | } 198 | 199 | #[test] 200 | #[should_fail] 201 | fn sounddata_create_FAIL() -> () { 202 | #![allow(unused_variables)] 203 | let snd_data = SoundData::new("toto.wav").unwrap(); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/states.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! The states of a Sound or a Music 23 | 24 | /// The differents states in which a sound can be. 25 | #[deriving(Clone, PartialEq, PartialOrd, Show, Copy)] 26 | pub enum State { 27 | /// Initial state of the sound or music 28 | Initial, 29 | /// The sound or music is currently playing 30 | Playing, 31 | /// The sound or music is paused 32 | Paused, 33 | /// The sound or music is stopped 34 | Stopped 35 | } 36 | --------------------------------------------------------------------------------