/* ksmp3play - Curses-based MP3 player Copyright (C) 2001 Karl Soderstrom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "ksmp3play.h" #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif int main (int argc, char *argv[]) { char buf[32]; int i = 0, a = 0, y = 0; int ret = 0; int s; int bindings[] = { 0, 'c', 'n', 32, '+', '-', 'e', 'r', 'l', 'h', 'q', 'A', 'S', 'T', 'R', 'd', 's', '/', 'a', ',', '.' }; // We don't want stderr stuff in out nice curses output stderrfp = freopen ("/dev/null", "w", stderr); defaultvol = 80; xterm_title = 0; delay_between_songs = 0; random_method = 2; parse_config (bindings); s = decode_switches (argc, argv); if (s >= argc) usage (0); term = getenv ("TERM"); srand ((unsigned int) time (NULL)); signal (SIGINT, end_program); signal (SIGWINCH, repaint); if ((SDL_Init (SDL_INIT_AUDIO) < 0) || !SDL_AudioDriverName (buf, 1)) { fprintf (stderr, "Warning: Couldn't init SDL audio: %s\n", SDL_GetError ()); exit (1); } i = 0; printf ("Loading ...\n"); for (a = s; a < argc; a++) /* for each playlist/mp3 provided */ load_playlist (argv[a]); if (strlen (ks3playlist) == 0) //if (argc - s == 1) if (!strcmp (&argv[s][strlen (argv[s]) - 4], ".ks3")) strcpy (ks3playlist, argv[s]); getting_times = 1; init_mp3_screen (); for (a = 0; a < num_songs; a++) play[a].mp3file[COLS - 15] = '\0'; if (num_songs < 2) { random_play = 0; ret = y = 0; } else if (random_play) { generate_random_playlist (); ret = y = -1; } while (y < num_songs) { if (ret == -1) { if (random_play) { y = rand () % num_randoms; if (selected == current_song) { selected = current_song = y = random_playlist[y]; pagetop = current_song - (LINES - 9) / 2; } else { current_song = y = random_playlist[y]; } } else { if (current_song == num_songs - 1) { if (repeat_play) y = 0; else end_program (); } else { y++; } if (selected == current_song) selected = current_song = y; else current_song = y; } } else y = current_song = selected; ret = play_mp3 (play[y], bindings); } end_program (); return (0); } int decode_switches (int argc, char **argv) { int c; if (argc < 2) usage (0); while ((c = getopt_long (argc, argv, "hvl::r::p:t::d:m:", long_options, (int *) 0)) != EOF) { switch (c) { case 'v': printf ("ksmp3play %s\n", VERSION); exit (0); case 'h': usage (0); case 'l': if (optarg == 0) repeat_play = 1; else repeat_play = atoi (optarg); break; case 'r': if (optarg == 0) random_play = 1; else random_play = atoi (optarg); break; case 't': if (optarg == 0) xterm_title = 1; else xterm_title = atoi (optarg); break; case 'd': delay_between_songs = atoi (optarg) * 1000; break; case 'm': random_method = atoi (optarg); break; case 'p': strcpy ((char *) ks3playlist, optarg); break; default: usage (0); } } return optind; } int play_mp3 (struct PlayList mp3, int *bindings) { int ch, fd, http = 0; struct PlayList tmpplay; if (strncmp (mp3.file, "http://", strlen ("http://")) == 0) { fd = http_open ((char *) mp3.file); mpeg = SMPEG_new_descr (fd, &mp3_info, 1); http = 1; } else { mpeg = SMPEG_new (mp3.file, &mp3_info, 1); } if (SMPEG_error (mpeg) != NULL) return (-1); //play[current_song].time = mp3_info.total_time; //play[current_song].size = mp3_info.total_size; get_playlist_times (current_song); SMPEG_enableaudio (mpeg, 1); SMPEG_enablevideo (mpeg, 0); if (play[current_song].vol) vol = play[current_song].vol; else vol = defaultvol; SMPEG_setvolume (mpeg, vol); SMPEG_play (mpeg); is_paused = 0; if (!strcmp ((char *) term, (char *) "xterm") && xterm_title) { if (strlen (play[current_song].artist) == 0 && strlen (play[current_song].title) == 0) printf ("\033]2;ksmp3play %s : %s\007", VERSION, play[current_song].mp3file); else printf ("\033]2;ksmp3play %s : %s - %s\007", VERSION, play[current_song].artist, play[current_song].title); } while (is_paused || (SMPEG_status (mpeg) == SMPEG_PLAYING)) { if (!is_paused) SMPEG_getinfo (mpeg, &mp3_info); while (play[q].time && q < num_songs) q++; if (q < num_songs) { getting_times = 1; get_playlist_times (q); q++; } else if (random && random_method == 3 && getting_times) { generate_random_playlist (); getting_times = 0; } show_top_win (mp3_info.current_time); show_playlist (); if ((int) mp3_info.current_time == (int) mp3_info.total_time && !http) /* make sure it stops playing at end of song */ { SMPEG_delete (mpeg); if (delay_between_songs) SDL_Delay (delay_between_songs); return (-1); } ch = getch (); /* grab input (wait for max 0.1 sec) */ /* FIXME: change to switch() */ if (ch == bindings[PAUSE]) /* space */ { if (SMPEG_status (mpeg) == SMPEG_PLAYING) { SMPEG_pause (mpeg); is_paused = 1; } else { SMPEG_play (mpeg); is_paused = 0; SDL_Delay (200); /* pause a while to make sure the info gets updated */ } } else if (ch == bindings[VOLUME_UP]) { if (vol != 100) { playlist_changes = 1; play[current_song].vol = ++vol; if (vol == defaultvol) play[current_song].vol = 0; SMPEG_setvolume (mpeg, vol); } } else if (ch == bindings[VOLUME_DOWN]) { if (vol) { playlist_changes = 1; play[current_song].vol = --vol; if (vol == defaultvol) play[current_song].vol = 0; SMPEG_setvolume (mpeg, vol); } } else if (ch == KEY_RIGHT) { mp3_seek (KEY_RIGHT); } else if (ch == KEY_LEFT) { mp3_seek (KEY_LEFT); } else if (ch == KEY_DOWN) { if (selected < num_songs - 1) selected++; } else if (ch == KEY_UP) { if (selected) selected--; } else if (ch == KEY_NPAGE) { if (selected + 10 < num_songs - 1) { selected += LINES - 9; pagetop += LINES - 9; } else { selected = num_songs - 1; } } else if (ch == KEY_PPAGE) { if (selected - 10 >= 0) { selected -= LINES - 9; pagetop -= LINES - 9; } else { selected = 0; } } else if (ch == KEY_HOME) { selected = 0; } else if (ch == KEY_END) { selected = num_songs - 1; } else if (ch == bindings[MOVE_UP] && selected != 0) { memcpy (&tmpplay, &play[selected], sizeof (struct PlayList)); memcpy (&play[selected], &play[selected - 1], sizeof (struct PlayList)); memcpy (&play[selected - 1], &tmpplay, sizeof (struct PlayList)); if (current_song == selected) current_song--; else if (current_song == selected - 1) current_song++; selected--; if (random_play) generate_random_playlist (); playlist_changes = 1; } else if (ch == bindings[MOVE_DOWN] && selected < num_songs - 1) { memcpy (&tmpplay, &play[selected], sizeof (struct PlayList)); memcpy (&play[selected], &play[selected + 1], sizeof (struct PlayList)); memcpy (&play[selected + 1], &tmpplay, sizeof (struct PlayList)); if (current_song == selected) current_song++; else if (current_song == selected + 1) current_song--; selected++; if (random_play) generate_random_playlist (); playlist_changes = 1; } else if (ch == bindings[RANDOM_MODE] && num_songs > 1) { if (!random_play) { generate_random_playlist (); random_play = 1; } else { random_play = 0; } } else if (ch == bindings[ADD]) { show_filebrowser (); } else if (ch == bindings[LOOP_MODE]) { if (!repeat_play) repeat_play = 1; else repeat_play = 0; } else if (ch == bindings[NEXT]) { SMPEG_delete (mpeg); return (-1); } else if (ch == bindings[HELP]) { show_help (bindings); } else if (ch == bindings[QUIT]) { if (playlist_changes) if (confirm ("You have not saved your playlist", "Save it now?")) write_playlist (); end_program (); } else if (ch == 10) /* enter */ { SMPEG_delete (mpeg); return (selected); } else if (ch == bindings[DELETE]) { if (confirm ("", "Delete from playlist?")) { if (delete_song (selected)) { SMPEG_delete (mpeg); if (random_play) generate_random_playlist (); return (current_song); } if (random_play) generate_random_playlist (); } } else if (ch >= 49 && ch <= 57) /* 1 - 9 */ { playlist_changes = 1; play[selected].rate = ch - 48; if (random_play) generate_random_playlist (); } else if (ch == bindings[EDIT_ID3]) { show_id3tag_win (); } else if (ch == bindings[SAVE]) { write_playlist (); } else if (ch == bindings[SEARCH]) { show_search_win (); } else if (ch == bindings[JUMP_TO_CURRENT]) { selected = current_song; pagetop = current_song - (LINES - 9) / 2; } else if (ch == bindings[SORT_R]) { if (playlist_sort == 1) sort_playlist (5); else sort_playlist (1); if (q != num_songs) q = 0; } else if (ch == bindings[SORT_T]) { if (playlist_sort == 2) sort_playlist (6); else sort_playlist (2); if (q != num_songs) q = 0; } else if (ch == bindings[SORT_A]) { if (playlist_sort == 3) sort_playlist (7); else sort_playlist (3); if (q != num_songs) q = 0; } else if (ch == bindings[SORT_S]) { if (playlist_sort == 4) sort_playlist (8); else sort_playlist (4); if (q != num_songs) q = 0; } } SMPEG_delete (mpeg); if (delay_between_songs) SDL_Delay (delay_between_songs); return (-1); } void mp3_seek (int key) { if (!is_paused) SMPEG_pause (mpeg); while (1) { SMPEG_getinfo (mpeg, &mp3_info); if (key == KEY_RIGHT) { if (mp3_info.current_time + 5.0 < mp3_info.total_time) SMPEG_seek (mpeg, (int) ((float) ((float) (mp3_info.current_time + 5.0) / (float) mp3_info.total_time) * mp3_info.total_size)); } else if (key == KEY_LEFT) { if (mp3_info.current_time - 5.0 > 0.0) SMPEG_seek (mpeg, (int) ((float) ((float) (mp3_info.current_time - 5.0) / (float) mp3_info.total_time) * mp3_info.total_size)); else SMPEG_seek (mpeg, 1); } SMPEG_getinfo (mpeg, &mp3_info); show_top_win (mp3_info.current_time); if (getch () != key) break; } if (!is_paused) { SMPEG_play (mpeg); } } char * stristr (const char *String, const char *Pattern) { char *pptr, *sptr, *start; unsigned int slen, plen; for (start = (char *) String, pptr = (char *) Pattern, slen = strlen (String), plen = strlen (Pattern); /* while string length not shorter than pattern length */ slen >= plen; start++, slen--) { /* find start of pattern in string */ while (toupper (*start) != toupper (*Pattern)) { start++; slen--; /* if pattern longer than string */ if (slen < plen) return (NULL); } sptr = start; pptr = (char *) Pattern; while (toupper (*sptr) == toupper (*pptr)) { sptr++; pptr++; /* if end of pattern then pattern was found */ if ('\0' == *pptr) return (start); } } return (NULL); } void usage (int status) { printf ("ksmp3play - \ Curses-based MP3 player\n"); printf ("Usage: ksmp3play [OPTION ...] FILE1 [FILE2 ...]\n"); printf ("\ Options:\n\ -h, --help display this help and exit\n\ -v, --version output version information and exit\n\ -r, --random start in random play mode (0/1, default=1)\n\ -l, --loop start in loop play mode (0/1, default=1)\n\ -t, --title set xterm title (0/1, default=1)\n\ -d, --delay delay between songs (in seconds)\n\ -m, --rmethod method used for random play (1/2/3)\n\ -p, --playlist default playlist to save to\n\ "); exit (status); } void end_program () { endwin (); fclose (stderrfp); // SMPEG_delete (mpeg); SDL_Quit (); exit (0); } void parse_config (int *bindings) { char *home, conffile[256], line[256], *var, *val; FILE *infile; int tmpbind; home = getenv ("HOME"); sprintf ((char *) &conffile, "%s/.ksmp3playrc", home); infile = fopen (conffile, "r"); if (infile) { while (fgets (line, 255, infile)) if (line[0] != '#' && line[0] != ' ' && line[0] != '\n') { var = strtok ((char *) &line, " = "); val = strtok (NULL, " = "); val[strlen (val) - 1] = '\0'; tmpbind = is_bind (var); if (tmpbind) bindings[tmpbind] = get_val (val); else if (strcasecmp (var, "random") == 0) { if (atoi (val) == 0 || atoi (val) == 1) { random_play = atoi (val); } else { printf ("config: random can only take a value of 0 or 1.\n"); exit (1); } } else if (strcasecmp (var, "loop") == 0) { if (atoi (val) == 0 || atoi (val) == 1) { repeat_play = atoi (val); } else { printf ("config: loop can only take a value of 0 or 1.\n"); exit (1); } } else if (strcasecmp (var, "set_xterm_title") == 0) { if (atoi (val) == 0 || atoi (val) == 1) { xterm_title = atoi (val); } else { printf ("config: set_xterm_title can only take a value of 0 or 1.\n"); exit (1); } } else if (strcasecmp (var, "random_method") == 0) { if (0 < atoi (val) < 4) { random_method = atoi (val); } else { printf ("config: random_method can only take a value between 1 and 3.\n"); exit (1); } } else if (strcasecmp (var, "delay_between_songs") == 0) { if (0 < atoi (val) < 10) { delay_between_songs = atoi (val) * 1000; } else { printf ("config: delay_between_songs can only take a value between 0 or 10.\n"); exit (1); } } else if (strcasecmp (var, "volume") == 0) { if (atoi (val) >= 0 && atoi (val) <= 100) { defaultvol = atoi (val); } else { printf ("config: volume can only take a value between 0 and 100.\n"); exit (1); } } else { printf ("config: Unkown variable (%s)\n", var); exit (1); } } fclose (infile); } } int get_val (char *val) { if (val[0] == '\\') return (atoi (++val)); else return (val[0]); } int is_bind (char *var) { if (strcasecmp (var, "JUMP_TO_CURRENT") == 0) return (JUMP_TO_CURRENT); else if (strcasecmp (var, "NEXT") == 0) return (NEXT); else if (strcasecmp (var, "PAUSE") == 0) return (PAUSE); else if (strcasecmp (var, "VOLUME_UP") == 0) return (VOLUME_UP); else if (strcasecmp (var, "VOLUME_DOWN") == 0) return (VOLUME_DOWN); else if (strcasecmp (var, "EDIT_ID3") == 0) return (EDIT_ID3); else if (strcasecmp (var, "RANDOM_MODE") == 0) return (RANDOM_MODE); else if (strcasecmp (var, "LOOP_MODE") == 0) return (LOOP_MODE); else if (strcasecmp (var, "HELP") == 0) return (HELP); else if (strcasecmp (var, "QUIT") == 0) return (QUIT); else if (strcasecmp (var, "SORT_A") == 0) return (SORT_A); else if (strcasecmp (var, "SORT_S") == 0) return (SORT_S); else if (strcasecmp (var, "SORT_T") == 0) return (SORT_T); else if (strcasecmp (var, "SORT_R") == 0) return (SORT_R); else if (strcasecmp (var, "DELETE") == 0) return (DELETE); else if (strcasecmp (var, "SAVE") == 0) return (SAVE); else if (strcasecmp (var, "SEARCH") == 0) return (SEARCH); else if (strcasecmp (var, "ADD") == 0) return (ADD); else if (strcasecmp (var, "MOVE_UP") == 0) return (MOVE_UP); else if (strcasecmp (var, "MOVE_DOWN") == 0) return (MOVE_DOWN); else return (0); }