sndplay: typo
[nihav-player.git] / sndplay / src / main.rs
CommitLineData
b043bd0a
KS
1extern crate libc;
2extern crate sdl2_sys;
3extern crate nihav_core;
4extern crate nihav_registry;
b043bd0a
KS
5
6use std::fs::File;
7use std::io::prelude::*;
8use std::io::{BufReader, SeekFrom};
9use std::sync::mpsc;
10use std::time::Duration;
11use std::thread;
12use sdl2_sys::{SDL_AudioSpec, Uint32};
13use nihav_core::io::byteio::{FileReader, ByteReader};
14use nihav_core::frame::*;
15use nihav_core::codecs::*;
16use nihav_core::demuxers::*;
17use nihav_core::soundcvt::*;
18use nihav_registry::detect;
b043bd0a 19
41e41352 20mod allreg;
b043bd0a
KS
21mod command;
22use command::*;
23
24struct Player {
25 ended: bool,
26 dmx_reg: RegisteredDemuxers,
27 dec_reg: RegisteredDecoders,
28 paused: bool,
29 mute: bool,
30 volume: u8,
31 debug: bool,
32 buf: Vec<i16>,
33}
34
35struct AudioDevice {
36 device_id: sdl2_sys::SDL_AudioDeviceID
37}
38
39impl 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
88impl Drop for AudioDevice {
89 fn drop(&mut self) {
90 unsafe { sdl2_sys::SDL_CloseAudioDevice(self.device_id); }
91 }
92}
93
b043bd0a
KS
94struct Decoder<'a> {
95 demuxer: Demuxer<'a>,
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
108fn output_vol_i16(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[i16], mute: bool, volume: u8) {
109 if !mute {
110 tmp.truncate(0);
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);
116 }
117 } else {
118 tmp.truncate(0);
119 tmp.resize(src.len(), 0);
120 }
121 device.queue(&tmp);
122}
123
124fn output_vol_u8(device: &AudioDevice, tmp: &mut Vec<i16>, src: &[u8], mute: bool, volume: u8) {
125 if !mute {
126 tmp.truncate(0);
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);
133 }
134 } else {
135 tmp.truncate(0);
136 tmp.resize(src.len() / 2, 0);
137 }
138 device.queue(&tmp);
139}
140
141impl<'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();
150 if let Some(pts) = frm.ts.get_pts() {
151 self.samplepos = NATimeInfo::ts_to_time(pts, u64::from(self.arate), frm.ts.tb_num, frm.ts.tb_den);
152 }
153 let out_buf = convert_audio_frame(&buf, &self.dst_info, &self.dst_chmap).unwrap();
154 match out_buf {
155 NABufferType::AudioI16(abuf) => {
156 if !self.mute && self.volume == 100 {
157 device.queue(&abuf.get_data());
158 } else {
159 output_vol_i16(device, self.buf, &abuf.get_data(), self.mute, self.volume);
160 }
161 self.samplepos += abuf.get_length() as u64;
162 },
163 NABufferType::AudioPacked(abuf) => {
164 if !self.mute && self.volume == 100 {
165 device.queue_bytes(&abuf.get_data());
166 } else {
167 output_vol_u8(device, self.buf, &abuf.get_data(), self.mute, self.volume);
168 }
169 self.samplepos += abuf.get_length() as u64;
170 },
171 _ => println!("unknown buffer type"),
172 };
173 return false;
174 },
175 Err(err) => {
176 println!(" error decoding {:?}", err);
177 return true;
178 },
179 };
180 }
181 },
182 Err(DemuxerError::EOF) => return true,
183 Err(err) => {
184 println!("demuxing error {:?}", err);
185 return true;
186 },
187 };
188 }
189 }
190 fn seek(&mut self, time: u64) -> bool {
191 let ret = self.demuxer.seek(NATimePoint::Milliseconds(time));
192if ret.is_err() { println!(" seek error\n"); }
193 ret.is_ok()
194 }
195}
196
197fn format_time(ms: u64) -> String {
198 let s = ms / 1000;
199 let ds = (ms % 1000) / 100;
200 let (min, s) = (s / 60, s % 60);
be48734e
KS
201 let (h, min) = (min / 60, min % 60);
202 if h == 0 {
203 if min == 0 {
204 format!("{}.{}", s, ds)
205 } else {
206 format!("{}:{:02}.{}", min, s, ds)
207 }
b043bd0a 208 } else {
be48734e 209 format!("{}:{:02}:{:02}.{}", h, min, s, ds)
b043bd0a
KS
210 }
211}
212
213impl Player {
214 fn new() -> Self {
215 let mut dmx_reg = RegisteredDemuxers::new();
41e41352 216 allreg::register_all_demuxers(&mut dmx_reg);
b043bd0a 217 let mut dec_reg = RegisteredDecoders::new();
41e41352 218 allreg::register_all_decoders(&mut dec_reg);
b043bd0a
KS
219
220 unsafe {
221 if sdl2_sys::SDL_Init(sdl2_sys::SDL_INIT_AUDIO) != 0 {
222 panic!("cannot init SDL");
223 }
224 }
225
226 Self {
227 ended: false,
228 paused: false,
229 dmx_reg, dec_reg,
230 volume: 100,
231 mute: false,
232 debug: false,
233 buf: Vec::new(),
234 }
235 }
36ac48e5 236 fn play_file(&mut self, name: &str, cmd_receiver: &mpsc::Receiver<Command>, start_time: NATimePoint) {
b043bd0a
KS
237 let ret = File::open(name);
238 if ret.is_err() {
239 println!("error opening {}", name);
240 return;
241 }
242 let mut file = ret.unwrap();
243
244 let mut fr = FileReader::new_read(&mut file);
245 let mut br = ByteReader::new(&mut fr);
246 let res = detect::detect_format(name, &mut br);
247 if res.is_none() {
248 println!("cannot detect format for {}", name);
249 return;
250 }
251 let (dmx_name, _) = res.unwrap();
252 drop(br);
253 drop(fr);
254 let dmx_fact = self.dmx_reg.find_demuxer(dmx_name);
255 if dmx_fact.is_none() {
256 println!("no demuxer for format {}", dmx_name);
257 return;
258 }
259 let dmx_fact = dmx_fact.unwrap();
260
261 file.seek(SeekFrom::Start(0)).unwrap();
262 let mut file = BufReader::new(file);
263 let mut fr = FileReader::new_read(&mut file);
264 let mut br = ByteReader::new(&mut fr);
265 let res = create_demuxer(dmx_fact, &mut br);
266 if res.is_err() {
267 println!("cannot create demuxer");
268 return;
269 }
270 let dmx = res.unwrap();
271
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();
279 if info.is_audio() {
280 let decfunc = self.dec_reg.find_decoder(info.get_name());
281 if decfunc.is_none() {
282 println!("no decoder for {}", info.get_name());
283 continue;
284 }
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);
289 continue;
290 }
291 dec = Some((dsupp, decoder));
292 ainfo = Some(info);
293 stream_no = i;
294 if s.duration > 0 {
295 duration = NATimeInfo::ts_to_time(s.duration, 1000, s.tb_num, s.tb_den);
296 }
297 break;
298 }
299 }
300 if dec.is_none() {
301 println!("no audio decoder found");
302 return;
303 }
304 let (dsupp, decoder) = dec.unwrap();
305
306 let ainfo = ainfo.unwrap().get_properties().get_audio_info().unwrap();
307 let arate = if ainfo.sample_rate > 0 { ainfo.sample_rate } else { 44100 };
308 let ch = ainfo.channels;
309
310 println!("Playing {} [{}Hz {}ch]", name, arate, ch);
5a92ee55 311 let ret = AudioDevice::open(arate, ch.min(2));
b043bd0a
KS
312 if ret.is_none() {
313 println!("cannot open output");
314 return;
315 }
316 let (device, dspec) = ret.unwrap();
317 let block_limit = dmx.get_stream(stream_no).unwrap().tb_num * arate / dmx.get_stream(stream_no).unwrap().tb_den * u32::from(dspec.channels);
318
319 let dst_info = NAAudioInfo { sample_rate: dspec.freq as u32, channels: dspec.channels, format: SND_S16_FORMAT, block_len: 1 };
320 let dst_chmap = if dst_info.channels == 2 {
321 NAChannelMap::from_str("L,R").unwrap()
322 } else {
323 NAChannelMap::from_str("C").unwrap()
324 };
325 let mut decoder = Decoder {
326 demuxer: dmx,
327 decoder, dsupp,
328 stream_no,
329 dst_info, dst_chmap,
330 samplepos: 0,
331 arate,
332 volume: self.volume,
333 mute: self.mute,
334 buf: &mut self.buf,
335 };
336 let mut refill_limit = arate * u32::from(dspec.channels);
337 let underfill_limit = (arate * u32::from(dspec.channels) / 4).max(block_limit);
338
36ac48e5
KS
339 if start_time != NATimePoint::None {
340 let _ret = decoder.demuxer.seek(start_time);
341 }
342
b043bd0a
KS
343 let mut eof = decoder.refill(&device);
344 while !eof && device.size() < refill_limit {
345 eof = decoder.refill(&device);
346 }
347
348 let duration_str = if duration != 0 { format_time(duration) } else { "???".to_owned() };
349 if !self.paused {
350 device.resume();
351 }
0a727e05 352 let mut no_display = false;
b043bd0a
KS
353 'main: loop {
354 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
355 let full_ms = cur_time * 1000 / u64::from(arate);
356 let timestr = format_time(full_ms);
357 let disp_vol = if self.mute { 0 } else { self.volume };
0a727e05
KS
358 if !no_display {
359 if !self.debug {
360 print!("> {} / {} {}% \r", timestr, duration_str, disp_vol);
361 } else {
362 print!("> {} / {} |{}| {}% \r", timestr, duration_str, device.size(), disp_vol);
363 }
b043bd0a
KS
364 }
365 std::io::stdout().flush().unwrap();
366 if device.size() < underfill_limit && !self.paused && refill_limit < (1 << 20) {
367 if full_ms > 5000 {
368 println!("underrun!");
369 }
370 refill_limit += refill_limit >> 1;
371 }
372 if device.size() < refill_limit / 2 {
373 while !eof && device.size() < refill_limit {
374 eof = decoder.refill(&device);
375 }
376 }
377 if eof && device.size() == 0 {
378 break 'main;
379 }
380 while let Ok(cmd) = cmd_receiver.try_recv() {
381 let cur_time = decoder.samplepos.saturating_sub(u64::from(device.size() / 2 / u32::from(dst_info.channels)));
382 match cmd {
383 Command::Forward(val) => {
384 device.pause();
385 device.clear();
386 let seekoff = match val {
387 1 => 10,
388 2 => 60,
389 _ => 10 * 60,
390 };
391 let seek_time = cur_time * 1000 / u64::from(arate) + seekoff * 1000;
392 let _ret = decoder.seek(seek_time);
393 while !eof && device.size() < refill_limit {
394 eof = decoder.refill(&device);
395 }
396 if eof {
397 break 'main;
398 }
399 if !self.paused {
400 device.resume();
401 }
402 },
403 Command::Back(val) => {
404 device.pause();
405 device.clear();
406 let seekoff = match val {
407 1 => 10,
408 2 => 60,
409 _ => 10 * 60,
410 };
411 let seek_time = (cur_time * 1000 / u64::from(arate)).saturating_sub(seekoff * 1000);
412 let _ret = decoder.seek(seek_time);
413 while !eof && device.size() < refill_limit {
414 eof = decoder.refill(&device);
415 }
416 if eof {
417 break 'main;
418 }
419 if !self.paused {
420 device.resume();
421 }
422 },
0a727e05
KS
423 Command::Seek(seek_time) => {
424 device.pause();
425 device.clear();
426 let _ret = decoder.seek(seek_time);
427 while !eof && device.size() < refill_limit {
428 eof = decoder.refill(&device);
429 }
430 if eof {
431 break 'main;
432 }
433 if !self.paused {
434 device.resume();
435 }
436 },
b043bd0a
KS
437 Command::Quit => {
438 device.pause();
439 self.ended = true;
440 break 'main;
441 },
442 Command::Next => {
443 device.pause();
444 break 'main;
445 },
446 Command::Repeat => {
447 device.pause();
448 device.clear();
449 let _ret = decoder.seek(0);
450 while !eof && device.size() < refill_limit {
451 eof = decoder.refill(&device);
452 }
453 if eof {
454 break 'main;
455 }
456 if !self.paused {
457 device.resume();
458 }
459 },
460 Command::Pause => {
461 self.paused = !self.paused;
462 if self.paused {
463 device.pause();
464 } else {
465 device.resume();
466 }
467 },
468 Command::VolumeUp => {
469 self.volume = (self.volume + 10).min(200);
470 decoder.volume = self.volume;
471 },
472 Command::VolumeDown => {
473 self.volume = self.volume.saturating_sub(10);
474 decoder.volume = self.volume;
475 },
476 Command::Mute => {
477 self.mute = !self.mute;
478 decoder.mute = self.mute;
479 },
480 Command::Debug => {
481 self.debug = !self.debug;
482 },
98431ed5
KS
483 Command::PauseDisplay => {
484 no_display = true;
485 if !self.paused {
486 device.pause();
487 }
488 },
489 Command::ResumeDisplay => {
490 no_display = false;
491 if !self.paused {
492 device.resume();
493 }
494 },
b043bd0a 495 };
0a727e05
KS
496 if !no_display {
497 print!("\r{:60}\r", ' ');
498 }
b043bd0a
KS
499 }
500 thread::sleep(Duration::from_millis(200));
501 }
502
503 println!();
504 }
505}
506
507fn main() {
508 let args: Vec<String> = std::env::args().collect();
509
510 let cmd_state = CmdLineState::new();
511
512 let (cmd_reader_thread, cmd_receiver) = start_reader();
513 let mut player = Player::new();
514
515 if args.len() == 1 {
516 println!("usage: nihav-sndplay file1 file2 ...");
517 return;
518 }
519
520 if args[1] == "--help" {
521 println!("usage: nihav-sndplay file1 file2 ...");
522 println!("commands:");
523 println!(" escape / q - quit");
524 println!(" space - pause / resume playback");
525 println!(" enter / end - play next file");
526 println!(" home - play current track from the beginning");
527 println!(" left / right - seek 10 seconds forward / back");
528 println!(" up / down - seek 1 minute forward / back");
529 println!(" pgup / pgdn - seek 10 minutes forward / back");
530 println!(" + / - - increase / decrease volume");
531 println!(" m - mute output");
532 return;
533 }
534
36ac48e5
KS
535 let mut aiter = args[1..].iter();
536 let mut start_time = NATimePoint::None;
537 while let Some(arg) = aiter.next() {
538 match arg.as_str() {
539 "-start" => {
540 if let Some(arg) = aiter.next() {
541 if let Ok(val) = arg.parse() {
542 start_time = val;
543 } else {
544 println!("invalid time");
545 }
546 } else {
547 println!("argument is required");
548 }
549 },
550 _ => {
551 player.play_file(arg, &cmd_receiver, start_time);
552 if player.ended {
553 break;
554 }
555 start_time = NATimePoint::None;
556 },
557 };
b043bd0a
KS
558 }
559 cmd_state.restore();
560
561 drop(cmd_reader_thread);
562 unsafe { sdl2_sys::SDL_Quit(); }
563}