/* commandline interface to modplugxmms library gurkan@linuks.mine.nu www.linuks.mine.nu Copyright (C) 2003 Gurkan Sengun TODO unlock /dev/dsp when in 'p'ause mode and have a look at SNDCTL_DSP_GETxPTR more functions sig handling 12:23 < tp76> tarzeau: yes. I prefer `T *foo = malloc (N * sizeof *foo);'. use stat() to get filesize run indent -kr 14:08 < tarzeau> so the player isn't stuck when anotehr thing is using /dev/dsp 14:08 < tarzeau> if that works a msg is displayed 14:08 < tarzeau> later the song is tried to be opened 14:08 < tarzeau> then the soundcard settings are set 14:09 < tarzeau> the the module is load using libmodplug 14:09 < tarzeau> module settings being set 14:09 < tarzeau> keyboard handling set 14:09 < tarzeau> the status string set up 14:09 < tarzeau> time stuff and the main loop 14:09 < warp> ok. 14:09 < tarzeau> where keyboard input is handled 14:09 < tarzeau> that's more or less it 14:10 < warp> see, you've just described all the functions you could make. 14:10 < warp> so, start that for loop with a init_sound_device () or something. 14:10 < warp> etc... make functions for all the things you just described. 14:10 < tarzeau> heh good idea, i'll copy paste this irc talk and do that for the next version like 0.9 with the sighandler for people trying to ctrl-c or ctlr-z maybe add support for write a .wav file for later play or .ogg and maybe add support for gzip -d some.mod.gz | modplugplay add support to allow modplugplay *.IT *.it *.s3m *.xm (n)ext (N)ot next and -r for random order (make a random played/unplayed (bit)list) show file size argument processing if it's not a file, we check if it's an option if not we ignore it action keys -> like key (put in list/directory) -> dislike key (remove) -> show info || [] >> << >| () TODO or maybe just a $HOME/.modplugplay or /etc/modplugplay command line option handling -l --loop -m --mono -s --stereo (default) -4 --44100 (default) -2 --22050 -1 --11025 -8 --8bit (default is 16bit) -h --help -q --quiet -v --version (head -c1080 module |strings) -i --info don't play, only show module info (songname,length,size,type) */ #include /* printf */ #include /* strcpy */ #include /* srand/rand */ #include #include /* core */ #include /* control device */ #include #ifdef __NetBSD__ #include #else #include #endif #include /* gettimeofday */ #include #include /* poll for keyboard input */ #include /* needed to get terminal size */ #define VERSION "1.0" #define BUF_SIZE 4096 #define DEVICE_NAME "/dev/dsp" static struct termios stored_settings; int audio_fd; int mixer_fd; unsigned char audio_buffer[BUF_SIZE]; char order[3]; /* inquire actual terminal size */ static int get_term_size(int fd, int *x, int *y) { #ifdef TIOCGSIZE struct ttysize win; #elif defined(TIOCGWINSZ) struct winsize win; #endif #ifdef TIOCGSIZE if (ioctl(fd,TIOCGSIZE,&win)) return 0; if (y) *y=win.ts_lines; if (x) *x=win.ts_cols; #elif defined TIOCGWINSZ if (ioctl(fd,TIOCGWINSZ,&win)) return 0; if (y) *y=win.ws_row; if (x) *x=win.ws_col; #else { const char *s; s=getenv("LINES"); if (s) *y=strtol(s,NULL,10); else *y=25; s=getenv("COLUMNS"); if (s) *x=strtol(s,NULL,10); else *x=80; } #endif return 1; } void set_keypress(void) { struct termios new_settings; tcgetattr(0,&stored_settings); new_settings=stored_settings; new_settings.c_lflag &= (~ICANON); new_settings.c_lflag &= (~ECHO); new_settings.c_cc[VTIME] = 0; new_settings.c_cc[VMIN] = 1; tcsetattr(0,TCSANOW,&new_settings); return; } void reset_keypress(void) { tcsetattr(0,TCSANOW,&stored_settings); return; } void ansi_cursor(int visible) { if (visible) { printf("\033[?25h"); } else { printf("\033[?25l"); } } void help(char *s) { printf("Copyright (C) 2003 Gurkan Sengun\n"); printf("Version %s compiled on %s at %s.\n",VERSION,__DATE__,__TIME__); printf("\n"); printf("%s: too few arguments\n",s); printf("Usage: modplugplay" /*[OPTIONS]*/" [FILES]\n"); printf("\n"); /* printf(" -v print version info\n"); printf(" -h print help\n"); printf(" -l start in looping mode\n"); */ /* printf(" -r randomize play sequence\n"); printf(" -c write to console instead of %s\n",DEVICE_NAME); printf(" -i use stdin for file input\n"); printf(" -q be quiet\n"); */ exit(1); } int get_byteorder(void) /* 0 LE little endian (intel) 1 BE big endian (sparc, powerpc, motorola) 2 unknown */ { int ival; #define sz sizeof(ival) char s[sz]; char t[sz]; int i, lit, big; for (i=0; i<(int)sz; i++) s[i] = i; ival = *(int *)s; big = lit = 0; for (i=0; i<(int)sz; i++) { char c = ival&0xff; ival >>= 8; if (s[i] == c) lit++; if (s[sz-i-1] == c) big++; t[i] = c; } if (lit == sz && big == 0) { return 0; } else if (big == sz && lit == 0) { return 1; } else { return 2; } } char *getFileData(char *filename, long *size) { FILE *f; char *data; f = fopen(filename, "rb"); fseek(f, 0L, SEEK_END); (*size) = ftell(f); rewind(f); data = (char*)malloc(*size); fread(data, *size, sizeof(char), f); fclose(f); return(data); } int getpcmvol() /* get absolute pcm volume, 0 - 100 (for left+right) */ { int vol; if ((mixer_fd=open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0)) == -1) { printf("can't open mixer\n"); exit(1); } ioctl(mixer_fd,MIXER_READ(SOUND_MIXER_PCM),&vol); close(mixer_fd); vol=vol & 255; if (vol==100) vol=99; return(vol); } int setrelpcmvol(int newvol) /* set relative pcm volume, -100 .. 100 */ { int currentvol=getpcmvol(); currentvol+=newvol; if (currentvol>100) currentvol=100; if (currentvol<0) currentvol=0; newvol=currentvol+(currentvol*256); if ((mixer_fd=open("/dev/mixer", O_RDONLY | O_NONBLOCK, 0)) == -1) { printf("can't open mixer\n"); exit(1); } ioctl(mixer_fd,MIXER_WRITE(SOUND_MIXER_PCM),&newvol); close(mixer_fd); return 0; } int main(int argc, char* argv[]) { FILE *f; long size; char *d; int x,y; ModPlugFile *f2; int len,mlen; struct timeval tvstart; struct timeval tv; struct timeval tvpause; struct timeval tvunpause; struct timeval tvptotal; char status[161]; char songname[41]; char notpaus[4]; /* int vol=getpcmvol(); printf("V%02x\n",vol); */ /* struct count_info ci; ioctl(audio_fd,SNDCTL_DSP_GETOPTR,&ci); typedef struct count_info { int bytes; // Total # of bytes processed int blocks; // # of fragment transitions since last time int ptr; // Current DMA pointer value } count_info; */ ModPlug_Settings settings; ModPlug_GetSettings(&settings); int format; int channels = 2; int speed = 44100; char buffer[128]; int result, nread; struct pollfd pollfds; int timeout = 1; /* Timeout in msec. */ int loop=0; int pause=0; int mono=0; int bits=0; int song; /* what about N if the previous song is not playable? */ /* maybe mark it played in randplayed */ // [rev--dly--] [sur--dly--] [bas--rng--] if (get_byteorder()==0) { format=AFMT_S16_LE; strcpy(order,"LE"); } else if (get_byteorder()==1) { format=AFMT_S16_BE; strcpy(order,"BE"); } else { printf("could not determine byte order.\n"); strcpy(order,"??"); return 1; } /* Initialize pollfds; we're looking at input, stdin */ pollfds.fd = 0; /* stdin */ pollfds.events = POLLIN; /* Wait for input */ if (argc==1) { help(argv[0]); } /* if (strstr(argv[1],"-v")) { printf("Copyright (C) 2003 Gurkan Sengun\n"); printf("Version %s compiled on %s at %s.\n",VERSION,__DATE__,__TIME__); exit(0); } if (strstr(argv[1],"-l")) { loop=1; } */ /* if(access(DEVICE_NAME,W_OK)!=0) { printf("failed! (no rights to write to %s)\n",DEVICE_NAME); exit(1); }*/ if (!get_term_size(STDIN_FILENO,&x,&y)) { fprintf(stderr,"warning: failed to get terminal size\n"); } srand(time(NULL)); for (song=1; song 0) { ioctl(audio_fd,SNDCTL_DSP_RESET,0); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000-10000); tvstart.tv_sec+=10; } } /* backward 10" */ /* if (buffer[0]=='i') { printf("\n"); } */ /* if (buffer[0]=='a') { rev++; settings.mReverbDepth=rev; ModPlug_SetSettings(&settings); } if (buffer[0]=='A') { rev--; settings.mReverbDepth=rev; ModPlug_SetSettings(&settings); } if (buffer[0]=='s') { revdly++; settings.mReverbDelay=revdly; ModPlug_SetSettings(&settings); } if (buffer[0]=='S') { revdly--; settings.mReverbDelay=revdly; ModPlug_SetSettings(&settings); } if (buffer[0]=='d') { sur++; settings.mSurroundDepth=sur; ModPlug_SetSettings(&settings); } if (buffer[0]=='D') { sur--; settings.mSurroundDepth=sur; ModPlug_SetSettings(&settings); } if (buffer[0]=='y') { surdly++; settings.mSurroundDelay=surdly; ModPlug_SetSettings(&settings); } if (buffer[0]=='Y') { surdly--; settings.mSurroundDelay=surdly; ModPlug_SetSettings(&settings); } if (buffer[0]=='x') { bas++; settings.mBassAmount=bas; ModPlug_SetSettings(&settings); } if (buffer[0]=='X') { bas--; settings.mBassAmount=bas; ModPlug_SetSettings(&settings); } if (buffer[0]=='c') { basrng++; settings.mBassRange=basrng; ModPlug_SetSettings(&settings); } if (buffer[0]=='C') { basrng--; settings.mBassRange=basrng; ModPlug_SetSettings(&settings); } */ if (buffer[0]=='n') { if (song1) { song-=2; mlen=0; pause=0; ioctl(audio_fd,SNDCTL_DSP_RESET,0); } } if (buffer[0]=='r') { song=(int) ((float)(argc-1)*rand()/(RAND_MAX+1.0)); mlen=0; pause=0; ioctl(audio_fd,SNDCTL_DSP_RESET,0); /* printf("\n[%d?]\n",song+1); */ } /*if (buffer[0]=='R') { song=(int) ((float)(argc-1)*rand()/(RAND_MAX+1.0)); mlen=0; pause=0; }*/ if (buffer[0]=='1') { /* 11025 hertz */ speed=11025; ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) { perror("SNDCTL_DSP_SPEED"); exit(1); } settings.mFrequency = 11025; ModPlug_SetSettings(&settings); f2=ModPlug_Load(d,size); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000); } if (buffer[0]=='2') { /* 22050 hertz */ speed=22050; ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) { perror("SNDCTL_DSP_SPEED"); exit(1); } settings.mFrequency = 22050; ModPlug_SetSettings(&settings); f2=ModPlug_Load(d,size); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000); } if (buffer[0]=='4') { /* 44100 hertz */ speed=44100; ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (ioctl(audio_fd,SNDCTL_DSP_SPEED,&speed) == -1) { perror("SNDCTL_DSP_SPEED"); exit(1); } settings.mFrequency = 44100; ModPlug_SetSettings(&settings); f2=ModPlug_Load(d,size); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000); } if (buffer[0]=='8') { /* 8/16 bit */ /*format=;*/ bits^=1; if (bits) { format=AFMT_U8; strcpy(order,""); } else { if (get_byteorder()==0) { format=AFMT_S16_LE; strcpy(order,"LE"); } else if (get_byteorder()==1) { format=AFMT_S16_BE; strcpy(order,"BE"); } else { printf("could not determine byte order.\n"); strcpy(order,"??"); return 1; } } ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (ioctl(audio_fd,SNDCTL_DSP_SETFMT, &format) == -1) { perror("SND_CTL_DSP_SETFMT"); exit(1); } if (bits) settings.mBits=8; else settings.mBits=16; ModPlug_SetSettings(&settings); f2=ModPlug_Load(d,size); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000); } if (buffer[0]=='m') { /* mono/stereo */ mono^=1; if (mono) channels=1; else channels=2; ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { perror("SNDCTL_DSP_CHANNELS"); exit(1); } if (mono) settings.mChannels=1; else settings.mChannels=2; ModPlug_SetSettings(&settings); f2=ModPlug_Load(d,size); ModPlug_Seek(f2,(tv.tv_sec-tvstart.tv_sec-tvptotal.tv_sec)*1000+10000); } if (buffer[0]=='l') { loop^=1; if (loop) { memcpy(status+4,"loop",4); } else { memcpy(status+4,"play",4); } } /* loop */ if (buffer[0]=='+') { setrelpcmvol(4); } if (buffer[0]=='-') { setrelpcmvol(-4); } if (buffer[0]=='p') { pause^=1; ioctl(audio_fd,SNDCTL_DSP_RESET,0); if (pause) { gettimeofday(&tvpause,NULL); memcpy(notpaus,status+4,4); memcpy(status+4,"paus",4); } else { gettimeofday(&tvunpause,NULL); memcpy(status+4,notpaus,4); tvptotal.tv_sec+=tvunpause.tv_sec-tvpause.tv_sec; tvptotal.tv_usec+=tvunpause.tv_usec-tvpause.tv_usec; /* printf(status,tv.tv_sec-tvstart.tv_sec,tv.tv_usec/100000); */ } } /* pause */ } } } } printf("\n"); reset_keypress(); ModPlug_Unload(f2); close(audio_fd); free(d); } /* valid module */ } /* for */ return 0; }