]>
| Commit | Line | Data |
|---|---|---|
| b043bd0a KS |
1 | extern crate libc; |
| 2 | extern crate sdl2_sys; | |
| 3 | extern crate nihav_core; | |
| 4 | extern crate nihav_registry; | |
| b043bd0a KS |
5 | |
| 6 | use std::fs::File; | |
| 7 | use std::io::prelude::*; | |
| e08bbe03 | 8 | use std::io::BufReader; |
| b043bd0a KS |
9 | use std::sync::mpsc; |
| 10 | use std::time::Duration; | |
| 11 | use std::thread; | |
| 12 | use sdl2_sys::{SDL_AudioSpec, Uint32}; | |
| 7d46fb05 | 13 | use nihav_core::io::byteio::{ByteIO,FileReader}; |
| b043bd0a KS |
14 | use nihav_core::frame::*; |
| 15 | use nihav_core::codecs::*; | |
| 16 | use nihav_core::demuxers::*; | |
| 17 | use nihav_core::soundcvt::*; | |
| b043bd0a | 18 | |
| 41e41352 | 19 | mod allreg; |
| b043bd0a KS |
20 | mod command; |
| 21 | use command::*; | |
| e08bbe03 KS |
22 | mod demux; |
| 23 | use demux::*; | |
| b043bd0a KS |
24 | |
| 25 | struct Player { | |
| 26 | ended: bool, | |
| e08bbe03 | 27 | full_reg: allreg::FullRegister, |
| b043bd0a KS |
28 | paused: bool, |
| 29 | mute: bool, | |
| 30 | volume: u8, | |
| 31 | debug: bool, | |
| 32 | buf: Vec<i16>, | |
| 33 | } | |
| 34 | ||
| 35 | struct AudioDevice { | |
| 36 | device_id: sdl2_sys::SDL_AudioDeviceID | |
| 37 | } | |
| 38 | ||
| 39 | impl AudioDevice { | |
| 40 | fn open(arate: u32, channels: u8) -> Option<(Self, SDL_AudioSpec)> { | |
| 41 | let desired_spec = SDL_AudioSpec { | |
| 42 | freq: arate as i32, | |
| 43 | format: sdl2_sys::AUDIO_S16 as u16, | |
| 44 | channels, | |
| 45 | silence: 0, | |
| 46 | samples: 0, | |
| 47 | padding: 0, | |
| 48 | size: 0, | |
| 49 | callback: None, | |
| 50 | userdata: std::ptr::null_mut(), | |
| 51 | }; | |
| 52 | let mut dspec = desired_spec; | |
| 53 | let device_id = unsafe { sdl2_sys::SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_spec, &mut dspec, 0) }; | |
| 54 | if device_id != 0 { | |
| 55 | Some((AudioDevice { device_id }, dspec)) | |
| 56 | } else { | |
| 57 | None | |
| 58 | } | |
| 59 | } | |
| 60 | fn pause(&self) { | |
| 61 | unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 1); } | |
| 62 | } | |
| 63 | fn resume(&self) { | |
| 64 | unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 0); } | |
| 65 | } | |
| 66 | fn clear(&self) { | |
| 67 | unsafe { sdl2_sys::SDL_ClearQueuedAudio(self.device_id); } | |
| 68 | } | |
| 69 | fn queue(&self, buf: &[i16]) { | |
| 70 | unsafe { | |
| 71 | let len = buf.len() * 2; | |
| 72 | let buf_ptr = buf.as_ptr(); | |
| 73 | sdl2_sys::SDL_QueueAudio(self.device_id, buf_ptr as *const core::ffi::c_void, len as Uint32); | |
| 74 | } | |
| 75 | } | |
| 76 | fn queue_bytes(&self, buf: &[u8]) { | |
| 77 | unsafe { | |
| 78 | let len = buf.len(); | |
| 79 | let buf_ptr = buf.as_ptr(); | |
| 80 | sdl2_sys::SDL_QueueAudio(self.device_id, buf_ptr as *const core::ffi::c_void, len as Uint32); | |
| 81 | } | |
| 82 | } | |
| 83 | fn size(&self) -> u32 { | |
| 84 | unsafe { sdl2_sys::SDL_GetQueuedAudioSize(self.device_id) } | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 65606213 KS |
88 | impl Drop for AudioDevice { |
| 89 | fn drop(&mut self) { | |
| 90 | unsafe { sdl2_sys::SDL_CloseAudioDevice(self.device_id); } | |
| 91 | } | |
| 92 | } | |
| 93 | ||
| b043bd0a | 94 | struct Decoder<'a> { |
| e08bbe03 | 95 | demuxer: DemuxerObject<'a>, |
| b043bd0a KS |
96 | decoder: Box<dyn NADecoder>, |
| 97 | dsupp: Box<NADecoderSupport>, | |
| 98 | buf: &'a mut Vec<i16>, | |
| 99 | stream_no: usize, | |
| 100 | dst_info: NAAudioInfo, | |
| 101 | dst_chmap: NAChannelMap, | |
| 102 | samplepos: u64, | |
| 103 | arate: u32, | |
| 104 | volume: u8, | |
| 105 | mute: bool, | |
| 106 | } | |
| 107 | ||
| 108 | fn output_vol_i16(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[i16], mute: bool, volume: u8) { | |
| 109 | if !mute { | |
| b5053bfc | 110 | tmp.clear(); |
| b043bd0a KS |
111 | tmp.reserve(src.len()); |
| 112 | let vol = i32::from(volume); | |
| 113 | for &sample in src.iter() { | |
| 114 | let nsamp = vol * i32::from(sample) / 100; | |
| b575a912 | 115 | tmp.push(nsamp.clamp(-32768, 32767) as i16); |
| b043bd0a KS |
116 | } |
| 117 | } else { | |
| b5053bfc | 118 | tmp.clear(); |
| b043bd0a KS |
119 | tmp.resize(src.len(), 0); |
| 120 | } | |
| 9d321987 | 121 | device.queue(tmp); |
| b043bd0a KS |
122 | } |
| 123 | ||
| 124 | fn output_vol_u8(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[u8], mute: bool, volume: u8) { | |
| 125 | if !mute { | |
| b5053bfc | 126 | tmp.clear(); |
| b043bd0a KS |
127 | tmp.reserve(src.len()); |
| 128 | let vol = i32::from(volume); | |
| 129 | for sample in src.chunks_exact(2) { | |
| 130 | let sample = (u16::from(sample[0]) + u16::from(sample[1]) * 256) as i16; | |
| 131 | let nsamp = vol * i32::from(sample) / 100; | |
| b575a912 | 132 | tmp.push(nsamp.clamp(-32768, 32767) as i16); |
| b043bd0a KS |
133 | } |
| 134 | } else { | |
| b5053bfc | 135 | tmp.clear(); |
| b043bd0a KS |
136 | tmp.resize(src.len() / 2, 0); |
| 137 | } | |
| 9d321987 | 138 | device.queue(tmp); |
| b043bd0a KS |
139 | } |
| 140 | ||
| 141 | impl<'a> Decoder<'a> { | |
| 142 | fn refill(&mut self, device: &AudioDevice) -> bool { | |
| 143 | loop { | |
| 144 | match self.demuxer.get_frame() { | |
| 145 | Ok(pkt) => { | |
| 146 | if pkt.get_stream().get_num() == self.stream_no { | |
| 147 | match self.decoder.decode(&mut self.dsupp, &pkt) { | |
| 148 | Ok(frm) => { | |
| 149 | let buf = frm.get_buffer(); | |
| 89f53245 KS |
150 | if let NABufferType::None = buf { |
| 151 | return false; | |
| 152 | } | |
| b043bd0a KS |
153 | if let Some(pts) = frm.ts.get_pts() { |
| 154 | self.samplepos = NATimeInfo::ts_to_time(pts, u64::from(self.arate), frm.ts.tb_num, frm.ts.tb_den); | |
| 155 | } | |
| 0a70659f KS |
156 | if buf.get_audio_length() == 0 { |
| 157 | return false; | |
| 158 | } | |
| b043bd0a KS |
159 | let out_buf = convert_audio_frame(&buf, &self.dst_info, &self.dst_chmap).unwrap(); |
| 160 | match out_buf { | |
| 161 | NABufferType::AudioI16(abuf) => { | |
| 162 | if !self.mute && self.volume == 100 { | |
| 9d321987 | 163 | device.queue(abuf.get_data()); |
| b043bd0a | 164 | } else { |
| 9d321987 | 165 | output_vol_i16(device, self.buf, abuf.get_data(), self.mute, self.volume); |
| b043bd0a KS |
166 | } |
| 167 | self.samplepos += abuf.get_length() as u64; | |
| 168 | }, | |
| 169 | NABufferType::AudioPacked(abuf) => { | |
| 170 | if !self.mute && self.volume == 100 { | |
| 9d321987 | 171 | device.queue_bytes(abuf.get_data()); |
| b043bd0a | 172 | } else { |
| 9d321987 | 173 | output_vol_u8(device, self.buf, abuf.get_data(), self.mute, self.volume); |
| b043bd0a KS |
174 | } |
| 175 | self.samplepos += abuf.get_length() as u64; | |
| 176 | }, | |
| 177 | _ => println!("unknown buffer type"), | |
| 178 | }; | |
| 179 | return false; | |
| 180 | }, | |
| 181 | Err(err) => { | |
| 182 | println!(" error decoding {:?}", err); | |
| 183 | return true; | |
| 184 | }, | |
| 185 | }; | |
| 186 | } | |
| 187 | }, | |
| 188 | Err(DemuxerError::EOF) => return true, | |
| 189 | Err(err) => { | |
| 190 | println!("demuxing error {:?}", err); | |
| 191 | return true; | |
| 192 | }, | |
| 193 | }; | |
| 194 | } | |
| 195 | } | |
| 196 | fn seek(&mut self, time: u64) -> bool { | |
| 197 | let ret = self.demuxer.seek(NATimePoint::Milliseconds(time)); | |
| 198 | if ret.is_err() { println!(" seek error\n"); } | |
| 3ab53787 | 199 | self.decoder.flush(); |
| b043bd0a KS |
200 | ret.is_ok() |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | fn format_time(ms: u64) -> String { | |
| 205 | let s = ms / 1000; | |
| 206 | let ds = (ms % 1000) / 100; | |
| 207 | let (min, s) = (s / 60, s % 60); | |
| be48734e KS |
208 | let (h, min) = (min / 60, min % 60); |
| 209 | if h == 0 { | |
| 210 | if min == 0 { | |
| 211 | format!("{}.{}", s, ds) | |
| 212 | } else { | |
| 213 | format!("{}:{:02}.{}", min, s, ds) | |
| 214 | } | |
| b043bd0a | 215 | } else { |
| be48734e | 216 | format!("{}:{:02}:{:02}.{}", h, min, s, ds) |
| b043bd0a KS |
217 | } |
| 218 | } | |
| 219 | ||
| 220 | impl Player { | |
| 221 | fn new() -> Self { | |
| e08bbe03 | 222 | let full_reg = allreg::FullRegister::new(); |
| b043bd0a KS |
223 | |
| 224 | unsafe { | |
| 225 | if sdl2_sys::SDL_Init(sdl2_sys::SDL_INIT_AUDIO) != 0 { | |
| 226 | panic!("cannot init SDL"); | |
| 227 | } | |
| 228 | } | |
| 229 | ||
| 230 | Self { | |
| 231 | ended: false, | |
| 232 | paused: false, | |
| e08bbe03 | 233 | full_reg, |
| b043bd0a KS |
234 | volume: 100, |
| 235 | mute: false, | |
| 236 | debug: false, | |
| 237 | buf: Vec::new(), | |
| 238 | } | |
| 239 | } | |
| 36ac48e5 | 240 | fn play_file(&mut self, name: &str, cmd_receiver: &mpsc::Receiver<Command>, start_time: NATimePoint) { |
| b043bd0a KS |
241 | let ret = File::open(name); |
| 242 | if ret.is_err() { | |
| 243 | println!("error opening {}", name); | |
| 244 | return; | |
| 245 | } | |
| e08bbe03 KS |
246 | let file = ret.unwrap(); |
| 247 | let file = BufReader::new(file); | |
| b043bd0a | 248 | |
| e08bbe03 | 249 | let mut fr = FileReader::new_read(file); |
| 7d46fb05 | 250 | if fr.peek_byte().is_err() { |
| b2d3e848 KS |
251 | println!("Cannot read {}", name); |
| 252 | return; | |
| 253 | } | |
| 7d46fb05 | 254 | let (is_raw, start, end) = detect_tags(&mut fr); |
| b043bd0a | 255 | |
| 7d46fb05 | 256 | let mut br: Box<dyn ByteIO>; |
| e08bbe03 KS |
257 | if start != 0 || end.is_some() { |
| 258 | //println!(" limiting range to {:X}-{:X}", start, end.unwrap_or(0)); | |
| 259 | let file = fr.finish(); | |
| 7d46fb05 | 260 | br = Box::new(BoundedFileReader::new_read(file, start, end).unwrap()); |
| e08bbe03 | 261 | } else { |
| 7d46fb05 | 262 | br = Box::new(fr); |
| e08bbe03 | 263 | } |
| 7d46fb05 | 264 | let dmx = DemuxerObject::create(br.as_mut(), &self.full_reg, name, is_raw); |
| e08bbe03 KS |
265 | if dmx.is_none() { |
| 266 | println!("No demuxer found!"); | |
| b043bd0a KS |
267 | return; |
| 268 | } | |
| b043bd0a KS |
269 | |
| 270 | let mut ainfo = None; | |
| 271 | let mut dec: Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)> = None; | |
| 272 | let mut stream_no = 0; | |
| 273 | let mut duration = dmx.get_duration(); | |
| 274 | for i in 0..dmx.get_num_streams() { | |
| 275 | let s = dmx.get_stream(i).unwrap(); | |
| 276 | let info = s.get_info(); | |
| 277 | if info.is_audio() { | |
| e08bbe03 | 278 | let decfunc = self.full_reg.dec_reg.find_decoder(info.get_name()); |
| b043bd0a KS |
279 | if decfunc.is_none() { |
| 280 | println!("no decoder for {}", info.get_name()); | |
| 281 | continue; | |
| 282 | } | |
| 283 | let mut decoder = (decfunc.unwrap())(); | |
| 284 | let mut dsupp = Box::new(NADecoderSupport::new()); | |
| 285 | if decoder.init(&mut dsupp, info.clone()).is_err() { | |
| 286 | println!("cannot init decoder for stream {}", i); | |
| 287 | continue; | |
| 288 | } | |
| 289 | dec = Some((dsupp, decoder)); | |
| 290 | ainfo = Some(info); | |
| 291 | stream_no = i; | |
| 292 | if s.duration > 0 { | |
| 293 | duration = NATimeInfo::ts_to_time(s.duration, 1000, s.tb_num, s.tb_den); | |
| 294 | } | |
| 295 | break; | |
| 296 | } | |
| 297 | } | |
| 298 | if dec.is_none() { | |
| 299 | println!("no audio decoder found"); | |
| 300 | return; | |
| 301 | } | |
| 302 | let (dsupp, decoder) = dec.unwrap(); | |
| 303 | ||
| 7b7b426d KS |
304 | let info = ainfo.unwrap(); |
| 305 | let ainfo = info.get_properties().get_audio_info().unwrap(); | |
| 306 | let sbr_hack = info.get_name() == "aac" && ainfo.sample_rate < 32000; | |
| 307 | let arate = if ainfo.sample_rate > 0 { | |
| 308 | if !sbr_hack { | |
| 309 | ainfo.sample_rate | |
| 310 | } else { | |
| 311 | ainfo.sample_rate * 2 | |
| 312 | } | |
| 313 | } else { 44100 }; | |
| b043bd0a KS |
314 | let ch = ainfo.channels; |
| 315 | ||
| 316 | println!("Playing {} [{}Hz {}ch]", name, arate, ch); | |
| 5a92ee55 | 317 | let ret = AudioDevice::open(arate, ch.min(2)); |
| b043bd0a KS |
318 | if ret.is_none() { |
| 319 | println!("cannot open output"); | |
| 320 | return; | |
| 321 | } | |
| 322 | let (device, dspec) = ret.unwrap(); | |
| 323 | let block_limit = dmx.get_stream(stream_no).unwrap().tb_num * arate / dmx.get_stream(stream_no).unwrap().tb_den * u32::from(dspec.channels); | |
| 324 | ||
| 325 | let dst_info = NAAudioInfo { sample_rate: dspec.freq as u32, channels: dspec.channels, format: SND_S16_FORMAT, block_len: 1 }; | |
| 326 | let dst_chmap = if dst_info.channels == 2 { | |
| 327 | NAChannelMap::from_str("L,R").unwrap() | |
| 328 | } else { | |
| 329 | NAChannelMap::from_str("C").unwrap() | |
| 330 | }; | |
| 331 | let mut decoder = Decoder { | |
| 332 | demuxer: dmx, | |
| 333 | decoder, dsupp, | |
| 334 | stream_no, | |
| 335 | dst_info, dst_chmap, | |
| 336 | samplepos: 0, | |
| 337 | arate, | |
| 338 | volume: self.volume, | |
| 339 | mute: self.mute, | |
| 340 | buf: &mut self.buf, | |
| 341 | }; | |
| 342 | let mut refill_limit = arate * u32::from(dspec.channels); | |
| 343 | let underfill_limit = (arate * u32::from(dspec.channels) / 4).max(block_limit); | |
| 344 | ||
| 36ac48e5 KS |
345 | if start_time != NATimePoint::None { |
| 346 | let _ret = decoder.demuxer.seek(start_time); | |
| 347 | } | |
| 348 | ||
| b043bd0a KS |
349 | let mut eof = decoder.refill(&device); |
| 350 | while !eof && device.size() < refill_limit { | |
| 351 | eof = decoder.refill(&device); | |
| 352 | } | |
| 353 | ||
| 354 | let duration_str = if duration != 0 { format_time(duration) } else { "???".to_owned() }; | |
| 355 | if !self.paused { | |
| 356 | device.resume(); | |
| 357 | } | |
| 0a727e05 | 358 | let mut no_display = false; |
| b043bd0a KS |
359 | 'main: loop { |
| 360 | let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels))); | |
| 361 | let full_ms = cur_time * 1000 / u64::from(arate); | |
| 362 | let timestr = format_time(full_ms); | |
| 363 | let disp_vol = if self.mute { 0 } else { self.volume }; | |
| 0a727e05 KS |
364 | if !no_display { |
| 365 | if !self.debug { | |
| 366 | print!("> {} / {} {}% \r", timestr, duration_str, disp_vol); | |
| 367 | } else { | |
| 368 | print!("> {} / {} |{}| {}% \r", timestr, duration_str, device.size(), disp_vol); | |
| 369 | } | |
| b043bd0a KS |
370 | } |
| 371 | std::io::stdout().flush().unwrap(); | |
| f8b39df7 | 372 | if device.size() < underfill_limit && !self.paused && (refill_limit < (1 << 20)) & !eof { |
| b043bd0a KS |
373 | if full_ms > 5000 { |
| 374 | println!("underrun!"); | |
| 375 | } | |
| 376 | refill_limit += refill_limit >> 1; | |
| 377 | } | |
| 378 | if device.size() < refill_limit / 2 { | |
| 379 | while !eof && device.size() < refill_limit { | |
| 380 | eof = decoder.refill(&device); | |
| 381 | } | |
| 382 | } | |
| 383 | if eof && device.size() == 0 { | |
| 384 | break 'main; | |
| 385 | } | |
| 386 | while let Ok(cmd) = cmd_receiver.try_recv() { | |
| 387 | let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels))); | |
| 388 | match cmd { | |
| 389 | Command::Forward(val) => { | |
| 390 | device.pause(); | |
| 391 | device.clear(); | |
| 392 | let seekoff = match val { | |
| 393 | 1 => 10, | |
| 394 | 2 => 60, | |
| 395 | _ => 10 * 60, | |
| 396 | }; | |
| 397 | let seek_time = cur_time * 1000 / u64::from(arate) + seekoff * 1000; | |
| 398 | let _ret = decoder.seek(seek_time); | |
| 399 | while !eof && device.size() < refill_limit { | |
| 400 | eof = decoder.refill(&device); | |
| 401 | } | |
| 402 | if eof { | |
| 403 | break 'main; | |
| 404 | } | |
| 405 | if !self.paused { | |
| 406 | device.resume(); | |
| 407 | } | |
| 408 | }, | |
| 409 | Command::Back(val) => { | |
| 410 | device.pause(); | |
| 411 | device.clear(); | |
| 412 | let seekoff = match val { | |
| 413 | 1 => 10, | |
| 414 | 2 => 60, | |
| 415 | _ => 10 * 60, | |
| 416 | }; | |
| 417 | let seek_time = (cur_time * 1000 / u64::from(arate)).saturating_sub(seekoff * 1000); | |
| 418 | let _ret = decoder.seek(seek_time); | |
| 419 | while !eof && device.size() < refill_limit { | |
| 420 | eof = decoder.refill(&device); | |
| 421 | } | |
| 422 | if eof { | |
| 423 | break 'main; | |
| 424 | } | |
| 425 | if !self.paused { | |
| 426 | device.resume(); | |
| 427 | } | |
| 428 | }, | |
| 0a727e05 KS |
429 | Command::Seek(seek_time) => { |
| 430 | device.pause(); | |
| 431 | device.clear(); | |
| 432 | let _ret = decoder.seek(seek_time); | |
| 433 | while !eof && device.size() < refill_limit { | |
| 434 | eof = decoder.refill(&device); | |
| 435 | } | |
| 436 | if eof { | |
| 437 | break 'main; | |
| 438 | } | |
| 439 | if !self.paused { | |
| 440 | device.resume(); | |
| 441 | } | |
| 442 | }, | |
| b043bd0a KS |
443 | Command::Quit => { |
| 444 | device.pause(); | |
| 445 | self.ended = true; | |
| 446 | break 'main; | |
| 447 | }, | |
| 448 | Command::Next => { | |
| 449 | device.pause(); | |
| 450 | break 'main; | |
| 451 | }, | |
| 452 | Command::Repeat => { | |
| 453 | device.pause(); | |
| 454 | device.clear(); | |
| 455 | let _ret = decoder.seek(0); | |
| 456 | while !eof && device.size() < refill_limit { | |
| 457 | eof = decoder.refill(&device); | |
| 458 | } | |
| 459 | if eof { | |
| 460 | break 'main; | |
| 461 | } | |
| 462 | if !self.paused { | |
| 463 | device.resume(); | |
| 464 | } | |
| 465 | }, | |
| 466 | Command::Pause => { | |
| 467 | self.paused = !self.paused; | |
| 468 | if self.paused { | |
| 469 | device.pause(); | |
| 470 | } else { | |
| 471 | device.resume(); | |
| 472 | } | |
| 473 | }, | |
| 474 | Command::VolumeUp => { | |
| 475 | self.volume = (self.volume + 10).min(200); | |
| 476 | decoder.volume = self.volume; | |
| 477 | }, | |
| 478 | Command::VolumeDown => { | |
| 479 | self.volume = self.volume.saturating_sub(10); | |
| 480 | decoder.volume = self.volume; | |
| 481 | }, | |
| 482 | Command::Mute => { | |
| 483 | self.mute = !self.mute; | |
| 484 | decoder.mute = self.mute; | |
| 485 | }, | |
| 486 | Command::Debug => { | |
| 487 | self.debug = !self.debug; | |
| 488 | }, | |
| 98431ed5 KS |
489 | Command::PauseDisplay => { |
| 490 | no_display = true; | |
| 491 | if !self.paused { | |
| 492 | device.pause(); | |
| 493 | } | |
| 494 | }, | |
| 495 | Command::ResumeDisplay => { | |
| 496 | no_display = false; | |
| 497 | if !self.paused { | |
| 498 | device.resume(); | |
| 499 | } | |
| 500 | }, | |
| b043bd0a | 501 | }; |
| 0a727e05 KS |
502 | if !no_display { |
| 503 | print!("\r{:60}\r", ' '); | |
| 504 | } | |
| b043bd0a KS |
505 | } |
| 506 | thread::sleep(Duration::from_millis(200)); | |
| 507 | } | |
| 508 | ||
| 509 | println!(); | |
| 510 | } | |
| 511 | } | |
| 512 | ||
| 513 | fn main() { | |
| 514 | let args: Vec<String> = std::env::args().collect(); | |
| 515 | ||
| 516 | let cmd_state = CmdLineState::new(); | |
| 517 | ||
| 518 | let (cmd_reader_thread, cmd_receiver) = start_reader(); | |
| 519 | let mut player = Player::new(); | |
| 520 | ||
| 521 | if args.len() == 1 { | |
| 522 | println!("usage: nihav-sndplay file1 file2 ..."); | |
| 523 | return; | |
| 524 | } | |
| 525 | ||
| 526 | if args[1] == "--help" { | |
| 527 | println!("usage: nihav-sndplay file1 file2 ..."); | |
| 528 | println!("commands:"); | |
| 529 | println!(" escape / q - quit"); | |
| 530 | println!(" space - pause / resume playback"); | |
| 531 | println!(" enter / end - play next file"); | |
| 532 | println!(" home - play current track from the beginning"); | |
| 533 | println!(" left / right - seek 10 seconds forward / back"); | |
| 534 | println!(" up / down - seek 1 minute forward / back"); | |
| 535 | println!(" pgup / pgdn - seek 10 minutes forward / back"); | |
| 536 | println!(" + / - - increase / decrease volume"); | |
| 537 | println!(" m - mute output"); | |
| 538 | return; | |
| 539 | } | |
| 540 | ||
| 36ac48e5 KS |
541 | let mut aiter = args[1..].iter(); |
| 542 | let mut start_time = NATimePoint::None; | |
| 543 | while let Some(arg) = aiter.next() { | |
| 544 | match arg.as_str() { | |
| 545 | "-start" => { | |
| 546 | if let Some(arg) = aiter.next() { | |
| 547 | if let Ok(val) = arg.parse() { | |
| 548 | start_time = val; | |
| 549 | } else { | |
| 550 | println!("invalid time"); | |
| 551 | } | |
| 552 | } else { | |
| 553 | println!("argument is required"); | |
| 554 | } | |
| 555 | }, | |
| b40819ce KS |
556 | "-vol" => { |
| 557 | if let Some(arg) = aiter.next() { | |
| 558 | if let Ok(vol) = arg.parse::<u8>() { | |
| 559 | player.volume = vol.min(200); | |
| 560 | } else { | |
| 561 | println!("wrong volume"); | |
| 562 | } | |
| 563 | } | |
| 564 | }, | |
| 36ac48e5 KS |
565 | _ => { |
| 566 | player.play_file(arg, &cmd_receiver, start_time); | |
| 567 | if player.ended { | |
| 568 | break; | |
| 569 | } | |
| 570 | start_time = NATimePoint::None; | |
| 571 | }, | |
| 572 | }; | |
| b043bd0a KS |
573 | } |
| 574 | cmd_state.restore(); | |
| 575 | ||
| 576 | drop(cmd_reader_thread); | |
| 577 | unsafe { sdl2_sys::SDL_Quit(); } | |
| 578 | } |