/* 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 void init_mp3_screen () { initscr (); if (LINES < 24 || COLS < 80) { endwin (); printf ("Needs atleast a 80x24 display...\n"); end_program (); } keypad (stdscr, TRUE); nocbreak (); noecho (); halfdelay (1); curs_set (0); if (has_colors ()) { start_color (); init_pair (1, COLOR_WHITE, COLOR_BLACK); init_pair (2, COLOR_WHITE, COLOR_BLUE); init_pair (3, COLOR_WHITE, COLOR_RED); init_pair (4, COLOR_WHITE, COLOR_WHITE); } bottom = newwin (LINES - 7, COLS, 7, 0); top = newwin (7, COLS, 0, 0); wbkgd (top, ' ' | COLOR_PAIR (2)); wbkgd (bottom, ' ' | COLOR_PAIR (1)); leaveok (top, FALSE); leaveok (bottom, FALSE); typeahead (-1); } void show_top_win (float current_time) { char time_string[14]; char topversion[25]; werase (top); box (top, 0, 0); wattr_on (top, COLOR_PAIR (2) | A_BOLD, NULL); sprintf ((char *) &topversion, " ksmp3play %s ", VERSION); mvwprintw (top, 0, COLS - strlen (topversion) - 2, "%s", topversion); mvwprintw (top, 5, 2, "[ ]"); if (!random_play) wattr_off (top, A_BOLD, NULL); mvwprintw (top, 5, 3, "Random"); wattr_on (top, A_BOLD, NULL); mvwprintw (top, 5, 11, "[ ]"); if (!repeat_play) wattr_off (top, A_BOLD, NULL); mvwprintw (top, 5, 12, "Loop"); wattr_on (top, A_BOLD, NULL); mvwprintw (top, 1, COLS - 5, "(%d)", play[current_song].rate); mvwprintw (top, 2, COLS - 17, "Vol:"); wattr_on (top, COLOR_PAIR (3), NULL); mvwhline (top, 2, COLS - 12, 32, 10); mvwhline (top, 4, 2, 32, COLS - 4); wattr_on (top, COLOR_PAIR (2) | A_BOLD, NULL); if (strlen (play[current_song].artist) == 0 && strlen (play[current_song].title) == 0) mvwprintw (top, 1, 2, "%s", play[current_song].mp3file); else mvwprintw (top, 1, 2, "%s - %s", play[current_song].artist, play[current_song].title); wattr_off (top, A_BOLD, NULL); mvwprintw (top, 2, 2, "(%s)", mp3_info.audio_string); wattr_on (top, COLOR_PAIR (4), NULL); mvwhline (top, 2, COLS - 12, 32, vol / 10); mvwhline (top, 4, 2, 32, current_time / mp3_info.total_time * (COLS - 4)); wattr_on (top, COLOR_PAIR (2) | A_BOLD, NULL); sprintf (time_string, "%d:%.2d / %d:%.2d", ((int) current_time / 60), ((int) current_time - (int) ((int) current_time / 60) * 60), ((int) mp3_info.total_time / 60), ((int) mp3_info.total_time - (int) ((int) mp3_info.total_time / 60) * 60)); mvwprintw (top, 5, (COLS - 2) - 5 - strlen (time_string), " %s", time_string); wrefresh (top); } void show_playlist () { int rows = (LINES - 9); int i; int total_h, total_m, total_s; float size; char unit[3]; werase (bottom); wattr_on (bottom, A_BOLD, NULL); box (bottom, 0, 0); if (total_size > 1048576) { size = (float) total_size / 1048576; strcpy ((char *) &unit, "Gb"); } else { size = (float) total_size / 1024; strcpy ((char *) &unit, "Mb"); } total_h = (int) total_time / 3600; total_m = (int) (total_time - (total_h * 3600)) / 60; total_s = total_time - (total_h * 3600) - (total_m * 60); mvwprintw (bottom, rows + 1, 4, " Total: %d files, %.1f %s, %dh %.2dm %.2ds ", num_songs, size, unit, total_h, total_m, total_s); wattr_off (bottom, A_BOLD, NULL); wattr_on (bottom, COLOR_PAIR (2), NULL); mvwvline (bottom, 1, COLS - 2, 32, rows); wattr_on (bottom, COLOR_PAIR (3), NULL); mvwvline (bottom, (int) ((float) selected / num_songs * rows) + 1, COLS - 2, 32, 1); wattr_on (bottom, COLOR_PAIR (1), NULL); if (selected >= pagetop + rows) pagetop += rows - 2; else if (selected < pagetop) pagetop -= rows - 2; if (pagetop < 0) pagetop = 0; else if (pagetop >= num_songs) pagetop = num_songs - 1; for (i = 0; i < rows; i++) { if (i + pagetop < num_songs) { if (i + pagetop == selected) wattr_on (bottom, COLOR_PAIR (3), NULL); else wattr_on (bottom, COLOR_PAIR (1), NULL); mvwhline (bottom, i + 1, 2, 32, COLS - 4); if (i + pagetop == current_song) wattr_on (bottom, A_BOLD, NULL); if (strlen (play[i + pagetop].artist) == 0 && strlen (play[i + pagetop].title) == 0) mvwprintw (bottom, i + 1, 1, " %s", play[i + pagetop].mp3file); else mvwprintw (bottom, i + 1, 1, " %s - %s", play[i + pagetop].artist, play[i + pagetop].title); if (play[i + pagetop].time) { mvwprintw (bottom, i + 1, COLS - 8, "%.2d:%.2d", ((int) play[i + pagetop].time / 60), ((int) play[i + pagetop].time - (int) ((int) play[i + pagetop].time / 60) * 60)); } mvwprintw (bottom, i + 1, COLS - 12, "(%d)", play[i + pagetop].rate); wattr_off (bottom, A_BOLD | COLOR_PAIR (1) | COLOR_PAIR (3), NULL); } } wrefresh (bottom); } void show_id3tag_win () { WINDOW *id3_win; char artist[31]; char title[31]; char *artistp; char *titlep; int answer; strcpy (artist, play[selected].artist); strcpy (title, play[selected].title); artistp = artist; titlep = title; id3_win = subwin (bottom, 8, 43, 8 + ((LINES - 17) / 2), (COLS - 43) / 2); curs_set (1); echo (); werase (id3_win); wbkgd (id3_win, COLOR_PAIR (2) | A_BOLD); box (id3_win, 0, 0); mvwprintw (id3_win, 2, 2, "Artist:"); mvwprintw (id3_win, 4, 2, "Title:"); wattr_on (id3_win, COLOR_PAIR (3), NULL); mvwhline (id3_win, 2, 10, 32, 31); mvwhline (id3_win, 4, 10, 32, 31); wrefresh (id3_win); mvwgetnstr (id3_win, 2, 10, artistp, 30); mvwgetnstr (id3_win, 4, 10, titlep, 30); wattr_on (id3_win, COLOR_PAIR (2), NULL); mvwprintw (id3_win, 6, 2, "Are you sure? [Y/n]"); curs_set (0); noecho (); nodelay (id3_win, FALSE); notimeout (id3_win, TRUE); answer = wgetch (id3_win); if (answer != 'n' && answer != 'N') { strcpy (play[selected].artist, artist); strcpy (play[selected].title, title); writeID3 (play[selected].file, play[selected].title, play[selected].artist); } halfdelay (1); delwin (id3_win); } int show_ks3playlist_win () { WINDOW *ks3_win; char filename[100]; int answer; ks3_win = subwin (bottom, 6, COLS - 10, 6 + ((LINES - 17) / 2), (COLS - (COLS - 10)) / 2); curs_set (1); echo (); werase (ks3_win); wbkgd (ks3_win, COLOR_PAIR (2) | A_BOLD); box (ks3_win, 0, 0); mvwprintw (ks3_win, 2, 2, "Filename:"); mvwprintw (ks3_win, 2, COLS - 16, ".ks3"); wattr_on (ks3_win, COLOR_PAIR (3), NULL); mvwhline (ks3_win, 2, 12, 32, COLS - 28); wrefresh (ks3_win); mvwgetnstr (ks3_win, 2, 12, (char *) &filename, COLS - 28); if (strlen (filename) == 0) return (0); wattr_on (ks3_win, COLOR_PAIR (2), NULL); mvwprintw (ks3_win, 4, 2, "Are you sure? [Y/n]"); curs_set (0); noecho (); nodelay (ks3_win, FALSE); notimeout (ks3_win, TRUE); answer = wgetch (ks3_win); halfdelay (1); delwin (ks3_win); if (answer != 'n' && answer != 'N') { sprintf ((char *) &ks3playlist, "%s.ks3", filename); return (1); } return (0); } int confirm (char statement[100], char question[100]) { WINDOW *confirm_win; int answer; int width, height; if (strlen (statement)) { if (strlen (statement) > strlen (question) + 6) width = strlen (statement) + 4; else width = strlen (question) + 10; height = 6; } else { width = strlen (question) + 10; height = 5; } confirm_win = subwin (bottom, height, width, 7 + ((LINES - 7 - height) / 2), (COLS - width) / 2); werase (confirm_win); wbkgd (confirm_win, COLOR_PAIR (2) | A_BOLD); box (confirm_win, 0, 0); wattr_on (confirm_win, COLOR_PAIR (2), NULL); if (strlen (statement)) mvwprintw (confirm_win, 2, 2, "%s", statement); mvwprintw (confirm_win, height - 3, 2, "%s [Y/n]", question); nodelay (confirm_win, FALSE); notimeout (confirm_win, TRUE); answer = wgetch (confirm_win); halfdelay (1); delwin (confirm_win); if (answer != 'n' && answer != 'N') { return (1); } return (0); } void show_search_win () { WINDOW *search_win; char *found1; char *found2; char *found3; int cursong; char newsearchfor[27]; search_win = subwin (bottom, 6, 43, 8 + ((LINES - 17) / 2), (COLS - 43) / 2); curs_set (1); echo (); werase (search_win); wbkgd (search_win, COLOR_PAIR (2) | A_BOLD); box (search_win, 0, 0); mvwprintw (search_win, 2, 2, "Search for:"); if (strlen (searchfor)) mvwprintw (search_win, 3, 2, "[ %s ]", searchfor); wattr_on (search_win, COLOR_PAIR (3), NULL); mvwhline (search_win, 2, 14, 32, 27); wrefresh (search_win); mvwgetnstr (search_win, 2, 14, newsearchfor, 26); if (strlen (newsearchfor)) strcpy (searchfor, newsearchfor); if (strlen (searchfor)) { for (cursong = selected + 1; cursong < num_songs; cursong++) { found1 = stristr (play[cursong].mp3file, searchfor); found2 = stristr (play[cursong].artist, searchfor); found3 = stristr (play[cursong].title, searchfor); if (found1 != NULL || found2 != NULL || found3 != NULL) { selected = cursong; break; } } if (selected != cursong) for (cursong = 0; cursong < selected; cursong++) { found1 = stristr (play[cursong].mp3file, searchfor); found2 = stristr (play[cursong].artist, searchfor); found3 = stristr (play[cursong].title, searchfor); if (found1 != NULL || found2 != NULL || found3 != NULL) { selected = cursong; break; } } pagetop = selected - (LINES - 9) / 2; } curs_set (0); noecho (); delwin (search_win); } void get_bind (int *bindings, int bind, char *ch) { if (bindings[bind] > 32) sprintf (ch, "%c", bindings[bind]); else if (bindings[bind] == 9) strcpy (ch, "TAB"); else if (bindings[bind] == 10 || bindings[bind] == 13) strcpy (ch, "ENTER"); else if (bindings[bind] == 32) strcpy (ch, "SPACE"); } void show_help (int *bindings) { int help_rows = 28; struct help_struct { char key[20]; char desc[50]; } help[help_rows]; WINDOW *help_win; int col, rows; int c, i, current = 0; strcpy (help[0].key, "LEFT"); strcpy (help[0].desc, "Seek left within song"); strcpy (help[1].key, "RIGHT"); strcpy (help[1].desc, "Seek right within song"); strcpy (help[2].key, "UP"); strcpy (help[2].desc, "Move up"); strcpy (help[3].key, "DOWN"); strcpy (help[3].desc, "Move down"); strcpy (help[4].key, "PGUP"); strcpy (help[4].desc, "Jump up"); strcpy (help[5].key, "PGDN"); strcpy (help[5].desc, "Jump down"); strcpy (help[6].key, "ENTER"); strcpy (help[6].desc, "Select song"); get_bind (bindings, PAUSE, help[7].key); strcpy (help[7].desc, "Pause/unpause"); get_bind (bindings, VOLUME_UP, help[8].key); strcpy (help[8].desc, "Increase volume for current song"); get_bind (bindings, VOLUME_DOWN, help[9].key); strcpy (help[9].desc, "Decrease volume for current song"); get_bind (bindings, NEXT, help[10].key); strcpy (help[10].desc, "Next song"); get_bind (bindings, SEARCH, help[11].key); strcpy (help[11].desc, "Search within the playlist"); get_bind (bindings, SORT_A, help[12].key); strcpy (help[12].desc, "Sort playlist according to Artist"); get_bind (bindings, SORT_S, help[13].key); strcpy (help[13].desc, "Sort playlist according to Song title"); get_bind (bindings, SORT_T, help[14].key); strcpy (help[14].desc, "Sort playlist according to Time"); get_bind (bindings, SORT_R, help[15].key); strcpy (help[15].desc, "Sort playlist according to Rating"); strcpy (help[16].key, "1-9"); strcpy (help[16].desc, "Set rating of selected song"); get_bind (bindings, ADD, help[17].key); strcpy (help[17].desc, "Add files to playlist"); get_bind (bindings, RANDOM_MODE, help[18].key); strcpy (help[18].desc, "Enable/disable random play mode"); get_bind (bindings, LOOP_MODE, help[19].key); strcpy (help[19].desc, "Enable/disable loop play mode"); get_bind (bindings, EDIT_ID3, help[20].key); strcpy (help[20].desc, "Edit ID3 tags for selected song"); get_bind (bindings, SAVE, help[21].key); strcpy (help[21].desc, "Save playlist"); get_bind (bindings, JUMP_TO_CURRENT, help[22].key); strcpy (help[22].desc, "Jump to currently playing song"); get_bind (bindings, QUIT, help[23].key); strcpy (help[23].desc, "Quit"); get_bind (bindings, HELP, help[24].key); strcpy (help[24].desc, "Show this help screen"); get_bind (bindings, DELETE, help[25].key); strcpy (help[25].desc, "Delete selected song"); get_bind (bindings, MOVE_UP, help[26].key); strcpy (help[26].desc, "Move selected song up"); get_bind (bindings, MOVE_DOWN, help[27].key); strcpy (help[27].desc, "Move selected song down"); halfdelay (255); help_win = newwin (LINES - 3, COLS - 10, 2, 5); werase (help_win); wbkgd (help_win, COLOR_PAIR (2) | A_BOLD); box (help_win, 0, 0); rows = LINES - 8; while (1) { for (i = 0; i < rows && i < help_rows; i++) { mvwprintw (help_win, i + 2, 5, "%s", help[i + current].key); mvwprintw (help_win, i + 2, 13, "%s", help[i + current].desc); } mvwprintw (help_win, rows + 3, 5, "Use arrows to move up/down and Q to close help screen"); wrefresh (help_win); c = getch (); if (c == 'q') break; else if (c == KEY_DOWN && help_rows - current > rows) current++; else if (c == KEY_UP && current) current--; werase (help_win); box (help_win, 0, 0); } wclear (stdscr); halfdelay (1); delwin (help_win); } void show_filebrowser () { int file_select (); int sort_dirs_first (); int file_top = 0, num_added_files = 0; char added_files[1000][256], tmp[256]; WINDOW *filebrowse_win; char pathname[MAXPATHLEN], olddir[MAXPATHLEN]; int c, old; //struct stat b; extern int alphasort (); int num_files, i, q; struct dirent **files; int file_selected = 0, found; char tmpfilename[COLS - 15]; char *tmpdir; getcwd (pathname, MAXPATHLEN); filebrowse_win = newwin (LINES - 3, COLS - 10, 2, 5); werase (filebrowse_win); wbkgd (filebrowse_win, COLOR_PAIR (1)); wattr_on (filebrowse_win, A_BOLD, NULL); box (filebrowse_win, 0, 0); num_files = scandir (pathname, &files, file_select, alphasort); while (1) { for (i = 0; i < LINES - 5; i++) { wattr_on (filebrowse_win, A_BOLD, NULL); box (filebrowse_win, 0, 0); mvwprintw (filebrowse_win, 0, 3, " %s ", pathname); if (i + file_top == file_selected) wattr_on (filebrowse_win, COLOR_PAIR (3), NULL); else wattr_on (filebrowse_win, COLOR_PAIR (1), NULL); mvwhline (filebrowse_win, i + 1, 1, 32, COLS - 12); if (i + file_top < num_files) { found = 0; for (q = 0; q < num_added_files; q++) { sprintf (tmp, "%s/%s", pathname, files[i + file_top]->d_name); if (strcmp ((char *) &added_files[q], (char *) &tmp) == 0) { found = 1; break; } } if (found) wattr_on (filebrowse_win, A_BOLD, NULL); else wattr_off (filebrowse_win, A_BOLD, NULL); strcpy (tmpfilename, files[i + file_top]->d_name); tmpfilename[COLS - 16] = '\0'; if (files[i + file_top]->d_type == 4) { mvwprintw (filebrowse_win, i + 1, 1, "-> %s", tmpfilename); } else { mvwprintw (filebrowse_win, i + 1, 1, " %s", tmpfilename); //stat((char *)files[i+file_top]->d_name, (struct stat *)&b); //mvwprintw (filebrowse_win, i + 1, COLS - 16, "%dkb", (int)b.st_size/1024); } } wattr_on (filebrowse_win, COLOR_PAIR (2), NULL); mvwvline (filebrowse_win, 1, COLS - 12, 32, LINES - 5); wattr_on (filebrowse_win, COLOR_PAIR (3), NULL); mvwvline (filebrowse_win, (int) ((float) file_selected / num_files * (LINES - 5)) + 1, COLS - 12, 32, 1); wattr_on (filebrowse_win, COLOR_PAIR (1), NULL); } wrefresh (filebrowse_win); c = getch (); if (c == KEY_DOWN) { if (file_selected < num_files - 1) file_selected++; } else if (c == KEY_UP) { if (file_selected) file_selected--; } else if (c == KEY_NPAGE) { if (file_selected + LINES - 5 < num_files) { file_selected += LINES - 5; file_top += LINES - 5; } else { file_selected = num_files - 1; } } else if (c == KEY_PPAGE) { if (file_top - (LINES - 5) >= 0) { file_selected -= (LINES - 5); file_top -= (LINES - 5); } else { file_selected = file_top = 0; } } else if (c == 10 || c == KEY_LEFT || c == KEY_RIGHT) { if (c == KEY_LEFT) { files[file_selected]->d_type = 4; strcpy (files[file_selected]->d_name, ".."); } if (files[file_selected]->d_type == 4) { chdir (files[file_selected]->d_name); werase (filebrowse_win); //wbkgd (filebrowse_win, COLOR_PAIR (1) | A_BOLD); wattr_on (filebrowse_win, A_BOLD, NULL); box (filebrowse_win, 0, 0); if (strncmp ((char *) &files[file_selected]->d_name, "..", 2) == 0) { tmpdir = rindex (pathname, '/'); strcpy ((char *) olddir, (char *) ++tmpdir); old = 1; } else { old = 0; } getcwd (pathname, MAXPATHLEN); num_files = scandir (pathname, &files, file_select, alphasort); if (old) { i = 0; while (i < num_files) { if (strcmp ((char *) olddir, (char *) files[i]->d_name) == 0) break; i++; } if (i == num_files) file_selected = file_top = 0; else { file_selected = i; file_top = i - 5; } } else { file_selected = file_top = 0; } } } else if (c == 32) { if (files[file_selected]->d_type != 4) { found = 0; for (q = 0; q < num_added_files; q++) { sprintf (tmp, "%s/%s", pathname, files[file_selected]->d_name); if (strcmp ((char *) &added_files[q], (char *) &tmp) == 0) { found = q + 1; break; } } if (found) { added_files[found - 1][0] = '\0'; if (file_selected < num_files - 1) file_selected++; } else { sprintf (added_files[num_added_files], "%s/%s", pathname, files[file_selected]->d_name); num_added_files++; if (file_selected < num_files - 1) file_selected++; } } else { if (file_selected < num_files - 1) file_selected++; } } else if (c == 'q') { for (q = 0; q < num_added_files; q++) if (added_files[q][0] != '\0') { load_playlist (added_files[q]); playlist_changes = 1; } if (random_play) generate_random_playlist (); break; } if (file_selected >= file_top + LINES - 5) file_top += LINES - 5; else if (file_selected < file_top) file_top -= LINES - 5; if (file_top < 0) file_top = 0; } wclear (stdscr); delwin (filebrowse_win); } int file_select (struct dirent *entry) { char *ptr; struct stat b; if (strcmp (entry->d_name, ".") == 0) return (0); ptr = rindex (entry->d_name, '.'); if ((ptr != NULL && strcasecmp (ptr, ".mp3") == 0) || (ptr != NULL && strcasecmp (ptr, ".m3u") == 0) || (ptr != NULL && strcasecmp (ptr, ".ks3") == 0)) return (1); stat ((char *) entry->d_name, (struct stat *) &b); if (S_ISDIR (b.st_mode)) { entry->d_type = 4; return (1); } return (0); } void repaint () { endwin (); init_mp3_screen (); }