3 extern crate nihav_core;
4 extern crate nihav_registry;
7 use std::io::prelude::*;
8 use std::io::BufReader;
10 use std::time::Duration;
12 use sdl2_sys::{SDL_AudioSpec, Uint32};
13 use nihav_core::io::byteio::{FileReader, ByteReader};
14 use nihav_core::frame::*;
15 use nihav_core::codecs::*;
16 use nihav_core::demuxers::*;
17 use nihav_core::soundcvt::*;
27 full_reg: allreg::FullRegister,
36 device_id: sdl2_sys::SDL_AudioDeviceID
40 fn open(arate: u32, channels: u8) -> Option<(Self, SDL_AudioSpec)> {
41 let desired_spec = SDL_AudioSpec {
43 format: sdl2_sys::AUDIO_S16 as u16,
50 userdata: std::ptr::null_mut(),
52 let mut dspec = desired_spec;
53 let device_id = unsafe { sdl2_sys::SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_spec, &mut dspec, 0) };
55 Some((AudioDevice { device_id }, dspec))
61 unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 1); }
64 unsafe { sdl2_sys::SDL_PauseAudioDevice(self.device_id, 0); }
67 unsafe { sdl2_sys::SDL_ClearQueuedAudio(self.device_id); }
69 fn queue(&self, buf: &[i16]) {
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);
76 fn queue_bytes(&self, buf: &[u8]) {
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);
83 fn size(&self) -> u32 {
84 unsafe { sdl2_sys::SDL_GetQueuedAudioSize(self.device_id) }
88 impl Drop for AudioDevice {
90 unsafe { sdl2_sys::SDL_CloseAudioDevice(self.device_id); }
95 demuxer: DemuxerObject<'a>,
96 decoder: Box<dyn NADecoder>,
97 dsupp: Box<NADecoderSupport>,
98 buf: &'a mut Vec<i16>,
100 dst_info: NAAudioInfo,
101 dst_chmap: NAChannelMap,
108 fn output_vol_i16(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[i16], mute: bool, volume: u8) {
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;
115 tmp.push(nsamp.min(32767).max(-32768) as i16);
119 tmp.resize(src.len(), 0);
124 fn output_vol_u8(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[u8], mute: bool, volume: u8) {
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;
132 tmp.push(nsamp.min(32767).max(-32768) as i16);
136 tmp.resize(src.len() / 2, 0);
141 impl<'a> Decoder<'a> {
142 fn refill(&mut self, device: &AudioDevice) -> bool {
144 match self.demuxer.get_frame() {
146 if pkt.get_stream().get_num() == self.stream_no {
147 match self.decoder.decode(&mut self.dsupp, &pkt) {
149 let buf = frm.get_buffer();
150 if let NABufferType::None = buf {
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);
156 if buf.get_audio_length() == 0 {
159 let out_buf = convert_audio_frame(&buf, &self.dst_info, &self.dst_chmap).unwrap();
161 NABufferType::AudioI16(abuf) => {
162 if !self.mute && self.volume == 100 {
163 device.queue(&abuf.get_data());
165 output_vol_i16(device, self.buf, &abuf.get_data(), self.mute, self.volume);
167 self.samplepos += abuf.get_length() as u64;
169 NABufferType::AudioPacked(abuf) => {
170 if !self.mute && self.volume == 100 {
171 device.queue_bytes(&abuf.get_data());
173 output_vol_u8(device, self.buf, &abuf.get_data(), self.mute, self.volume);
175 self.samplepos += abuf.get_length() as u64;
177 _ => println!("unknown buffer type"),
182 println!(" error decoding {:?}", err);
188 Err(DemuxerError::EOF) => return true,
190 println!("demuxing error {:?}", err);
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"); }
199 self.decoder.flush();
204 fn format_time(ms: u64) -> String {
206 let ds = (ms % 1000) / 100;
207 let (min, s) = (s / 60, s % 60);
208 let (h, min) = (min / 60, min % 60);
211 format!("{}.{}", s, ds)
213 format!("{}:{:02}.{}", min, s, ds)
216 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
222 let full_reg = allreg::FullRegister::new();
225 if sdl2_sys::SDL_Init(sdl2_sys::SDL_INIT_AUDIO) != 0 {
226 panic!("cannot init SDL");
240 fn play_file(&mut self, name: &str, cmd_receiver: &mpsc::Receiver<Command>, start_time: NATimePoint) {
241 let ret = File::open(name);
243 println!("error opening {}", name);
246 let file = ret.unwrap();
247 let file = BufReader::new(file);
249 let mut fr = FileReader::new_read(file);
250 let mut br = ByteReader::new(&mut fr);
251 if br.peek_byte().is_err() {
252 println!("Cannot read {}", name);
255 let (is_raw, start, end) = detect_tags(&mut br);
257 let mut nfr: Box<dyn ByteIO>;
258 if start != 0 || end.is_some() {
259 //println!(" limiting range to {:X}-{:X}", start, end.unwrap_or(0));
260 let file = fr.finish();
261 nfr = Box::new(BoundedFileReader::new_read(file, start, end).unwrap());
265 let mut br = ByteReader::new(nfr.as_mut());
266 let dmx = DemuxerObject::create(&mut br, &self.full_reg, name, is_raw);
268 println!("No demuxer found!");
272 let mut ainfo = None;
273 let mut dec: Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)> = None;
274 let mut stream_no = 0;
275 let mut duration = dmx.get_duration();
276 for i in 0..dmx.get_num_streams() {
277 let s = dmx.get_stream(i).unwrap();
278 let info = s.get_info();
280 let decfunc = self.full_reg.dec_reg.find_decoder(info.get_name());
281 if decfunc.is_none() {
282 println!("no decoder for {}", info.get_name());
285 let mut decoder = (decfunc.unwrap())();
286 let mut dsupp = Box::new(NADecoderSupport::new());
287 if decoder.init(&mut dsupp, info.clone()).is_err() {
288 println!("cannot init decoder for stream {}", i);
291 dec = Some((dsupp, decoder));
295 duration = NATimeInfo::ts_to_time(s.duration, 1000, s.tb_num, s.tb_den);
301 println!("no audio decoder found");
304 let (dsupp, decoder) = dec.unwrap();
306 let info = ainfo.unwrap();
307 let ainfo = info.get_properties().get_audio_info().unwrap();
308 let sbr_hack = info.get_name() == "aac" && ainfo.sample_rate < 32000;
309 let arate = if ainfo.sample_rate > 0 {
313 ainfo.sample_rate * 2
316 let ch = ainfo.channels;
318 println!("Playing {} [{}Hz {}ch]", name, arate, ch);
319 let ret = AudioDevice::open(arate, ch.min(2));
321 println!("cannot open output");
324 let (device, dspec) = ret.unwrap();
325 let block_limit = dmx.get_stream(stream_no).unwrap().tb_num * arate / dmx.get_stream(stream_no).unwrap().tb_den * u32::from(dspec.channels);
327 let dst_info = NAAudioInfo { sample_rate: dspec.freq as u32, channels: dspec.channels, format: SND_S16_FORMAT, block_len: 1 };
328 let dst_chmap = if dst_info.channels == 2 {
329 NAChannelMap::from_str("L,R").unwrap()
331 NAChannelMap::from_str("C").unwrap()
333 let mut decoder = Decoder {
344 let mut refill_limit = arate * u32::from(dspec.channels);
345 let underfill_limit = (arate * u32::from(dspec.channels) / 4).max(block_limit);
347 if start_time != NATimePoint::None {
348 let _ret = decoder.demuxer.seek(start_time);
351 let mut eof = decoder.refill(&device);
352 while !eof && device.size() < refill_limit {
353 eof = decoder.refill(&device);
356 let duration_str = if duration != 0 { format_time(duration) } else { "???".to_owned() };
360 let mut no_display = false;
362 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
363 let full_ms = cur_time * 1000 / u64::from(arate);
364 let timestr = format_time(full_ms);
365 let disp_vol = if self.mute { 0 } else { self.volume };
368 print!("> {} / {} {}% \r", timestr, duration_str, disp_vol);
370 print!("> {} / {} |{}| {}% \r", timestr, duration_str, device.size(), disp_vol);
373 std::io::stdout().flush().unwrap();
374 if device.size() < underfill_limit && !self.paused && (refill_limit < (1 << 20)) & !eof {
376 println!("underrun!");
378 refill_limit += refill_limit >> 1;
380 if device.size() < refill_limit / 2 {
381 while !eof && device.size() < refill_limit {
382 eof = decoder.refill(&device);
385 if eof && device.size() == 0 {
388 while let Ok(cmd) = cmd_receiver.try_recv() {
389 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
391 Command::Forward(val) => {
394 let seekoff = match val {
399 let seek_time = cur_time * 1000 / u64::from(arate) + seekoff * 1000;
400 let _ret = decoder.seek(seek_time);
401 while !eof && device.size() < refill_limit {
402 eof = decoder.refill(&device);
411 Command::Back(val) => {
414 let seekoff = match val {
419 let seek_time = (cur_time * 1000 / u64::from(arate)).saturating_sub(seekoff * 1000);
420 let _ret = decoder.seek(seek_time);
421 while !eof && device.size() < refill_limit {
422 eof = decoder.refill(&device);
431 Command::Seek(seek_time) => {
434 let _ret = decoder.seek(seek_time);
435 while !eof && device.size() < refill_limit {
436 eof = decoder.refill(&device);
457 let _ret = decoder.seek(0);
458 while !eof && device.size() < refill_limit {
459 eof = decoder.refill(&device);
469 self.paused = !self.paused;
476 Command::VolumeUp => {
477 self.volume = (self.volume + 10).min(200);
478 decoder.volume = self.volume;
480 Command::VolumeDown => {
481 self.volume = self.volume.saturating_sub(10);
482 decoder.volume = self.volume;
485 self.mute = !self.mute;
486 decoder.mute = self.mute;
489 self.debug = !self.debug;
491 Command::PauseDisplay => {
497 Command::ResumeDisplay => {
505 print!("\r{:60}\r", ' ');
508 thread::sleep(Duration::from_millis(200));
516 let args: Vec<String> = std::env::args().collect();
518 let cmd_state = CmdLineState::new();
520 let (cmd_reader_thread, cmd_receiver) = start_reader();
521 let mut player = Player::new();
524 println!("usage: nihav-sndplay file1 file2 ...");
528 if args[1] == "--help" {
529 println!("usage: nihav-sndplay file1 file2 ...");
530 println!("commands:");
531 println!(" escape / q - quit");
532 println!(" space - pause / resume playback");
533 println!(" enter / end - play next file");
534 println!(" home - play current track from the beginning");
535 println!(" left / right - seek 10 seconds forward / back");
536 println!(" up / down - seek 1 minute forward / back");
537 println!(" pgup / pgdn - seek 10 minutes forward / back");
538 println!(" + / - - increase / decrease volume");
539 println!(" m - mute output");
543 let mut aiter = args[1..].iter();
544 let mut start_time = NATimePoint::None;
545 while let Some(arg) = aiter.next() {
548 if let Some(arg) = aiter.next() {
549 if let Ok(val) = arg.parse() {
552 println!("invalid time");
555 println!("argument is required");
559 player.play_file(arg, &cmd_receiver, start_time);
563 start_time = NATimePoint::None;
569 drop(cmd_reader_thread);
570 unsafe { sdl2_sys::SDL_Quit(); }