2 * minmad - a minimal mp3 player using libmad and oss
4 * Copyright (C) 2009-2011 Ali Gholami Rudi
6 * This program is released under GNU GPL version 2.
20 #include <sys/soundcard.h>
23 #define CTRLKEY(x) ((x) - 96)
24 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 #define MAX(a, b) ((a) > (b) ? (a) : (b))
30 static struct mad_decoder decoder
;
31 static struct termios termios
;
32 static int afd
; /* oss fd */
34 static char filename
[1 << 12];
35 static unsigned char *mem
;
38 static int frame_sz
; /* frame size */
39 static int frame_ms
; /* frame duration in milliseconds */
40 static int played
; /* playing time in milliseconds */
47 static int readkey(void)
50 if (read(STDIN_FILENO
, &b
, 1) <= 0)
55 static void updatepos(void)
59 pos
= decoder
.sync
->stream
.this_frame
- mem
;
60 sz
= decoder
.sync
->stream
.next_frame
-
61 decoder
.sync
->stream
.this_frame
;
62 ms
= mad_timer_count(decoder
.sync
->frame
.header
.duration
,
63 MAD_UNITS_MILLISECONDS
);
64 frame_ms
= frame_ms
? ((frame_ms
<< 5) - frame_ms
+ ms
) >> 5 : ms
;
65 frame_sz
= frame_sz
? ((frame_sz
<< 5) - frame_sz
+ sz
) >> 5 : sz
;
69 static void printinfo(void)
71 int per
= pos
* 1000.0 / len
;
72 int loc
= pos
/ frame_sz
* frame_ms
/ 1000;
73 printf("%c %02d.%d%% (%d:%02d:%02d - %04d.%ds) [%s]\r",
76 loc
/ 3600, (loc
% 3600) / 60, loc
% 60,
77 played
/ 1000, (played
/ 100) % 10,
82 static int getcount(int def
)
84 int result
= count
? count
: def
;
89 static void seek(int n
)
91 int diff
= n
* frame_sz
* 1000 / (frame_ms
? frame_ms
: 40);
92 pos
= MAX(0, MIN(len
, pos
+ diff
));
95 static void seek_thousands(int n
)
97 pos
= len
* (float) n
/ 1000;
98 pos
-= pos
% frame_sz
;
101 static int execkey(void)
105 while ((c
= readkey()) != -1) {
108 seek(JMP3
* getcount(1));
111 seek(-JMP3
* getcount(1));
114 seek(JMP2
* getcount(1));
117 seek(-JMP2
* getcount(1));
120 seek(JMP1
* getcount(1));
123 seek(-JMP1
* getcount(1));
126 seek_thousands(getcount(0) * 10);
143 count
= count
* 10 + c
- '0';
149 static enum mad_flow
input(void *data
, struct mad_stream
*stream
)
151 static unsigned long cpos
;
152 if (pos
&& pos
== cpos
) {
154 return MAD_FLOW_STOP
;
157 mad_stream_buffer(stream
, mem
+ pos
, len
- pos
);
158 return MAD_FLOW_CONTINUE
;
161 static signed int scale(mad_fixed_t sample
)
164 sample
+= (1L << (MAD_F_FRACBITS
- 16));
166 if (sample
>= MAD_F_ONE
)
167 sample
= MAD_F_ONE
- 1;
168 else if (sample
< -MAD_F_ONE
)
171 return sample
>> (MAD_F_FRACBITS
+ 1 - 16);
174 static void push_sample(char *buf
, mad_fixed_t sample
)
176 *buf
++ = (sample
>> 0) & 0xff;
177 *buf
++ = (sample
>> 8) & 0xff;
180 static void oss_conf(void)
184 ioctl(afd
, SOUND_PCM_WRITE_RATE
, &rate
);
185 ioctl(afd
, SOUND_PCM_WRITE_CHANNELS
, &ch
);
186 ioctl(afd
, SOUND_PCM_WRITE_BITS
, &bits
);
189 static char mixed
[1 << 20];
190 static enum mad_flow
output(void *data
,
191 struct mad_header
const *header
,
195 int right
= pcm
->channels
> 1 ? 1 : 0;
196 played
+= mad_timer_count(decoder
.sync
->frame
.header
.duration
,
197 MAD_UNITS_MILLISECONDS
);
198 for (i
= 0; i
< pcm
->length
; i
++) {
199 push_sample(mixed
+ i
* 4, scale(pcm
->samples
[0][i
]));
200 push_sample(mixed
+ i
* 4 + 2, scale(pcm
->samples
[right
][i
]));
202 if (header
->samplerate
!= rate
) {
203 rate
= header
->samplerate
;
206 write(afd
, mixed
, pcm
->length
* 4);
207 return execkey() ? MAD_FLOW_STOP
: MAD_FLOW_CONTINUE
;
210 static enum mad_flow
error(void *data
,
211 struct mad_stream
*stream
,
212 struct mad_frame
*frame
)
214 return MAD_FLOW_CONTINUE
;
217 static void waitkey(void)
219 struct pollfd ufds
[1];
220 ufds
[0].fd
= STDIN_FILENO
;
221 ufds
[0].events
= POLLIN
;
225 static void decode(void)
227 mad_decoder_init(&decoder
, NULL
, input
, 0, 0, output
, error
, 0);
233 mad_decoder_run(&decoder
, MAD_DECODER_MODE_SYNC
);
236 mad_decoder_finish(&decoder
);
239 static void oss_init(void)
241 afd
= open("/dev/dsp", O_RDWR
);
243 fprintf(stderr
, "cannot open /dev/dsp\n");
248 static void oss_close(void)
253 static void term_setup(void)
255 struct termios newtermios
;
256 tcgetattr(STDIN_FILENO
, &termios
);
257 newtermios
= termios
;
258 newtermios
.c_lflag
&= ~ICANON
;
259 newtermios
.c_lflag
&= ~ECHO
;
260 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &newtermios
);
261 fcntl(STDIN_FILENO
, F_SETFL
, fcntl(STDIN_FILENO
, F_GETFL
) | O_NONBLOCK
);
264 static void term_cleanup(void)
266 tcsetattr(STDIN_FILENO
, 0, &termios
);
269 static void sigcont(int sig
)
274 int main(int argc
, char *argv
[])
280 fd
= open(argv
[1], O_RDONLY
);
281 strcpy(filename
, argv
[1]);
283 if (fstat(fd
, &stat
) == -1 || stat
.st_size
== 0)
285 mem
= mmap(0, stat
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
287 if (mem
== MAP_FAILED
)
290 signal(SIGCONT
, sigcont
);
295 munmap(mem
, stat
.st_size
);