2 * minmad - a minimal mp3 player using libmad and oss
4 * Copyright (C) 2009-2016 Ali Gholami Rudi
6 * This program is released under the Modified BSD license.
18 #include <sys/soundcard.h>
21 #define MIN(a, b) ((a) < (b) ? (a) : (b))
22 #define MAX(a, b) ((a) > (b) ? (a) : (b))
24 static struct mad_decoder maddec
;
25 static int afd
; /* oss fd */
27 static char filename
[128];
28 static char *ossdsp
; /* oss device */
29 static int mfd
; /* input file descriptor */
30 static long msize
; /* file size */
31 static unsigned char mbuf
[1 << 16];
32 static long mpos
; /* the position of mbuf[] */
33 static long mlen
; /* data in mbuf[] */
34 static long moff
; /* offset into mbuf[] */
35 static long mark
[256]; /* mark positions */
36 static int frame_sz
; /* frame size */
37 static int frame_ms
; /* frame duration in milliseconds */
38 static int played
; /* playing time in milliseconds */
39 static int rate
; /* current oss sample rate */
40 static int topause
; /* planned pause (compared with played) */
49 static int oss_open(void)
51 afd
= open(ossdsp
? ossdsp
: "/dev/dsp", O_WRONLY
);
55 static void oss_close(void)
57 if (afd
> 0) /* zero fd is used for input */
63 static void oss_conf(int rate
, int ch
, int bits
)
65 int frag
= 0x0003000b; /* 0xmmmmssss: 2^m fragments of size 2^s each */
66 ioctl(afd
, SOUND_PCM_WRITE_CHANNELS
, &ch
);
67 ioctl(afd
, SOUND_PCM_WRITE_BITS
, &bits
);
68 ioctl(afd
, SOUND_PCM_WRITE_RATE
, &rate
);
69 ioctl(afd
, SOUND_PCM_SETFRAGMENT
, &frag
);
72 static int cmdread(void)
75 if (read(0, &b
, 1) <= 0)
77 return (unsigned char) b
;
80 static void cmdwait(void)
82 struct pollfd ufds
[1];
84 ufds
[0].events
= POLLIN
;
88 static long muldiv64(long num
, long mul
, long div
)
90 return (long long) num
* mul
/ div
;
93 static void cmdinfo(void)
95 int per
= muldiv64(mpos
+ moff
, 1000, msize
);
96 int loc
= muldiv64(mpos
+ moff
, frame_ms
, frame_sz
* 1000);
97 printf("%c %02d.%d%% (%d:%02d:%02d - %04d.%ds) [%s]\r",
98 paused
? (afd
< 0 ? '*' : ' ') : '>',
100 loc
/ 3600, (loc
% 3600) / 60, loc
% 60,
101 played
/ 1000, (played
/ 100) % 10,
106 static int cmdcount(int def
)
108 int result
= count
? count
: def
;
113 static void seek(long pos
)
115 mark
['\''] = mpos
+ moff
;
116 mpos
= MAX(0, MIN(msize
, pos
));
120 static void cmdseekrel(int n
)
122 int diff
= muldiv64(n
, frame_sz
* 1000, frame_ms
? frame_ms
: 40);
123 seek(mpos
+ moff
+ diff
);
126 static void cmdseek100(int n
)
129 seek(muldiv64(msize
, n
, 100));
132 static void cmdseek(int n
)
134 long pos
= muldiv64(n
* 60, frame_sz
* 1000, frame_ms
? frame_ms
: 40);
138 static int cmdpause(int pause
)
140 if (!pause
&& paused
) {
145 if (pause
&& !paused
) {
152 static int cmdexec(void)
155 if (topause
> 0 && topause
<= played
) {
159 while ((c
= cmdread()) >= 0) {
162 mark
[c
] = mpos
+ moff
;
173 cmdseekrel(+600 * cmdcount(1));
176 cmdseekrel(-600 * cmdcount(1));
179 cmdseekrel(+60 * cmdcount(1));
182 cmdseekrel(-60 * cmdcount(1));
185 cmdseekrel(+10 * cmdcount(1));
188 cmdseekrel(-10 * cmdcount(1));
191 cmdseek100(cmdcount(0));
194 cmdseek(cmdcount(0));
207 if (cmdpause(!paused
))
211 topause
= count
? played
+ cmdcount(0) * 60000 : 0;
221 count
= count
* 10 + c
- '0';
227 static enum mad_flow
madinput(void *data
, struct mad_stream
*stream
)
229 int nread
= stream
->next_frame
? stream
->next_frame
- mbuf
: moff
;
230 int nleft
= mlen
- nread
;
238 memmove(mbuf
, mbuf
+ nread
, nleft
);
239 if (nleft
< sizeof(mbuf
)) {
240 if ((nr
= read(mfd
, mbuf
+ nleft
, sizeof(mbuf
) - nleft
)) <= 0) {
242 return MAD_FLOW_STOP
;
246 mad_stream_buffer(stream
, mbuf
, mlen
);
249 return MAD_FLOW_CONTINUE
;
252 static signed int madscale(mad_fixed_t sample
)
254 sample
+= (1l << (MAD_F_FRACBITS
- 16)); /* round */
255 if (sample
>= MAD_F_ONE
) /* clip */
256 sample
= MAD_F_ONE
- 1;
257 if (sample
< -MAD_F_ONE
)
259 return sample
>> (MAD_F_FRACBITS
+ 1 - 16); /* quantize */
262 static void madupdate(void)
266 moff
= maddec
.sync
->stream
.this_frame
- mbuf
;
267 sz
= maddec
.sync
->stream
.next_frame
-
268 maddec
.sync
->stream
.this_frame
;
269 ms
= mad_timer_count(maddec
.sync
->frame
.header
.duration
,
270 MAD_UNITS_MILLISECONDS
);
271 frame_ms
= frame_ms
? ((frame_ms
<< 5) - frame_ms
+ ms
) >> 5 : ms
;
272 frame_sz
= frame_sz
? ((frame_sz
<< 5) - frame_sz
+ sz
) >> 5 : sz
;
276 static char mixed
[1 << 18];
277 static enum mad_flow
madoutput(void *data
,
278 struct mad_header
const *header
,
282 int c2
= pcm
->channels
> 1 ? 1 : 0;
284 played
+= mad_timer_count(maddec
.sync
->frame
.header
.duration
,
285 MAD_UNITS_MILLISECONDS
);
286 for (i
= 0; i
< pcm
->length
; i
++) {
287 mixed
[i
* 4 + 0] = madscale(pcm
->samples
[c1
][i
]) & 0xff;
288 mixed
[i
* 4 + 1] = (madscale(pcm
->samples
[c1
][i
]) >> 8) & 0xff;
289 mixed
[i
* 4 + 2] = madscale(pcm
->samples
[c2
][i
]) & 0xff;
290 mixed
[i
* 4 + 3] = (madscale(pcm
->samples
[c2
][i
]) >> 8) & 0xff;
292 if (header
->samplerate
!= rate
) {
293 rate
= header
->samplerate
;
294 oss_conf(rate
, 2, 16);
296 write(afd
, mixed
, pcm
->length
* 4);
298 return cmdexec() ? MAD_FLOW_STOP
: MAD_FLOW_CONTINUE
;
301 static enum mad_flow
maderror(void *data
,
302 struct mad_stream
*stream
,
303 struct mad_frame
*frame
)
305 return MAD_FLOW_CONTINUE
;
308 static void maddecode(void)
310 mad_decoder_init(&maddec
, NULL
, madinput
, 0, 0, madoutput
, maderror
, 0);
316 mad_decoder_run(&maddec
, MAD_DECODER_MODE_SYNC
);
319 mad_decoder_finish(&maddec
);
322 static void term_init(struct termios
*termios
)
324 struct termios newtermios
;
325 tcgetattr(0, termios
);
326 newtermios
= *termios
;
327 newtermios
.c_lflag
&= ~ICANON
;
328 newtermios
.c_lflag
&= ~ECHO
;
329 tcsetattr(0, TCSAFLUSH
, &newtermios
);
330 fcntl(0, F_SETFL
, fcntl(0, F_GETFL
) | O_NONBLOCK
);
333 static void term_done(struct termios
*termios
)
335 tcsetattr(0, 0, termios
);
338 int main(int argc
, char *argv
[])
341 struct termios termios
;
342 char *path
= argc
>= 2 ? argv
[1] : NULL
;
345 if (strchr(path
, '/'))
346 path
= strrchr(path
, '/') + 1;
347 snprintf(filename
, 30, "%s", path
);
348 mfd
= open(argv
[1], O_RDONLY
);
349 if (fstat(mfd
, &stat
) == -1 || stat
.st_size
== 0)
351 msize
= stat
.st_size
;
352 ossdsp
= getenv("OSSDSP");
354 fprintf(stderr
, "minmad: /dev/dsp busy?\n");