├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── dump_raw.rs ├── metadata.rs └── transcode.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | install: 3 | - sudo apt-add-repository ppa:andrewrk/libgroove -y 4 | - sudo apt-get update 5 | - sudo apt-get install libgroove-dev 6 | script: 7 | - cargo build --verbose 8 | - cargo test --verbose 9 | - cargo doc 10 | notifications: 11 | email: 12 | on_success: never 13 | env: 14 | global: 15 | - secure: fABMIt5khhYF0qMguxyk927BFBPQ54ykLGVYASuHk23uFeR8FYCFyBkt0Pw4CkZI+VB4W8ipTjmn9VTDcxzLw+qBezGX4hoe/nJjKu1EkOypKhP79U//Bp0cb4uckgTGL+lojnuoZWCnchfzHT6kmamW0UthDq9lSSwacMMxuuM= 16 | after_script: 17 | - curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "groove" 4 | version = "0.0.1" 5 | authors = ["Andrew Kelley "] 6 | repository = "https://github.com/andrewrk/groove-rs" 7 | license = "MIT" 8 | documentation = "https://s3.amazonaws.com/superjoe/doc/rust-groove/groove/index.html" 9 | description = "Safe bindings to libgroove - streaming audio processing library" 10 | 11 | [dependencies] 12 | 13 | lazy_static = "0.1.*" 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # groove-rs [![Build Status](https://travis-ci.org/andrewrk/groove-rs.svg?branch=master)](https://travis-ci.org/andrewrk/groove-rs) 2 | 3 | Rust bindings to [libgroove](https://github.com/andrewrk/libgroove) - 4 | streaming audio processing library. 5 | 6 | [Documentation](https://s3.amazonaws.com/superjoe/doc/rust-groove/groove/index.html) 7 | 8 | ## Features 9 | 10 | * Safe interface - no functions are `unsafe` 11 | * Resources are automatically cleaned up for you. 12 | 13 | ## What's Done 14 | 15 | * opening files and adding to a playlist 16 | * basic raw sink support 17 | * basic endoder sink support 18 | 19 | ## What's Left to Do 20 | 21 | * miscellaneous API functions 22 | * groove-player API 23 | * groove-loudness-detector API 24 | * groove-fingerprinter API 25 | -------------------------------------------------------------------------------- /examples/dump_raw.rs: -------------------------------------------------------------------------------- 1 | #![feature(os)] 2 | #![feature(core)] 3 | #![feature(path)] 4 | extern crate groove; 5 | 6 | use std::option::Option; 7 | use std::iter::range_step; 8 | 9 | // dump raw audio samples to stdout 10 | 11 | fn main() { 12 | let args = std::os::args_as_bytes(); 13 | 14 | groove::set_logging(groove::Log::Info); 15 | 16 | let playlist = groove::Playlist::new(); 17 | let sink = groove::Sink::new(); 18 | sink.set_audio_format(groove::AudioFormat { 19 | sample_rate: 44100, 20 | channel_layout: groove::ChannelLayout::LayoutStereo, 21 | sample_fmt: groove::SampleFormat { 22 | sample_type: groove::SampleType::S16, 23 | planar: false, 24 | }, 25 | }); 26 | sink.attach(&playlist).ok().expect("error attaching sink"); 27 | 28 | let input_filename = args[1].as_slice(); 29 | match groove::File::open(&Path::new(input_filename)) { 30 | Option::Some(file) => { 31 | playlist.append(&file, 1.0, 1.0); 32 | }, 33 | Option::None => panic!("could not open file"), 34 | } 35 | 36 | loop { 37 | match sink.buffer_get_blocking() { 38 | Option::Some(decoded_buffer) => { 39 | let buf = decoded_buffer.as_slice_i16(); 40 | for i in range_step(0, buf.len(), 2) { 41 | println!("{} {}", buf[i], buf[i + 1]); 42 | } 43 | }, 44 | Option::None => break, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/metadata.rs: -------------------------------------------------------------------------------- 1 | #![feature(os)] 2 | #![feature(io)] 3 | #![feature(core)] 4 | #![feature(path)] 5 | extern crate groove; 6 | 7 | // read or update metadata in a media file 8 | 9 | fn main() { 10 | let mut stderr = std::old_io::stderr(); 11 | let args = std::os::args(); 12 | let exe = args[0].as_slice(); 13 | 14 | if args.len() < 2 { 15 | print_usage(&mut stderr, exe); 16 | std::os::set_exit_status(1); 17 | return; 18 | } 19 | let _ = writeln!(&mut stderr, "Using libgroove version v{}", groove::version()); 20 | 21 | let filename = Path::new(args[1].as_bytes()); 22 | groove::set_logging(groove::Log::Info); 23 | 24 | { 25 | let file = groove::File::open(&filename).expect("error opening file"); 26 | 27 | let mut i = 2; 28 | while i < args.len() { 29 | let arg = args[i].as_slice(); 30 | if arg == "--update" { 31 | if i + 2 >= args.len() { 32 | let _ = writeln!(&mut stderr, "--update requires 2 arguments"); 33 | print_usage(&mut stderr, exe); 34 | std::os::set_exit_status(1); 35 | return; 36 | } 37 | let key = args[i + 1].as_slice(); 38 | let value = args[i + 2].as_slice(); 39 | i += 2; 40 | file.metadata_set(key, value, false).ok().expect("unable to set metadata"); 41 | } else if arg == "--delete" { 42 | if i + 1 >= args.len() { 43 | let _ = writeln!(&mut stderr, "--delete requires 1 argument"); 44 | print_usage(&mut stderr, exe); 45 | std::os::set_exit_status(1); 46 | return; 47 | } 48 | let key = args[i + 1].as_slice(); 49 | i += 1; 50 | file.metadata_delete(key, false).ok().expect("unable to delete metadata"); 51 | } else { 52 | print_usage(&mut stderr, exe); 53 | std::os::set_exit_status(1); 54 | return; 55 | } 56 | 57 | i += 1; 58 | } 59 | 60 | println!("duration={}", file.duration()); 61 | for tag in file.metadata_iter() { 62 | let k = tag.key().ok().unwrap(); 63 | let v = tag.value().ok().unwrap(); 64 | println!("{}={}", k, v); 65 | } 66 | if file.is_dirty() { 67 | file.save().ok().expect("unable to save file"); 68 | } 69 | } 70 | 71 | groove::finish(); 72 | } 73 | 74 | fn print_usage(stderr: &mut std::old_io::LineBufferedWriter, exe: &str) { 75 | let _ = write!(stderr, "Usage: {} [--update key value] [--delete key]\n", exe); 76 | let _ = write!(stderr, "Repeat --update and --delete as many times as you need to.\n"); 77 | } 78 | -------------------------------------------------------------------------------- /examples/transcode.rs: -------------------------------------------------------------------------------- 1 | #![feature(os)] 2 | #![feature(core)] 3 | #![feature(io)] 4 | #![feature(collections)] 5 | #![feature(path)] 6 | extern crate groove; 7 | 8 | use std::option::Option; 9 | use std::old_io::File; 10 | 11 | // transcode one or more files into one output file 12 | 13 | fn main() { 14 | let mut stderr = std::old_io::stderr(); 15 | let args = std::os::args(); 16 | let exe = args[0].as_slice(); 17 | 18 | let mut bit_rate_k = 320; 19 | let mut format_option = Option::None; 20 | let mut codec_option = Option::None; 21 | let mut mime_option = Option::None; 22 | let mut output_file_name_option = Option::None; 23 | 24 | groove::set_logging(groove::Log::Info); 25 | 26 | let playlist = groove::Playlist::new(); 27 | 28 | let mut i = 1; 29 | while i < args.len() { 30 | let full_arg = args[i].as_slice(); 31 | if full_arg.starts_with("--") { 32 | let arg = &full_arg[2..]; 33 | if i + 1 >= args.len() { 34 | print_usage(&mut stderr, exe); 35 | std::os::set_exit_status(1); 36 | return; 37 | } else if arg == "bitrate" { 38 | i += 1; 39 | bit_rate_k = args[i].parse().unwrap(); 40 | } else if arg == "format" { 41 | i += 1; 42 | format_option = Option::Some(args[i].as_slice()); 43 | } else if arg == "codec" { 44 | i += 1; 45 | codec_option = Option::Some(args[i].as_slice()); 46 | } else if arg == "mime" { 47 | i += 1; 48 | mime_option = Option::Some(args[i].as_slice()); 49 | } else if arg == "output" { 50 | i += 1; 51 | output_file_name_option = Option::Some(args[i].as_slice()); 52 | } else { 53 | print_usage(&mut stderr, exe); 54 | std::os::set_exit_status(1); 55 | return; 56 | } 57 | } else { 58 | match groove::File::open(&Path::new(full_arg.as_bytes())) { 59 | Option::Some(file) => { 60 | playlist.append(&file, 1.0, 1.0); 61 | }, 62 | Option::None => { 63 | let _ = writeln!(&mut stderr, "Error opening input file {}", full_arg); 64 | std::os::set_exit_status(1); 65 | return; 66 | }, 67 | } 68 | } 69 | i += 1; 70 | } 71 | let output_file_name = match output_file_name_option { 72 | Option::Some(file_name) => file_name, 73 | Option::None => { 74 | print_usage(&mut stderr, exe); 75 | std::os::set_exit_status(1); 76 | return; 77 | }, 78 | }; 79 | let encoder = groove::Encoder::new(); 80 | encoder.set_bit_rate(bit_rate_k * 1000); 81 | match format_option { 82 | Option::Some(format) => encoder.set_format_short_name(format), 83 | _ => {}, 84 | } 85 | match codec_option { 86 | Option::Some(codec) => encoder.set_codec_short_name(codec), 87 | _ => {}, 88 | } 89 | match mime_option { 90 | Option::Some(mime) => encoder.set_mime_type(mime), 91 | _ => {}, 92 | } 93 | encoder.set_filename(output_file_name); 94 | 95 | if playlist.len() == 1 { 96 | encoder.set_target_audio_format(playlist.first().file().audio_format()); 97 | 98 | // copy metadata 99 | for tag in playlist.first().file().metadata_iter() { 100 | let k = tag.key().ok().unwrap(); 101 | let v = tag.value().ok().unwrap(); 102 | encoder.metadata_set(k, v, false).ok().expect("unable to set metadata"); 103 | } 104 | } 105 | 106 | encoder.attach(&playlist).ok().expect("error attaching encoder"); 107 | 108 | let mut f = match File::create(&Path::new(output_file_name)) { 109 | Err(_) => { 110 | let _ = writeln!(&mut stderr, "Error opening output file {}", output_file_name); 111 | std::os::set_exit_status(1); 112 | return; 113 | }, 114 | Ok(file) => file, 115 | }; 116 | 117 | loop { 118 | match encoder.buffer_get_blocking() { 119 | Option::Some(buf) => { 120 | f.write_all(buf.as_vec()).ok().expect("write error"); 121 | }, 122 | Option::None => break, 123 | } 124 | } 125 | 126 | groove::finish(); 127 | } 128 | 129 | fn print_usage(stderr: &mut std::old_io::LineBufferedWriter, exe: &str) { 130 | let _ = write!(stderr, "Usage: {} file1 [file2 ...] --output outputfile [--bitrate 320] [--format name] [--codec name] [--mime mimetype]\n", exe); 131 | } 132 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_copy_implementations)] 2 | #![feature(core)] 3 | #![feature(std_misc)] 4 | #![feature(libc)] 5 | #![feature(path)] 6 | extern crate libc; 7 | 8 | #[macro_use] 9 | extern crate lazy_static; 10 | 11 | use std::sync::{Once, ONCE_INIT}; 12 | use std::str::Utf8Error; 13 | use std::option::Option; 14 | use std::result::Result; 15 | use libc::{c_int, uint64_t, c_char, c_void, c_double, uint8_t}; 16 | use std::ffi::CString; 17 | use std::collections::HashMap; 18 | use std::hash::Hash; 19 | use std::collections::hash_map::Hasher; 20 | use std::sync::Mutex; 21 | 22 | lazy_static! { 23 | static ref GROOVE_FILE_RC: Mutex> = 24 | Mutex::new(PointerReferenceCounter::new()); 25 | } 26 | 27 | fn init() { 28 | static mut INIT: Once = ONCE_INIT; 29 | 30 | unsafe { 31 | INIT.call_once(|| { 32 | let err_code = groove_init(); 33 | if err_code != 0 { 34 | panic!("groove_init() failed"); 35 | } 36 | }); 37 | } 38 | } 39 | 40 | #[link(name="groove")] 41 | extern { 42 | fn groove_init() -> c_int; 43 | fn groove_finish(); 44 | fn groove_set_logging(level: c_int); 45 | fn groove_channel_layout_count(channel_layout: uint64_t) -> c_int; 46 | fn groove_channel_layout_default(count: c_int) -> uint64_t; 47 | fn groove_sample_format_bytes_per_sample(format: c_int) -> c_int; 48 | fn groove_version_major() -> c_int; 49 | fn groove_version_minor() -> c_int; 50 | fn groove_version_patch() -> c_int; 51 | fn groove_version() -> *const c_char; 52 | 53 | fn groove_file_open(filename: *const c_char) -> *mut GrooveFile; 54 | fn groove_file_close(file: *mut GrooveFile); 55 | fn groove_file_duration(file: *mut GrooveFile) -> c_double; 56 | fn groove_file_metadata_get(file: *mut GrooveFile, key: *const c_char, 57 | prev: *const c_void, flags: c_int) -> *mut c_void; 58 | fn groove_file_metadata_set(file: *mut GrooveFile, key: *const c_char, 59 | value: *const c_char, flags: c_int) -> c_int; 60 | fn groove_file_save(file: *mut GrooveFile) -> c_int; 61 | fn groove_file_audio_format(file: *mut GrooveFile, audio_format: *mut GrooveAudioFormat); 62 | 63 | fn groove_tag_key(tag: *mut c_void) -> *const c_char; 64 | fn groove_tag_value(tag: *mut c_void) -> *const c_char; 65 | 66 | fn groove_playlist_create() -> *mut GroovePlaylist; 67 | fn groove_playlist_insert(playlist: *mut GroovePlaylist, file: *mut GrooveFile, 68 | gain: c_double, peak: c_double, 69 | next: *mut GroovePlaylistItem) -> *mut GroovePlaylistItem; 70 | fn groove_playlist_destroy(playlist: *mut GroovePlaylist); 71 | fn groove_playlist_count(playlist: *mut GroovePlaylist) -> c_int; 72 | fn groove_playlist_clear(playlist: *mut GroovePlaylist); 73 | fn groove_playlist_set_fill_mode(playlist: *mut GroovePlaylist, mode: c_int); 74 | 75 | fn groove_encoder_create() -> *mut GrooveEncoder; 76 | fn groove_encoder_destroy(encoder: *mut GrooveEncoder); 77 | fn groove_encoder_metadata_set(encoder: *mut GrooveEncoder, key: *const c_char, 78 | value: *const c_char, flags: c_int) -> c_int; 79 | fn groove_encoder_attach(encoder: *mut GrooveEncoder, playlist: *mut GroovePlaylist) -> c_int; 80 | fn groove_encoder_detach(encoder: *mut GrooveEncoder) -> c_int; 81 | fn groove_encoder_buffer_get(encoder: *mut GrooveEncoder, buffer: *mut *mut GrooveBuffer, 82 | block: c_int) -> c_int; 83 | 84 | fn groove_buffer_unref(buffer: *mut GrooveBuffer); 85 | 86 | fn groove_sink_create() -> *mut GrooveSink; 87 | fn groove_sink_destroy(sink: *mut GrooveSink); 88 | fn groove_sink_attach(sink: *mut GrooveSink, playlist: *mut GroovePlaylist) -> c_int; 89 | fn groove_sink_detach(sink: *mut GrooveSink) -> c_int; 90 | fn groove_sink_buffer_get(sink: *mut GrooveSink, buffer: *mut *mut GrooveBuffer, 91 | block: c_int) -> c_int; 92 | } 93 | 94 | #[repr(C)] 95 | struct GrooveSink { 96 | audio_format: GrooveAudioFormat, 97 | disable_resample: c_int, 98 | /// If you leave this to its default of 0, frames pulled from the sink 99 | /// will have sample count determined by efficiency. 100 | /// If you set this to a positive number, frames pulled from the sink 101 | /// will always have this number of samples. 102 | buffer_sample_count: c_int, 103 | 104 | /// how big the buffer queue should be, in sample frames. 105 | /// groove_sink_create defaults this to 8192 106 | buffer_size: c_int, 107 | 108 | /// This volume adjustment only applies to this sink. 109 | /// It is recommended that you leave this at 1.0 and instead adjust the 110 | /// gain of the playlist. 111 | /// If you want to change this value after you have already attached the 112 | /// sink to the playlist, you must use groove_sink_set_gain. 113 | /// float format. Defaults to 1.0 114 | gain: c_double, 115 | 116 | /// set to whatever you want 117 | userdata: *mut c_void, 118 | /// called when the audio queue is flushed. For example, if you seek to a 119 | /// different location in the song. 120 | flush: extern fn(sink: *mut GrooveSink), 121 | /// called when a playlist item is deleted. Take this opportunity to remove 122 | /// all your references to the GroovePlaylistItem. 123 | purge: extern fn(sink: *mut GrooveSink, item: *mut GroovePlaylistItem), 124 | /// called when the playlist is paused 125 | pause: extern fn(sink: *mut GrooveSink), 126 | /// called when the playlist is played 127 | play: extern fn(sink: *mut GrooveSink), 128 | 129 | /// read-only. set when you call groove_sink_attach. cleared when you call 130 | /// groove_sink_detach 131 | playlist: *mut GroovePlaylist, 132 | 133 | /// read-only. automatically computed from audio_format when you call 134 | /// groove_sink_attach 135 | bytes_per_sec: c_int, 136 | } 137 | 138 | /// use this to get access to a realtime raw audio buffer 139 | /// for example you could use it to draw a waveform or other visualization 140 | /// GroovePlayer uses this internally to get the audio buffer for playback 141 | pub struct Sink { 142 | groove_sink: *mut GrooveSink, 143 | } 144 | 145 | impl Drop for Sink { 146 | fn drop(&mut self) { 147 | unsafe { 148 | if !(*self.groove_sink).playlist.is_null() { 149 | groove_sink_detach(self.groove_sink); 150 | } 151 | groove_sink_destroy(self.groove_sink) 152 | } 153 | } 154 | } 155 | 156 | impl Sink { 157 | pub fn new() -> Self { 158 | init(); 159 | unsafe { 160 | Sink { groove_sink: groove_sink_create() } 161 | } 162 | } 163 | 164 | /// set this to the audio format you want the sink to output 165 | pub fn set_audio_format(&self, format: AudioFormat) { 166 | unsafe { 167 | (*self.groove_sink).audio_format = format.to_groove(); 168 | } 169 | } 170 | 171 | pub fn attach(&self, playlist: &Playlist) -> Result<(), i32> { 172 | unsafe { 173 | let err_code = groove_sink_attach(self.groove_sink, playlist.groove_playlist); 174 | if err_code >= 0 { 175 | Result::Ok(()) 176 | } else { 177 | Result::Err(err_code as i32) 178 | } 179 | } 180 | } 181 | 182 | pub fn detach(&self) { 183 | unsafe { 184 | let _ = groove_sink_detach(self.groove_sink); 185 | } 186 | } 187 | 188 | /// returns None on end of playlist, Some when there is a buffer 189 | /// blocks the thread until a buffer or end is found 190 | pub fn buffer_get_blocking(&self) -> Option { 191 | unsafe { 192 | let mut buffer: *mut GrooveBuffer = std::ptr::null_mut(); 193 | match groove_sink_buffer_get(self.groove_sink, &mut buffer, 1) { 194 | BUFFER_NO => panic!("did not expect BUFFER_NO when blocking"), 195 | BUFFER_YES => Option::Some(DecodedBuffer { groove_buffer: buffer }), 196 | BUFFER_END => Option::None, 197 | _ => panic!("unexpected buffer result"), 198 | } 199 | } 200 | } 201 | 202 | /// Set this flag to ignore audio_format. If you set this flag, the 203 | /// buffers you pull from this sink could have any audio format. 204 | pub fn disable_resample(&self, disabled: bool) { 205 | unsafe { 206 | (*self.groove_sink).disable_resample = if disabled {1} else {0} 207 | } 208 | } 209 | } 210 | 211 | /// all fields read-only 212 | #[repr(C)] 213 | struct GrooveBuffer { 214 | /// for interleaved audio, data[0] is the buffer. 215 | /// for planar audio, each channel has a separate data pointer. 216 | /// for encoded audio, data[0] is the encoded buffer. 217 | data: *mut *mut uint8_t, 218 | 219 | format: GrooveAudioFormat, 220 | 221 | /// number of audio frames described by this buffer 222 | /// for encoded audio, this is unknown and set to 0. 223 | frame_count: c_int, 224 | 225 | /// when encoding, if item is NULL, this is a format header or trailer. 226 | /// otherwise, this is encoded audio for the item specified. 227 | /// when decoding, item is never NULL. 228 | item: *mut GroovePlaylistItem, 229 | pos: c_double, 230 | 231 | /// total number of bytes contained in this buffer 232 | size: c_int, 233 | 234 | /// presentation time stamp of the buffer 235 | pts: uint64_t, 236 | } 237 | // Read-only structs are Sync 238 | unsafe impl Sync for GrooveBuffer {} 239 | // Promise rust that nothing points to a GrooveBuffer 240 | // when it destructs 241 | unsafe impl Send for GrooveBuffer {} 242 | 243 | /// A buffer which contains encoded audio data 244 | pub struct EncodedBuffer { 245 | groove_buffer: *mut GrooveBuffer, 246 | } 247 | unsafe impl Sync for EncodedBuffer {} 248 | unsafe impl Send for EncodedBuffer {} 249 | 250 | impl Drop for EncodedBuffer { 251 | fn drop(&mut self) { 252 | unsafe { 253 | groove_buffer_unref(self.groove_buffer); 254 | } 255 | } 256 | } 257 | 258 | impl EncodedBuffer { 259 | pub fn as_vec(&self) -> &[u8] { 260 | unsafe { 261 | let raw_slice = std::raw::Slice { 262 | data: *(*self.groove_buffer).data, 263 | len: (*self.groove_buffer).size as usize, 264 | }; 265 | std::mem::transmute::, &[u8]>(raw_slice) 266 | } 267 | } 268 | } 269 | 270 | /// A buffer which contains raw samples 271 | pub struct DecodedBuffer { 272 | groove_buffer: *mut GrooveBuffer, 273 | } 274 | unsafe impl Sync for DecodedBuffer {} 275 | unsafe impl Send for DecodedBuffer {} 276 | 277 | impl Drop for DecodedBuffer { 278 | fn drop(&mut self) { 279 | unsafe { 280 | groove_buffer_unref(self.groove_buffer); 281 | } 282 | } 283 | } 284 | 285 | impl DecodedBuffer { 286 | /// returns a vector of f64 287 | /// panics if the buffer is not planar 288 | /// panics if the buffer is not SampleType::Dbl 289 | pub fn channel_as_slice_f64(&self, channel_index: u32) -> &[f64] { 290 | match self.sample_format().sample_type { 291 | SampleType::Dbl => self.channel_as_slice_generic(channel_index), 292 | _ => panic!("buffer not in f64 format"), 293 | } 294 | } 295 | 296 | /// returns a vector of f32 297 | /// panics if the buffer is not planar 298 | /// panics if the buffer is not SampleType::Flt 299 | pub fn channel_as_slice_f32(&self, channel_index: u32) -> &[f32] { 300 | match self.sample_format().sample_type { 301 | SampleType::Flt => self.channel_as_slice_generic(channel_index), 302 | _ => panic!("buffer not in f32 format"), 303 | } 304 | } 305 | 306 | /// returns a vector of i32 307 | /// panics if the buffer is not planar 308 | /// panics if the buffer is not SampleType::S32 309 | pub fn channel_as_slice_i32(&self, channel_index: u32) -> &[i32] { 310 | match self.sample_format().sample_type { 311 | SampleType::S32 => self.channel_as_slice_generic(channel_index), 312 | _ => panic!("buffer not in i32 format"), 313 | } 314 | } 315 | 316 | /// returns a vector of i16 317 | /// panics if the buffer is not planar 318 | /// panics if the buffer is not SampleType::S16 319 | pub fn channel_as_slice_i16(&self, channel_index: u32) -> &[i16] { 320 | match self.sample_format().sample_type { 321 | SampleType::S16 => self.channel_as_slice_generic(channel_index), 322 | _ => panic!("buffer not in i16 format"), 323 | } 324 | } 325 | 326 | /// returns a vector of u8 327 | /// panics if the buffer is not planar 328 | /// panics if the buffer is not SampleType::U8 329 | pub fn channel_as_slice_u8(&self, channel_index: u32) -> &[u8] { 330 | match self.sample_format().sample_type { 331 | SampleType::U8 => self.channel_as_slice_generic(channel_index), 332 | _ => panic!("buffer not in u8 format"), 333 | } 334 | } 335 | 336 | pub fn sample_format(&self) -> SampleFormat { 337 | unsafe { 338 | SampleFormat::from_groove((*self.groove_buffer).format.sample_fmt) 339 | } 340 | } 341 | 342 | fn channel_as_slice_generic(&self, channel_index: u32) -> &[T] { 343 | unsafe { 344 | let sample_fmt = self.sample_format(); 345 | if !sample_fmt.planar { 346 | panic!("expected planar buffer"); 347 | } 348 | let channel_count = groove_channel_layout_count( 349 | (*self.groove_buffer).format.channel_layout) as u32; 350 | if channel_index >= channel_count { 351 | panic!("invalid channel index"); 352 | } 353 | let frame_count = (*self.groove_buffer).frame_count as usize; 354 | let raw_slice = std::raw::Slice { 355 | data: *((*self.groove_buffer).data.offset(channel_index as isize)), 356 | len: frame_count, 357 | }; 358 | std::mem::transmute::, &[T]>(raw_slice) 359 | } 360 | } 361 | 362 | /// returns a single channel and always returns [u8] 363 | /// panics if the buffer is not planar 364 | pub fn channel_as_slice_raw(&self, channel_index: u32) -> &[u8] { 365 | self.channel_as_slice_generic(channel_index) 366 | } 367 | 368 | /// returns a vector of f64 369 | /// panics if the buffer is planar 370 | /// panics if the buffer is not SampleType::Dbl 371 | pub fn as_slice_f64(&self) -> &[f64] { 372 | match self.sample_format().sample_type { 373 | SampleType::Dbl => self.as_slice_generic(), 374 | _ => panic!("buffer not in f64 format"), 375 | } 376 | } 377 | 378 | /// returns a vector of f32 379 | /// panics if the buffer is planar 380 | /// panics if the buffer is not SampleType::Flt 381 | pub fn as_slice_f32(&self) -> &[f32] { 382 | match self.sample_format().sample_type { 383 | SampleType::Flt => self.as_slice_generic(), 384 | _ => panic!("buffer not in f32 format"), 385 | } 386 | } 387 | 388 | /// returns a vector of i32 389 | /// panics if the buffer is planar 390 | /// panics if the buffer is not SampleType::S32 391 | pub fn as_slice_i32(&self) -> &[i32] { 392 | match self.sample_format().sample_type { 393 | SampleType::S32 => self.as_slice_generic(), 394 | _ => panic!("buffer not in i32 format"), 395 | } 396 | } 397 | 398 | /// returns a vector of i16 399 | /// panics if the buffer is planar 400 | /// panics if the buffer is not SampleType::S16 401 | pub fn as_slice_i16(&self) -> &[i16] { 402 | match self.sample_format().sample_type { 403 | SampleType::S16 => self.as_slice_generic(), 404 | _ => panic!("buffer not in i16 format"), 405 | } 406 | } 407 | 408 | /// returns a vector of u8 409 | /// panics if the buffer is planar 410 | /// panics if the buffer is not SampleType::U8 411 | pub fn as_slice_u8(&self) -> &[u8] { 412 | match self.sample_format().sample_type { 413 | SampleType::U8 => self.as_slice_generic(), 414 | _ => panic!("buffer not in u8 format"), 415 | } 416 | } 417 | 418 | /// returns all the buffer data as [u8] 419 | /// panics if the buffer is planar 420 | pub fn as_slice_raw(&self) -> &[u8] { 421 | self.as_slice_generic() 422 | } 423 | 424 | fn as_slice_generic(&self) -> &[T] { 425 | unsafe { 426 | let sample_fmt = (*self.groove_buffer).format.sample_fmt; 427 | if SampleFormat::from_groove(sample_fmt).planar { 428 | panic!("as_vec works for interleaved buffers only"); 429 | } 430 | let channel_count = groove_channel_layout_count( 431 | (*self.groove_buffer).format.channel_layout) as usize; 432 | let frame_count = (*self.groove_buffer).frame_count as usize; 433 | let raw_slice = std::raw::Slice { 434 | data: *(*self.groove_buffer).data, 435 | len: channel_count * frame_count, 436 | }; 437 | std::mem::transmute::, &[T]>(raw_slice) 438 | } 439 | } 440 | } 441 | 442 | /// all fields are read-only. modify with methods 443 | #[repr(C)] 444 | struct GroovePlaylistItem { 445 | file: *mut GrooveFile, 446 | 447 | gain: c_double, 448 | peak: c_double, 449 | 450 | /// A GroovePlaylist is a doubly linked list. Use these fields to 451 | /// traverse the list. 452 | prev: *mut GroovePlaylistItem, 453 | next: *mut GroovePlaylistItem, 454 | } 455 | 456 | pub struct PlaylistItem { 457 | groove_playlist_item: *mut GroovePlaylistItem, 458 | } 459 | 460 | impl PlaylistItem { 461 | /// A volume adjustment in float format to apply to the file when it plays. 462 | /// This is typically used for loudness compensation, for example ReplayGain. 463 | /// To convert from dB to float, use exp(log(10) * 0.05 * dB_value) 464 | pub fn gain(&self) -> f64 { 465 | unsafe { 466 | (*self.groove_playlist_item).gain 467 | } 468 | } 469 | 470 | /// The sample peak of this playlist item is assumed to be 1.0 in float 471 | /// format. If you know for certain that the peak is less than 1.0, you 472 | /// may set this value which may allow the volume adjustment to use 473 | /// a pure amplifier rather than a compressor. This results in slightly 474 | /// better audio quality. 475 | pub fn peak(&self) -> f64 { 476 | unsafe { 477 | (*self.groove_playlist_item).peak 478 | } 479 | } 480 | 481 | pub fn file(&self) -> File { 482 | unsafe { 483 | let groove_file = (*self.groove_playlist_item).file; 484 | GROOVE_FILE_RC.lock().unwrap().incr(groove_file); 485 | File {groove_file: groove_file} 486 | } 487 | } 488 | } 489 | 490 | /// a GroovePlaylist keeps its sinks full. 491 | /// all fields are read-only. modify using methods. 492 | #[repr(C)] 493 | struct GroovePlaylist { 494 | /// doubly linked list which is the playlist 495 | head: *mut GroovePlaylistItem, 496 | tail: *mut GroovePlaylistItem, 497 | 498 | gain: c_double, 499 | } 500 | 501 | /// a playlist keeps its sinks full. 502 | pub struct Playlist { 503 | groove_playlist: *mut GroovePlaylist, 504 | } 505 | impl Drop for Playlist { 506 | fn drop(&mut self) { 507 | self.clear(); 508 | unsafe { groove_playlist_destroy(self.groove_playlist) } 509 | } 510 | } 511 | 512 | impl Playlist { 513 | pub fn new() -> Self { 514 | init(); 515 | unsafe { 516 | Playlist { groove_playlist: groove_playlist_create() } 517 | } 518 | } 519 | 520 | /// volume adjustment in float format which applies to all playlist items 521 | /// and all sinks. defaults to 1.0. 522 | pub fn gain(&self) -> f64 { 523 | unsafe { 524 | (*self.groove_playlist).gain 525 | } 526 | } 527 | 528 | /// get the first playlist item 529 | pub fn first(&self) -> PlaylistItem { 530 | unsafe { 531 | PlaylistItem {groove_playlist_item: (*self.groove_playlist).head } 532 | } 533 | } 534 | 535 | /// get the last playlist item 536 | pub fn last(&self) -> PlaylistItem { 537 | unsafe { 538 | PlaylistItem {groove_playlist_item: (*self.groove_playlist).tail } 539 | } 540 | } 541 | 542 | pub fn iter(&self) -> PlaylistIterator { 543 | unsafe { 544 | PlaylistIterator { curr: (*self.groove_playlist).head } 545 | } 546 | } 547 | 548 | /// once you add a file to the playlist, you must not destroy it until you first 549 | /// remove it from the playlist. 550 | /// gain: see PlaylistItem. use 1.0 for no adjustment. 551 | /// peak: see PlaylistItem. use 1.0 for no adjustment. 552 | /// returns the newly created playlist item. 553 | pub fn append(&self, file: &File, gain: f64, peak: f64) -> PlaylistItem { 554 | unsafe { 555 | let inserted_item = groove_playlist_insert(self.groove_playlist, file.groove_file, 556 | gain, peak, std::ptr::null_mut()); 557 | if inserted_item.is_null() { 558 | panic!("out of memory"); 559 | } else { 560 | GROOVE_FILE_RC.lock().unwrap().incr(file.groove_file); 561 | PlaylistItem {groove_playlist_item: inserted_item} 562 | } 563 | } 564 | } 565 | 566 | /// once you add a file to the playlist, you must not destroy it until you first 567 | /// remove it from the playlist. 568 | /// before: the item to insert before. 569 | /// gain: see Groove. use 1.0 for no adjustment. 570 | /// peak: see Groove. use 1.0 for no adjustment. 571 | /// returns the newly created playlist item. 572 | pub fn insert(&self, file: &File, gain: f64, peak: f64, before: &PlaylistItem) -> PlaylistItem { 573 | unsafe { 574 | let inserted_item = groove_playlist_insert(self.groove_playlist, file.groove_file, 575 | gain, peak, before.groove_playlist_item); 576 | if inserted_item.is_null() { 577 | panic!("out of memory"); 578 | } else { 579 | GROOVE_FILE_RC.lock().unwrap().incr(file.groove_file); 580 | PlaylistItem {groove_playlist_item: inserted_item} 581 | } 582 | } 583 | } 584 | 585 | /// return the count of playlist items 586 | pub fn len(&self) -> i32 { 587 | unsafe { 588 | groove_playlist_count(self.groove_playlist) as i32 589 | } 590 | } 591 | 592 | /// remove all playlist items 593 | pub fn clear(&self) { 594 | unsafe { 595 | let groove_files: Vec<*mut GrooveFile> = 596 | self.iter().map(|x| (*x.groove_playlist_item).file).collect(); 597 | groove_playlist_clear(self.groove_playlist); 598 | for groove_file in groove_files.iter() { 599 | GROOVE_FILE_RC.lock().unwrap().decr(*groove_file); 600 | } 601 | } 602 | } 603 | 604 | pub fn set_fill_mode(&self, mode: FillMode) { 605 | let mode_int = match mode { 606 | FillMode::EverySinkFull => EVERY_SINK_FULL, 607 | FillMode::AnySinkFull => ANY_SINK_FULL, 608 | }; 609 | unsafe { groove_playlist_set_fill_mode(self.groove_playlist, mode_int) } 610 | } 611 | } 612 | 613 | pub struct PlaylistIterator { 614 | curr: *mut GroovePlaylistItem, 615 | } 616 | 617 | impl Iterator for PlaylistIterator { 618 | type Item = PlaylistItem; 619 | 620 | fn next(&mut self) -> Option { 621 | unsafe { 622 | if self.curr.is_null() { 623 | Option::None 624 | } else { 625 | let prev = self.curr; 626 | self.curr = (*self.curr).next; 627 | Option::Some(PlaylistItem {groove_playlist_item: prev}) 628 | } 629 | } 630 | } 631 | } 632 | 633 | #[repr(C)] 634 | struct GrooveFile { 635 | dirty: c_int, 636 | filename: *const c_char, 637 | } 638 | 639 | impl Destroy for *mut GrooveFile { 640 | fn destroy(&self) { 641 | unsafe { 642 | groove_file_close(*self); 643 | } 644 | } 645 | } 646 | 647 | pub struct File { 648 | groove_file: *mut GrooveFile, 649 | } 650 | 651 | impl Drop for File { 652 | fn drop(&mut self) { 653 | GROOVE_FILE_RC.lock().unwrap().decr(self.groove_file); 654 | } 655 | } 656 | 657 | impl File { 658 | /// open a file on disk and prepare to stream audio from it 659 | pub fn open(filename: &Path) -> Option { 660 | init(); 661 | let c_filename = CString::from_slice(filename.as_vec()); 662 | unsafe { 663 | let groove_file = groove_file_open(c_filename.as_ptr()); 664 | match groove_file.is_null() { 665 | true => Option::None, 666 | false => { 667 | GROOVE_FILE_RC.lock().unwrap().incr(groove_file); 668 | Option::Some(File { groove_file: groove_file }) 669 | } 670 | } 671 | } 672 | } 673 | 674 | pub fn filename(&self) -> Path { 675 | unsafe { 676 | let slice = std::ffi::c_str_to_bytes(&(*self.groove_file).filename); 677 | Path::new(slice) 678 | } 679 | } 680 | /// whether the file has pending edits 681 | pub fn is_dirty(&self) -> bool { 682 | unsafe { 683 | (*self.groove_file).dirty == 1 684 | } 685 | } 686 | /// main audio stream duration in seconds. note that this relies on a 687 | /// combination of format headers and heuristics. It can be inaccurate. 688 | /// The most accurate way to learn the duration of a file is to use 689 | /// GrooveLoudnessDetector 690 | pub fn duration(&self) -> f64 { 691 | unsafe { 692 | groove_file_duration(self.groove_file) 693 | } 694 | } 695 | 696 | pub fn metadata_get(&self, key: &str, case_sensitive: bool) -> Option { 697 | let flags: c_int = if case_sensitive {TAG_MATCH_CASE} else {0}; 698 | let c_tag_key = CString::from_slice(key.as_bytes()); 699 | unsafe { 700 | let tag = groove_file_metadata_get(self.groove_file, c_tag_key.as_ptr(), 701 | std::ptr::null(), flags); 702 | if tag.is_null() { 703 | Option::None 704 | } else { 705 | Option::Some(Tag {groove_tag: tag}) 706 | } 707 | } 708 | } 709 | 710 | pub fn metadata_iter(&self) -> MetadataIterator { 711 | MetadataIterator { file: self, curr: std::ptr::null() } 712 | } 713 | 714 | pub fn metadata_set(&self, key: &str, value: &str, case_sensitive: bool) -> Result<(), i32> { 715 | let flags: c_int = if case_sensitive {TAG_MATCH_CASE} else {0}; 716 | let c_tag_key = CString::from_slice(key.as_bytes()); 717 | let c_tag_value = CString::from_slice(value.as_bytes()); 718 | unsafe { 719 | let err_code = groove_file_metadata_set(self.groove_file, c_tag_key.as_ptr(), 720 | c_tag_value.as_ptr(), flags); 721 | if err_code >= 0 { 722 | Result::Ok(()) 723 | } else { 724 | Result::Err(err_code as i32) 725 | } 726 | } 727 | } 728 | 729 | pub fn metadata_delete(&self, key: &str, case_sensitive: bool) -> Result<(), i32> { 730 | let flags: c_int = if case_sensitive {TAG_MATCH_CASE} else {0}; 731 | let c_tag_key = CString::from_slice(key.as_bytes()); 732 | unsafe { 733 | let err_code = groove_file_metadata_set(self.groove_file, c_tag_key.as_ptr(), 734 | std::ptr::null(), flags); 735 | if err_code >= 0 { 736 | Result::Ok(()) 737 | } else { 738 | Result::Err(err_code as i32) 739 | } 740 | } 741 | } 742 | 743 | /// write changes made to metadata to disk. 744 | pub fn save(&self) -> Result<(), i32> { 745 | unsafe { 746 | let err_code = groove_file_save(self.groove_file); 747 | if err_code >= 0 { 748 | Result::Ok(()) 749 | } else { 750 | Result::Err(err_code as i32) 751 | } 752 | } 753 | } 754 | 755 | /// get the audio format of the main audio stream of a file 756 | pub fn audio_format(&self) -> AudioFormat { 757 | unsafe { 758 | let mut result = GrooveAudioFormat { 759 | sample_rate: 0, 760 | channel_layout: 0, 761 | sample_fmt: 0, 762 | }; 763 | groove_file_audio_format(self.groove_file, &mut result); 764 | AudioFormat::from_groove(&result) 765 | } 766 | } 767 | } 768 | 769 | pub struct MetadataIterator<'a> { 770 | file: &'a File, 771 | curr: *const c_void, 772 | } 773 | 774 | impl<'a> Iterator for MetadataIterator<'a> { 775 | type Item = Tag<'a>; 776 | fn next(&mut self) -> Option { 777 | let c_tag_key = CString::from_slice("".as_bytes()); 778 | unsafe { 779 | let tag = groove_file_metadata_get(self.file.groove_file, c_tag_key.as_ptr(), 780 | self.curr, 0); 781 | self.curr = tag; 782 | if tag.is_null() { 783 | Option::None 784 | } else { 785 | Option::Some(Tag {groove_tag: tag}) 786 | } 787 | } 788 | } 789 | } 790 | 791 | const EVERY_SINK_FULL: c_int = 0; 792 | const ANY_SINK_FULL: c_int = 1; 793 | 794 | #[derive(Copy, Debug)] 795 | pub enum FillMode { 796 | /// This is the default behavior. The playlist will decode audio if any sinks 797 | /// are not full. If any sinks do not drain fast enough the data will buffer up 798 | /// in the playlist. 799 | EverySinkFull, 800 | 801 | /// With this behavior, the playlist will stop decoding audio when any attached 802 | /// sink is full, and then resume decoding audio every sink is not full. 803 | AnySinkFull, 804 | } 805 | 806 | #[derive(Copy, Debug)] 807 | pub enum Log { 808 | Quiet, 809 | Error, 810 | Warning, 811 | Info, 812 | } 813 | 814 | #[derive(Copy, Debug)] 815 | pub enum ChannelLayout { 816 | FrontLeft, 817 | FrontRight, 818 | FrontCenter, 819 | LayoutMono, 820 | LayoutStereo, 821 | } 822 | 823 | const CH_FRONT_LEFT :uint64_t = 0x00000001; 824 | const CH_FRONT_RIGHT :uint64_t = 0x00000002; 825 | const CH_FRONT_CENTER :uint64_t = 0x00000004; 826 | const CH_LAYOUT_MONO :uint64_t = CH_FRONT_CENTER; 827 | const CH_LAYOUT_STEREO :uint64_t = CH_FRONT_LEFT|CH_FRONT_RIGHT; 828 | 829 | impl ChannelLayout { 830 | /// get the default channel layout based on the channel count 831 | pub fn default(count: i32) -> Self { 832 | let x = unsafe { groove_channel_layout_default(count) }; 833 | ChannelLayout::from_groove(x) 834 | } 835 | 836 | /// Get the channel count for the channel layout 837 | pub fn count(&self) -> i32 { 838 | unsafe { groove_channel_layout_count(self.to_groove()) as i32 } 839 | } 840 | 841 | fn to_groove(&self) -> uint64_t { 842 | match *self { 843 | ChannelLayout::FrontLeft => CH_FRONT_LEFT, 844 | ChannelLayout::FrontRight => CH_FRONT_RIGHT, 845 | ChannelLayout::FrontCenter => CH_FRONT_CENTER, 846 | ChannelLayout::LayoutMono => CH_LAYOUT_MONO, 847 | ChannelLayout::LayoutStereo => CH_LAYOUT_STEREO, 848 | } 849 | } 850 | 851 | fn from_groove(x: uint64_t) -> Self { 852 | match x { 853 | CH_FRONT_LEFT => ChannelLayout::FrontLeft, 854 | CH_FRONT_RIGHT => ChannelLayout::FrontRight, 855 | CH_FRONT_CENTER => ChannelLayout::FrontCenter, 856 | CH_LAYOUT_STEREO => ChannelLayout::LayoutStereo, 857 | _ => panic!("invalid channel layout"), 858 | } 859 | } 860 | } 861 | 862 | const SAMPLE_FMT_NONE: i32 = -1; 863 | const SAMPLE_FMT_U8: i32 = 0; 864 | const SAMPLE_FMT_S16: i32 = 1; 865 | const SAMPLE_FMT_S32: i32 = 2; 866 | const SAMPLE_FMT_FLT: i32 = 3; 867 | const SAMPLE_FMT_DBL: i32 = 4; 868 | 869 | const SAMPLE_FMT_U8P: i32 = 5; 870 | const SAMPLE_FMT_S16P: i32 = 6; 871 | const SAMPLE_FMT_S32P: i32 = 7; 872 | const SAMPLE_FMT_FLTP: i32 = 8; 873 | const SAMPLE_FMT_DBLP: i32 = 9; 874 | 875 | /// how to organize bits which represent audio samples 876 | pub struct SampleFormat { 877 | pub sample_type: SampleType, 878 | /// planar means non-interleaved 879 | pub planar: bool, 880 | } 881 | impl Copy for SampleFormat {} 882 | 883 | #[derive(Copy, Debug)] 884 | pub enum SampleType { 885 | NoType, 886 | /// unsigned 8 bits 887 | U8, 888 | /// signed 16 bits 889 | S16, 890 | /// signed 32 bits 891 | S32, 892 | /// float (32 bits) 893 | Flt, 894 | /// double (64 bits) 895 | Dbl, 896 | } 897 | 898 | impl SampleFormat { 899 | fn to_groove(&self) -> i32 { 900 | match (self.sample_type, self.planar) { 901 | (SampleType::NoType, false) => SAMPLE_FMT_NONE, 902 | (SampleType::U8, false) => SAMPLE_FMT_U8, 903 | (SampleType::S16, false) => SAMPLE_FMT_S16, 904 | (SampleType::S32, false) => SAMPLE_FMT_S32, 905 | (SampleType::Flt, false) => SAMPLE_FMT_FLT, 906 | (SampleType::Dbl, false) => SAMPLE_FMT_DBL, 907 | 908 | (SampleType::NoType, true) => SAMPLE_FMT_NONE, 909 | (SampleType::U8, true) => SAMPLE_FMT_U8P, 910 | (SampleType::S16, true) => SAMPLE_FMT_S16P, 911 | (SampleType::S32, true) => SAMPLE_FMT_S32P, 912 | (SampleType::Flt, true) => SAMPLE_FMT_FLTP, 913 | (SampleType::Dbl, true) => SAMPLE_FMT_DBLP, 914 | } 915 | } 916 | 917 | fn from_groove(groove_sample_format: i32) -> SampleFormat { 918 | match groove_sample_format { 919 | SAMPLE_FMT_NONE => SampleFormat { sample_type: SampleType::NoType, planar: false }, 920 | SAMPLE_FMT_U8 => SampleFormat { sample_type: SampleType::U8, planar: false }, 921 | SAMPLE_FMT_S16 => SampleFormat { sample_type: SampleType::S16, planar: false }, 922 | SAMPLE_FMT_S32 => SampleFormat { sample_type: SampleType::S32, planar: false }, 923 | SAMPLE_FMT_FLT => SampleFormat { sample_type: SampleType::Flt, planar: false }, 924 | SAMPLE_FMT_DBL => SampleFormat { sample_type: SampleType::Dbl, planar: false }, 925 | 926 | SAMPLE_FMT_U8P => SampleFormat { sample_type: SampleType::U8, planar: true }, 927 | SAMPLE_FMT_S16P => SampleFormat { sample_type: SampleType::S16, planar: true }, 928 | SAMPLE_FMT_S32P => SampleFormat { sample_type: SampleType::S32, planar: true }, 929 | SAMPLE_FMT_FLTP => SampleFormat { sample_type: SampleType::Flt, planar: true }, 930 | SAMPLE_FMT_DBLP => SampleFormat { sample_type: SampleType::Dbl, planar: true }, 931 | 932 | _ => panic!("invalid sample format value"), 933 | } 934 | } 935 | 936 | pub fn bytes_per_sample(&self) -> u32 { 937 | unsafe { groove_sample_format_bytes_per_sample(self.to_groove()) as u32 } 938 | } 939 | } 940 | 941 | pub struct Tag<'a> { 942 | groove_tag: *mut c_void, 943 | } 944 | 945 | impl<'a> Tag<'a> { 946 | pub fn key(&self) -> Result<&'a str, Utf8Error> { 947 | unsafe { 948 | let key_c_str = groove_tag_key(self.groove_tag); 949 | let slice = std::ffi::c_str_to_bytes(&key_c_str); 950 | match std::str::from_utf8(slice) { 951 | Result::Ok(s) => Result::Ok(std::mem::transmute::<&str, &'a str>(s)), 952 | Result::Err(err) => Result::Err(err), 953 | } 954 | } 955 | } 956 | pub fn value(&self) -> Result<&'a str, Utf8Error> { 957 | unsafe { 958 | let val_c_str = groove_tag_value(self.groove_tag); 959 | let slice = std::ffi::c_str_to_bytes(&val_c_str); 960 | match std::str::from_utf8(slice) { 961 | Result::Ok(s) => Result::Ok(std::mem::transmute::<&str, &'a str>(s)), 962 | Result::Err(err) => Result::Err(err), 963 | } 964 | } 965 | } 966 | } 967 | 968 | #[repr(C)] 969 | struct GrooveAudioFormat { 970 | sample_rate: c_int, 971 | channel_layout: uint64_t, 972 | sample_fmt: c_int, 973 | } 974 | 975 | pub struct AudioFormat { 976 | pub sample_rate: i32, 977 | pub channel_layout: ChannelLayout, 978 | pub sample_fmt: SampleFormat, 979 | } 980 | impl Copy for AudioFormat {} 981 | 982 | impl AudioFormat { 983 | fn from_groove(groove_audio_format: &GrooveAudioFormat) -> Self { 984 | AudioFormat { 985 | sample_rate: groove_audio_format.sample_rate as i32, 986 | channel_layout: ChannelLayout::from_groove(groove_audio_format.channel_layout), 987 | sample_fmt: SampleFormat::from_groove(groove_audio_format.sample_fmt), 988 | } 989 | } 990 | fn to_groove(&self) -> GrooveAudioFormat { 991 | GrooveAudioFormat { 992 | sample_rate: self.sample_rate as c_int, 993 | channel_layout: self.channel_layout.to_groove(), 994 | sample_fmt: self.sample_fmt.to_groove(), 995 | } 996 | } 997 | } 998 | 999 | #[repr(C)] 1000 | struct GrooveEncoder { 1001 | target_audio_format: GrooveAudioFormat, 1002 | bit_rate: c_int, 1003 | format_short_name: *const c_char, 1004 | codec_short_name: *const c_char, 1005 | filename: *const c_char, 1006 | mime_type: *const c_char, 1007 | 1008 | /// how big the sink buffer should be, in sample frames. 1009 | /// groove_encoder_create defaults this to 8192 1010 | sink_buffer_size: c_int, 1011 | 1012 | /// how big the encoded audio buffer should be, in bytes 1013 | /// groove_encoder_create defaults this to 16384 1014 | encoded_buffer_size: c_int, 1015 | 1016 | /// This volume adjustment to make to this player. 1017 | /// It is recommended that you leave this at 1.0 and instead adjust the 1018 | /// gain of the underlying playlist. 1019 | /// If you want to change this value after you have already attached the 1020 | /// sink to the playlist, you must use groove_encoder_set_gain. 1021 | /// float format. Defaults to 1.0 1022 | gain: c_double, 1023 | 1024 | /// read-only. set when attached and cleared when detached 1025 | playlist: *mut GroovePlaylist, 1026 | 1027 | actual_audio_format: GrooveAudioFormat, 1028 | } 1029 | 1030 | /// attach an Encoder to a playlist to keep a buffer of encoded audio full. 1031 | /// for example you could use it to implement an http audio stream 1032 | pub struct Encoder { 1033 | groove_encoder: *mut GrooveEncoder, 1034 | } 1035 | 1036 | impl Drop for Encoder { 1037 | fn drop(&mut self) { 1038 | unsafe { 1039 | if !(*self.groove_encoder).playlist.is_null() { 1040 | groove_encoder_detach(self.groove_encoder); 1041 | } 1042 | groove_encoder_destroy(self.groove_encoder) 1043 | } 1044 | } 1045 | } 1046 | 1047 | impl Encoder { 1048 | pub fn new() -> Self { 1049 | init(); 1050 | unsafe { 1051 | Encoder { groove_encoder: groove_encoder_create() } 1052 | } 1053 | } 1054 | 1055 | /// The desired audio format to encode. 1056 | /// groove_encoder_create defaults these to 44100 Hz, 1057 | /// signed 16-bit int, stereo. 1058 | /// These are preferences; if a setting cannot be used, a substitute will be 1059 | /// used instead. actual_audio_format is set to the actual values. 1060 | pub fn set_target_audio_format(&self, target_audio_format: AudioFormat) { 1061 | unsafe { 1062 | (*self.groove_encoder).target_audio_format = target_audio_format.to_groove(); 1063 | } 1064 | } 1065 | pub fn get_target_audio_format(&self) -> AudioFormat { 1066 | unsafe { 1067 | AudioFormat::from_groove(&(*self.groove_encoder).target_audio_format) 1068 | } 1069 | } 1070 | 1071 | /// Select encoding quality by choosing a target bit rate in bits per 1072 | /// second. Note that typically you see this expressed in "kbps", such 1073 | /// as 320kbps or 128kbps. Surprisingly, in this circumstance 1 kbps is 1074 | /// 1000 bps, *not* 1024 bps as you would expect. 1075 | /// groove_encoder_create defaults this to 256000 1076 | pub fn set_bit_rate(&self, rate: i32) { 1077 | unsafe { 1078 | (*self.groove_encoder).bit_rate = rate; 1079 | } 1080 | } 1081 | pub fn get_bit_rate(&self) -> i32 { 1082 | unsafe { 1083 | (*self.groove_encoder).bit_rate 1084 | } 1085 | } 1086 | 1087 | /// optional - choose a short name for the format 1088 | /// to help libgroove guess which format to use 1089 | /// use `avconv -formats` to get a list of possibilities 1090 | pub fn set_format_short_name(&self, format: &str) { 1091 | let format_c_str = CString::from_slice(format.as_bytes()); 1092 | unsafe { 1093 | (*self.groove_encoder).format_short_name = format_c_str.as_ptr(); 1094 | } 1095 | } 1096 | 1097 | /// optional - choose a short name for the codec 1098 | /// to help libgroove guess which codec to use 1099 | /// use `avconv -codecs` to get a list of possibilities 1100 | pub fn set_codec_short_name(&self, codec: &str) { 1101 | let codec_c_str = CString::from_slice(codec.as_bytes()); 1102 | unsafe { 1103 | (*self.groove_encoder).codec_short_name = codec_c_str.as_ptr(); 1104 | } 1105 | } 1106 | 1107 | /// optional - provide an example filename 1108 | /// to help libgroove guess which format/codec to use 1109 | pub fn set_filename(&self, filename: &str) { 1110 | let filename_c_str = CString::from_slice(filename.as_bytes()); 1111 | unsafe { 1112 | (*self.groove_encoder).filename = filename_c_str.as_ptr(); 1113 | } 1114 | } 1115 | 1116 | /// optional - provide a mime type string 1117 | /// to help libgroove guess which format/codec to use 1118 | pub fn set_mime_type(&self, mime_type: &str) { 1119 | let mime_type_c_str = CString::from_slice(mime_type.as_bytes()); 1120 | unsafe { 1121 | (*self.groove_encoder).mime_type = mime_type_c_str.as_ptr(); 1122 | } 1123 | } 1124 | 1125 | /// set to the actual format you get when you attach to a 1126 | /// playlist. ideally will be the same as target_audio_format but might 1127 | /// not be. 1128 | pub fn get_actual_audio_format(&self) -> AudioFormat { 1129 | unsafe { 1130 | AudioFormat::from_groove(&(*self.groove_encoder).actual_audio_format) 1131 | } 1132 | } 1133 | 1134 | /// see docs for file::metadata_set 1135 | pub fn metadata_set(&self, key: &str, value: &str, case_sensitive: bool) -> Result<(), i32> { 1136 | let flags: c_int = if case_sensitive {TAG_MATCH_CASE} else {0}; 1137 | let c_tag_key = CString::from_slice(key.as_bytes()); 1138 | let c_tag_value = CString::from_slice(value.as_bytes()); 1139 | unsafe { 1140 | let err_code = groove_encoder_metadata_set(self.groove_encoder, c_tag_key.as_ptr(), 1141 | c_tag_value.as_ptr(), flags); 1142 | if err_code >= 0 { 1143 | Result::Ok(()) 1144 | } else { 1145 | Result::Err(err_code as i32) 1146 | } 1147 | } 1148 | } 1149 | 1150 | /// at playlist begin, format headers are generated. when end of playlist is 1151 | /// reached, format trailers are generated. 1152 | pub fn attach(&self, playlist: &Playlist) -> Result<(), i32> { 1153 | unsafe { 1154 | let err_code = groove_encoder_attach(self.groove_encoder, playlist.groove_playlist); 1155 | if err_code >= 0 { 1156 | Result::Ok(()) 1157 | } else { 1158 | Result::Err(err_code as i32) 1159 | } 1160 | } 1161 | } 1162 | 1163 | pub fn detach(&self) { 1164 | unsafe { 1165 | let _ = groove_encoder_detach(self.groove_encoder); 1166 | } 1167 | } 1168 | 1169 | /// returns None on end of playlist, Some when there is a buffer 1170 | /// blocks the thread until a buffer or end is found 1171 | pub fn buffer_get_blocking(&self) -> Option { 1172 | unsafe { 1173 | let mut buffer: *mut GrooveBuffer = std::ptr::null_mut(); 1174 | match groove_encoder_buffer_get(self.groove_encoder, &mut buffer, 1) { 1175 | BUFFER_NO => panic!("did not expect BUFFER_NO when blocking"), 1176 | BUFFER_YES => Option::Some(EncodedBuffer { groove_buffer: buffer }), 1177 | BUFFER_END => Option::None, 1178 | _ => panic!("unexpected buffer result"), 1179 | } 1180 | } 1181 | } 1182 | } 1183 | 1184 | /// Call at the end of your program to clean up. After calling this you may no 1185 | /// longer use this API. You may choose to never call this function, in which 1186 | /// case the worst thing that can happen is valgrind may report a memory leak. 1187 | pub fn finish() { 1188 | init(); 1189 | unsafe { groove_finish() } 1190 | } 1191 | 1192 | /// enable/disable logging of errors 1193 | pub fn set_logging(level: Log) { 1194 | init(); 1195 | let c_level: c_int = match level { 1196 | Log::Quiet => -8, 1197 | Log::Error => 16, 1198 | Log::Warning => 24, 1199 | Log::Info => 32, 1200 | }; 1201 | unsafe { groove_set_logging(c_level) } 1202 | } 1203 | 1204 | pub fn version_major() -> i32 { 1205 | unsafe { groove_version_major() } 1206 | } 1207 | 1208 | pub fn version_minor() -> i32 { 1209 | unsafe { groove_version_minor() } 1210 | } 1211 | 1212 | pub fn version_patch() -> i32 { 1213 | unsafe { groove_version_patch() } 1214 | } 1215 | 1216 | /// get a string which represents the version number of libgroove 1217 | pub fn version() -> &'static str { 1218 | unsafe { 1219 | let version = groove_version(); 1220 | let slice = std::ffi::c_str_to_bytes(&version); 1221 | std::mem::transmute::<&str, &'static str>(std::str::from_utf8(slice).unwrap()) 1222 | } 1223 | } 1224 | 1225 | const TAG_MATCH_CASE: c_int = 1; 1226 | 1227 | const BUFFER_NO: c_int = 0; 1228 | const BUFFER_YES: c_int = 1; 1229 | const BUFFER_END: c_int = 2; 1230 | 1231 | trait Destroy { 1232 | fn destroy(&self); 1233 | } 1234 | 1235 | struct PointerReferenceCounter + Eq> { 1236 | map: HashMap, 1237 | } 1238 | 1239 | impl + Eq> PointerReferenceCounter

{ 1240 | fn new() -> Self { 1241 | PointerReferenceCounter { 1242 | map: HashMap::new(), 1243 | } 1244 | } 1245 | fn incr(&mut self, ptr: P) { 1246 | let rc = match self.map.get(&ptr) { 1247 | Option::Some(rc) => *rc, 1248 | Option::None => 0, 1249 | }; 1250 | self.map.insert(ptr, rc + 1); 1251 | } 1252 | fn decr(&mut self, ptr: P) { 1253 | let count = *self.map.get(&ptr).expect("too many dereferences"); 1254 | if count == 1 { 1255 | self.map.remove(&ptr); 1256 | ptr.destroy(); 1257 | } else { 1258 | self.map.insert(ptr, count - 1); 1259 | } 1260 | } 1261 | } 1262 | --------------------------------------------------------------------------------