1 
2 /*
3  * Example Interface for libmodplug without using XMMS
4  **
5 commandline interface to modplugxmms library
6 gurkan@linuks.mine.nu www.linuks.mine.nu
7 Copyright (C) 2003 Gürkan Sengün
8 
9 TODO
10 unlock /dev/dsp when in 'p'ause mode
11 and have a look at SNDCTL_DSP_GETxPTR
12 more functions
13 sig handling
14 12:23 < tp76> tarzeau: yes. I prefer `T *foo = malloc (N * sizeof *foo);'.
15 use stat() to get filesize
16 run indent -kr
17 
18 14:08 < tarzeau> so the player isn't stuck when anotehr thing is using /dev/dsp
19 14:08 < tarzeau> if that works a msg is displayed
20 14:08 < tarzeau> later the song is tried to be opened
21 14:08 < tarzeau> then the soundcard settings are set
22 14:09 < tarzeau> the the module is load using libmodplug
23 14:09 < tarzeau> module settings being set
24 14:09 < tarzeau> keyboard handling set
25 14:09 < tarzeau> the status string set up
26 14:09 < tarzeau> time stuff and the main loop
27 14:09 < warp> ok.
28 14:09 < tarzeau> where keyboard input is handled
29 14:09 < tarzeau> that's more or less it
30 14:10 < warp> see, you've just described all the functions you could make.
31 14:10 < warp> so, start that for loop with a   init_sound_device ()   or
32       something.
33 14:10 < warp> etc...  make functions for all the things you just described.
34 14:10 < tarzeau> heh good idea, i'll copy paste this irc talk and do that for
35        the next version like 0.9 with the sighandler for people
36         trying to ctrl-c or ctlr-z
37 
38 maybe add support for write a .wav file for later play or .ogg
39 and maybe add support for gzip -d some.mod.gz | modplugplay
40 add support to allow modplugplay *.IT *.it *.s3m *.xm (n)ext (N)ot next
41 and -r for random order (make a random played/unplayed (bit)list)
42 show file size
43 
44 argument processing
45 if it's not a file, we check if it's an option if not we ignore it
46 
47 action keys
48 -> like key (put in list/directory)
49 -> dislike key (remove)
50 -> show info
51 
52 || [] >> << >| ()
53 
54 TODO
55 or maybe just a $HOME/.modplugplay or /etc/modplugplay
56 command line option handling
57   -l --loop
58   -m --mono
59   -s --stereo (default)
60   -4 --44100 (default)
61   -2 --22050
62   -1 --11025
63   -8 --8bit (default is 16bit)
64   -h --help
65   -q --quiet
66   -v --version (head -c1080 module |strings)
67   -i --info    don't play, only show module info (songname,length,size,type)
68 */
69 
70 #include <stdio.h>			/* printf */
71 #include <string.h>			/* strcpy */
72 #include <stdlib.h>			/* srand/rand */
73 #include <unistd.h>
74 #include <libmodplug/modplug.h>			/* core */
75 #include <sys/ioctl.h>			/* control device */
76 #include <fcntl.h>
77 
78 #ifdef __NetBSD__
79 #include <soundcard.h>
80 #else
81 #include <sys/soundcard.h>
82 #endif
83 
84 #include <sys/time.h>			/* gettimeofday */
85 #include <time.h>
86 #include <sys/poll.h>			/* poll for keyboard input */
87 #include <termios.h>			/* needed to get terminal size */
88 
89 #define VERSION "1.1"
90 
91 #define BUF_SIZE 4096
92 #define DEVICE_NAME "/dev/dsp"
93 
94 static struct termios stored_settings;
95 int audio_fd, mixer_fd;
96 unsigned char audio_buffer[BUF_SIZE];
97 
98 typedef struct {
99 	int x, y;
100 } term_size;
101 
102 /* inquire actual terminal size */
get_term_size(int fd,term_size * t)103 static int get_term_size(int fd, term_size *t) {
104 #ifdef TIOCGSIZE
105     struct ttysize win;
106 #elif defined(TIOCGWINSZ)
107     struct winsize win;
108 #endif
109 
110 #ifdef TIOCGSIZE
111     if (ioctl(fd,TIOCGSIZE,&win)) return 0;
112     if (t) {
113 	t->y=win.ts_lines;
114     	t->x=win.ts_cols;
115     }
116 #elif defined TIOCGWINSZ
117     if (ioctl(fd,TIOCGWINSZ,&win)) return 0;
118     if (t) {
119         t->y=win.ws_row;
120         t->x=win.ws_col;
121     }
122 #else
123     {
124 	const char *s;
125 	s=getenv("LINES");   if (s) t->y=strtol(s,NULL,10); else t->y=25;
126 	s=getenv("COLUMNS"); if (s) t->x=strtol(s,NULL,10); else t->x=80;
127     }
128 #endif
129     return 1;
130 }
131 
set_keypress(void)132 void set_keypress(void)
133 {
134     struct termios new_settings;
135     tcgetattr(0,&stored_settings);
136     new_settings=stored_settings;
137     new_settings.c_lflag &= (~ICANON);
138     new_settings.c_lflag &= (~ECHO);
139     new_settings.c_cc[VTIME] = 0;
140     new_settings.c_cc[VMIN] = 1;
141     tcsetattr(0,TCSANOW,&new_settings);
142     return;
143 }
144 
reset_keypress(void)145 void reset_keypress(void)
146 {
147     tcsetattr(0,TCSANOW,&stored_settings);
148     return;
149 }
150 
ansi_cursor(int visible)151 void ansi_cursor(int visible)
152 {
153     if (visible) {
154 	printf("\033[?25h");
155     } else {
156 	printf("\033[?25l");
157     }
158 }
159 
versioninfo()160 void versioninfo()
161 {
162 	printf("\nCopyright (C) 2003 Gürkan Sengün\n");
163 	printf("Version %s compiled on %s at %s.\n",VERSION,__DATE__,__TIME__);
164 }
165 
help(char * s,int exitcode)166 void help(char *s, int exitcode)
167 {
168 	printf("Copyright (C) 2003 Gürkan Sengün\n");
169         printf("Version %s compiled on %s at %s.\n",VERSION,__DATE__,__TIME__);
170 	printf("\n");
171 	if (exitcode!=0)
172 		printf("%s: too few arguments\n",s);
173 	printf("Usage: %s" /*[OPTIONS]*/" [FILES]\n",s);
174 	printf("\n");
175 
176 	printf("  -v/--version  print version info\n");
177 	printf("  -h/--help   print help\n");
178 	printf("  -l   start in looping mode\n");
179 /*
180 	printf("  -r   randomize play sequence\n");
181 	printf("  -c   write to console instead of %s\n",DEVICE_NAME);
182 	printf("  -i   use stdin for file input\n");
183 	printf("  -q   be quiet\n");
184 */
185 	exit(exitcode);
186 }
187 
get_byteorder()188 int get_byteorder()
189 {
190 #define sz sizeof(int)
191     int ival;
192     int format;
193     char s[sz];
194     char t[sz];
195     int i, lit, big;
196 
197     for (i=0; i<sz; i++) s[i] = i;
198     ival = *(int *)s;
199     big = lit = 0;
200 
201     for (i=0; i<sz; i++) {
202         char c = ival&0xff;
203         ival >>= 8;
204         if (s[i] == c) lit++;
205         if (s[sz-i-1] == c) big++;
206         t[i] = c;
207     }
208     if (lit == sz && big == 0) {
209         /*printf("little endian\n");*/
210 	format = AFMT_S16_LE;
211     } else if (big == sz && lit == 0) {
212         /*printf("big endian\n");*/
213 	format = AFMT_S16_BE;
214     } else {
215 	format = -1;
216     }
217     return(format);
218 }
219 
getFileData(char * filename,long * size)220 char *getFileData(char *filename, long *size)
221 {
222     FILE *f;
223     char *data;
224 
225     f = fopen(filename, "rb");
226     if (f == NULL) {
227       return NULL;
228     }
229     fseek(f, 0L, SEEK_END);
230     (*size) = ftell(f);
231     rewind(f);
232     data = (char*)malloc(*size);
233     fread(data, *size, sizeof(char), f);
234     fclose(f);
235 
236     return(data);
237 }
238 
getpcmvol()239 int getpcmvol()
240 /* get absolute pcm volume, 0 - 100 (for left+right) */
241 {
242     int vol;
243 
244     if ((mixer_fd=open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0)) == -1) {
245 	printf("can't open mixer\n");
246 	exit(1);
247     }
248     ioctl(mixer_fd,MIXER_READ(SOUND_MIXER_PCM),&vol);
249     close(mixer_fd);
250 
251     vol=vol & 255;
252     if (vol==100) vol=99;
253     return(vol);
254 }
255 
setrelpcmvol(int newvol)256 int setrelpcmvol(int newvol)
257 /* set relative pcm volume, -100 .. 100 */
258 {
259     int currentvol=getpcmvol();
260     currentvol+=newvol;
261     if (currentvol>100) currentvol=100;
262     if (currentvol<0) currentvol=0;
263     newvol=currentvol+(currentvol*256);
264 
265     if ((mixer_fd=open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0)) == -1) {
266 	printf("can't open mixer\n");
267 	exit(1);
268     }
269     ioctl(mixer_fd,MIXER_WRITE(SOUND_MIXER_PCM),&newvol);
270     close(mixer_fd);
271 }
272 
main(int argc,char * argv[])273 int main(int argc, char* argv[])
274 {
275     long size;
276     char *d;
277     term_size terminal;
278     ModPlugFile *f2;
279     int mlen, len;
280     struct timeval tvstart;
281     struct timeval tv;
282     struct timeval tvpause;
283     struct timeval tvunpause;
284     struct timeval tvptotal;
285     char status[161];
286     char songname[41];
287     char notpaus[4];
288 
289     int vol=getpcmvol();
290     int songsplayed = 0;
291 
292     ModPlug_Settings settings;
293     ModPlug_GetSettings(&settings);
294 
295     int format;
296     int channels = 2;
297     int speed = 44100;
298 
299     int c; // kontest
300     char buffer[128];
301     int result, nread;
302     struct pollfd pollfds;
303     int timeout = 1;            /* Timeout in msec. */
304     int loop=0; // kontest
305     int pause=0;
306     int mono=0;
307     int bits=0;
308     int song;
309     int millisecond;
310     char *randplayed; /* randomly played songs
311 			 songs that are n/N'd are done or not? */
312     /* what about N if the previous song is not playable? */
313     /* maybe mark it played in randplayed */
314 
315     // [rev--dly--] [sur--dly--] [bas--rng--]
316     int rev=0;    // a
317     int revdly=0; // s
318     int sur=0;    // d
319     int surdly=0; // y
320     int bas=0;    // x
321     int basrng=0; // c
322 
323     if ((format = get_byteorder()) == -1) {
324         return 1;
325     }
326 
327     /* Initialize pollfds; we're looking at input, stdin */
328     pollfds.fd = 0;             /* stdin */
329     pollfds.events = POLLIN;    /* Wait for input */
330 
331     if (argc==1) {
332 	help(argv[0],1);
333     }
334 
335     if (!get_term_size(STDIN_FILENO,&terminal)) {
336 	fprintf(stderr,"warning: failed to get terminal size\n");
337     }
338 
339     srand(time(NULL));
340 
341 for (song=1; song<argc; song++) {
342 
343 /* check if arguments need to be parsed */
344     if (argv[song][0] == '-') {
345       if (!songsplayed && strstr(argv[song],"-h")) {
346         printf("\n");
347         help(argv[0],0);
348       } else if (!songsplayed && strstr(argv[song],"-v")) {
349 	versioninfo();
350         exit(0);
351       } else if (strstr(argv[song],"-l")) {
352         loop=1;
353         continue;
354       }
355       if (argv[song][1] == '-') { // not a song
356         if (strstr(argv[song],"--help")) {
357           help(argv[0],0);
358         } else if (strstr(argv[song],"--version")) {
359 	  versioninfo();
360 	  exit(0);
361 	}
362         continue;
363        }
364       }
365 
366     /* O_NONBLOCK gave me the problem
367     that after about 5 seconds writing audiobuffer to DEVICE_NAME
368     was not completed, like 3072 bytes instead of the full buffer 4096
369     which made the sound get faster (and wrong)
370     */
371     if ((audio_fd=open(DEVICE_NAME, O_WRONLY | O_NONBLOCK, 0)) == -1) {
372 	fprintf(stderr,"%s (%s): ",DEVICE_NAME,argv[song]);
373 	perror("");
374 	exit(1);
375     }
376     close(audio_fd);
377 
378     if ((audio_fd=open(DEVICE_NAME, O_WRONLY /*| O_NONBLOCK*/, 0)) == -1) {
379 	perror(DEVICE_NAME);
380 	exit(1);
381     } else {
382 	printf("opened %s for playing ",DEVICE_NAME);
383     }
384     printf("%s ",argv[song]);
385     printf("[%d/%d]",song,argc-1);
386 
387     d = getFileData(argv[song], &size);
388     if (d == NULL) continue;
389     printf(" [%ld]\n",size);
390 
391     if (ioctl(audio_fd,SNDCTL_DSP_SETFMT, &format) == -1) {
392 	perror("SND_CTL_DSP_SETFMT");
393 	exit(1);
394     }
395 
396     if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
397 	perror("SNDCTL_DSP_CHANNELS");
398 	exit(1);
399     }
400     /* int mChannels; */   /* Number of channels - 1 for mono or 2 for stereo */
401     /* int mBits; */       /* Bits per sample - 8, 16, or 32 */
402     /* int mFrequency; */  /* Sampling rate - 11025, 22050, or 44100 */
403 
404     if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) {
405 	perror("SNDCTL_DSP_SPEED");
406 	exit(1);
407     }
408 
409     /* Note: Basic settings must be set BEFORE ModPlug_Load */
410     settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */
411     settings.mChannels = 2;
412     settings.mBits = 16;
413     settings.mFrequency = 44100;
414     /* insert more setting changes here */
415     ModPlug_SetSettings(&settings);
416 
417     f2 = ModPlug_Load(d, size);
418     if (!f2) {
419 	printf("could not load %s\n", argv[song]);
420 	close(audio_fd);
421 	free(d); /* ? */
422     } else {
423       songsplayed++;
424 /*    settings.mFlags=MODPLUG_ENABLE_OVERSAMPLING | \
425                     MODPLUG_ENABLE_NOISE_REDUCTION | \
426 		    MODPLUG_ENABLE_REVERB | \
427 		    MODPLUG_ENABLE_MEGABASS | \
428 		    MODPLUG_ENABLE_SURROUND;*/
429 
430 //    settings.mReverbDepth = 100; /* 0 - 100 */ *   [REV--DLY--]
431 //    settings.mReverbDelay = 200; /* 40 - 200 ms  00-FF */
432 //    settings.mSurroundDepth = 100; /* 0 - 100 */   [SUR--DLY--]
433 //    settings.mSurroundDelay = 40; /* 5 - 40 ms */
434 //    settings.mBassAmount  = 100; /* 0 - 100 */     [BAS--RNG--]
435 //    settings.mBassRange   = 100; /* 10 - 100 hz */
436 // [REV--DLY--] [SUR--DLY--] [BAS--RNG--]
437 // [rev--dly--] [sur--dly--] [bas--rng--]
438 
439     set_keypress();
440     strcpy(songname, ModPlug_GetName(f2));
441 
442     /* if no modplug "name" - use last 41 characters of filename */
443     if (strlen(songname)==0) {
444         int l = strlen(argv[song]);
445 	char *st = argv[song];
446         if (l >= 41) st = argv[song] + l - 41;
447         strncpy(songname,st,41);
448         songname[41] = 0;
449     }
450     sprintf(status,"playing %s (%%d.%%d/%d\") (%%d/%%d/%%d%%s)    \b\b\b\b",songname,ModPlug_GetLength(f2)/1000);
451     if (loop) sprintf(status,"looping %s (%%d.%%d/%d\") (%%d/%%d/%%d%%s)    \b\b\b\b",songname,ModPlug_GetLength(f2)/1000);
452 
453     gettimeofday(&tvstart,NULL);
454     tvptotal.tv_sec=tvptotal.tv_usec=0;
455     mlen=1;
456 
457     while(mlen!=0) {
458 	if (mlen==0) { break; }
459 
460 	if (!pause) {
461 	    gettimeofday(&tv,NULL);
462 	    mlen=ModPlug_Read(f2,audio_buffer,BUF_SIZE);
463 	    if ((len=write(audio_fd,audio_buffer,mlen)) == -1) {
464 		perror("audio write");
465 		exit(1);
466     	    }
467 	    /*printf("%d %d\n",mlen,len);*/
468         }
469 	printf(status,tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec,tv.tv_usec/100000,speed,channels,settings.mBits/*,rev,revdly,sur,surdly,bas,basrng*/);
470 	fflush(stdout);
471 
472 	if ((mlen==0) && (loop==1)) {
473 	    /*printf("LOOPING NOW\n");*/
474 	    ModPlug_Seek(f2,0);
475 	    gettimeofday(&tvstart,NULL);
476 	    mlen=ModPlug_Read(f2,audio_buffer,BUF_SIZE);
477 	    tvptotal.tv_sec=tvptotal.tv_usec=0;
478 	}
479 
480         result = poll(&pollfds, 1, timeout);
481         switch (result) {
482         case 0:
483             /*printf(".");*/
484             break;
485         case -1:
486             perror("select");
487             exit(1);
488 
489         default:
490             if (pollfds.revents && POLLIN) {
491 	        nread = read(0, buffer, 1); /* s/nread/1/2 */
492                 if (nread == 0) {
493                     printf("keyboard done\n");
494                     exit(0);
495                } else {
496                     buffer[nread] = 0;
497                     /* printf("%s", buffer); */
498 
499 		    if (buffer[0]=='q') { mlen=0; song=argc; ioctl(audio_fd,SNDCTL_DSP_RESET,0); } /* quit */
500 
501 		    if (buffer[0]=='f') {
502 			if ((tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec+10) < (ModPlug_GetLength(f2)/1000)) {
503 			    ioctl(audio_fd,SNDCTL_DSP_RESET,0);
504 			    ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
505 			    tvstart.tv_sec-=10;
506 			}
507 		    } /* forward 10" */
508 
509 		    if (buffer[0]=='b') {
510 			if ((tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec-10) > 0) {
511 			    ioctl(audio_fd,SNDCTL_DSP_RESET,0);
512 			    ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000-10000);
513 			    tvstart.tv_sec+=10;
514 			}
515 		    } /* backward 10" */
516 
517 		    /*
518 		    if (buffer[0]=='i') {
519 			printf("\n");
520 		    } */
521 
522 /*
523 		    if (buffer[0]=='a') {
524 			rev++; settings.mReverbDepth=rev;
525 			ModPlug_SetSettings(&settings);
526 		    }
527 		    if (buffer[0]=='A') {
528 			rev--; settings.mReverbDepth=rev;
529 			ModPlug_SetSettings(&settings);
530 		    }
531 		    if (buffer[0]=='s') {
532 			revdly++; settings.mReverbDelay=revdly;
533 			ModPlug_SetSettings(&settings);
534 		    }
535 		    if (buffer[0]=='S') {
536 			revdly--; settings.mReverbDelay=revdly;
537 			ModPlug_SetSettings(&settings);
538 		    }
539 		    if (buffer[0]=='d') {
540 			sur++; settings.mSurroundDepth=sur;
541 			ModPlug_SetSettings(&settings);
542 		    }
543 		    if (buffer[0]=='D') {
544 			sur--; settings.mSurroundDepth=sur;
545 			ModPlug_SetSettings(&settings);
546 		    }
547 		    if (buffer[0]=='y') {
548 			surdly++; settings.mSurroundDelay=surdly;
549 			ModPlug_SetSettings(&settings);
550 		    }
551 		    if (buffer[0]=='Y') {
552 			surdly--; settings.mSurroundDelay=surdly;
553 			ModPlug_SetSettings(&settings);
554 		    }
555 		    if (buffer[0]=='x') {
556 			bas++; settings.mBassAmount=bas;
557 			ModPlug_SetSettings(&settings);
558 		    }
559 		    if (buffer[0]=='X') {
560 			bas--; settings.mBassAmount=bas;
561 			ModPlug_SetSettings(&settings);
562 		    }
563 		    if (buffer[0]=='c') {
564 			basrng++; settings.mBassRange=basrng;
565 			ModPlug_SetSettings(&settings);
566 		    }
567 		    if (buffer[0]=='C') {
568 			basrng--; settings.mBassRange=basrng;
569 			ModPlug_SetSettings(&settings);
570 		    }
571 */
572 
573 		    if (buffer[0]=='n') {
574 			if (song<argc) { mlen=0; pause=0; ioctl(audio_fd,SNDCTL_DSP_RESET,0); }
575 		    }
576 
577 		    if (buffer[0]=='N') {
578 			if (song>1) { song-=2; mlen=0; pause=0; ioctl(audio_fd,SNDCTL_DSP_RESET,0); }
579 		    }
580 
581 		    if (buffer[0]=='r') {
582 			song=(int) ((float)(argc-1)*rand()/(RAND_MAX+1.0));
583 			mlen=0; pause=0;
584 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
585 			/* printf("\n[%d?]\n",song+1); */
586 		    }
587 
588 		    /*if (buffer[0]=='R') {
589 			song=(int) ((float)(argc-1)*rand()/(RAND_MAX+1.0));
590 			mlen=0; pause=0;
591 		    }*/
592 
593 		    if (buffer[0]=='1') {
594 			/* 11025 hertz */
595 			speed=11025;
596 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
597 			if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) {
598 			    perror("SNDCTL_DSP_SPEED");
599 			    exit(1);
600 			}
601 			settings.mFrequency = 11025;
602 			ModPlug_SetSettings(&settings);
603 			f2=ModPlug_Load(d,size);
604 			ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
605 		    }
606 
607 		    if (buffer[0]=='2') {
608 			/* 22050 hertz */
609 			speed=22050;
610 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
611 			if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) {
612 			    perror("SNDCTL_DSP_SPEED");
613 			    exit(1);
614 			}
615 			settings.mFrequency = 22050;
616 			ModPlug_SetSettings(&settings);
617 			f2=ModPlug_Load(d,size);
618 			ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
619 		    }
620 
621 		    if (buffer[0]=='4') {
622 			/* 44100 hertz */
623 			speed=44100;
624 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
625 			if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) {
626 			    perror("SNDCTL_DSP_SPEED");
627 			    exit(1);
628 			}
629 			settings.mFrequency = 44100;
630 			ModPlug_SetSettings(&settings);
631 			f2=ModPlug_Load(d,size);
632 			ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
633 		    }
634 
635 		    if (buffer[0]=='8') {
636 			/* 8/16 bit */
637 			/*format=;*/
638 			bits^=1;
639 			if (bits) { format=AFMT_U8; } else {
640                             switch (get_byteorder()) {
641                               case 0: format=AFMT_S16_LE; break;
642                               case 1: format=AFMT_S16_BE; break;
643                               default:
644 				printf("could not determine byte order.\n");
645 				return 1;
646 			    }
647 			}
648 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
649 			if (ioctl(audio_fd,SNDCTL_DSP_SETFMT, &format) == -1) {
650 			    perror("SND_CTL_DSP_SETFMT");
651 			    exit(1);
652 			}
653 			if (bits) settings.mBits=8; else settings.mBits=16;
654 			ModPlug_SetSettings(&settings);
655 			f2=ModPlug_Load(d,size);
656 			ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
657 		    }
658 
659 		    if (buffer[0]=='m') {
660 			/* mono/stereo */
661 			mono^=1;
662 			if (mono) channels=1; else channels=2;
663 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
664 			if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
665 			    perror("SNDCTL_DSP_CHANNELS");
666 			    exit(1);
667 			}
668 			if (mono) settings.mChannels=1; else settings.mChannels=2;
669 			ModPlug_SetSettings(&settings);
670 			f2=ModPlug_Load(d,size);
671 			ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000);
672 		    }
673 
674 		    if (buffer[0]=='l') {
675 			loop^=1;
676 			if (loop) {
677 			    memcpy(status+4,"loop",4);
678 			} else {
679 			    memcpy(status+4,"play",4);
680 			}
681 		    } /* loop */
682 
683 		    if (buffer[0]=='+') {
684 			setrelpcmvol(4);
685 		    }
686 
687 		    if (buffer[0]=='-') {
688 			setrelpcmvol(-4);
689 		    }
690 
691 		    if (buffer[0]=='p') {
692 			pause^=1;
693 			ioctl(audio_fd,SNDCTL_DSP_RESET,0);
694 			if (pause) {
695 			    gettimeofday(&tvpause,NULL);
696 			    memcpy(notpaus,status+4,4);
697 			    memcpy(status+4,"paus",4);
698 			} else {
699 			    gettimeofday(&tvunpause,NULL);
700 			    memcpy(status+4,notpaus,4);
701 			    tvptotal.tv_sec+=tvunpause.tv_sec-tvpause.tv_sec;
702 			    tvptotal.tv_usec+=tvunpause.tv_usec-tvpause.tv_usec;
703 			    /* printf(status,tv.tv_sec-tvstart.tv_sec,tv.tv_usec/100000); */
704 			}
705 		    } /* pause */
706                 }
707             }
708         }
709     }
710     printf("\n");
711 
712     reset_keypress();
713     ModPlug_Unload(f2);
714     close(audio_fd);
715     free(d);
716     } /* valid module */
717 
718 } /* for */
719 
720     return 0;
721 }
722 
723