static void print_playlistlist(FILE* out,const char* prefix, const char* indent, const struct gme_playlistlist* pll, struct gme* gme) { if (!pll || pll->len==0) return; if (prefix) fputs(prefix,out); fprintf(out,"{\n"); uint16_t i; for (i=0; i<le16toh(pll->len); i++) { const struct gme_playlist* pl=gme_playlistlist_get(gme,pll,i); fputs(" ",out); print_playlist(out,indent,pl); } if (indent) fputs(indent,out); fputs("}\n",out); }
static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val) { debug1("term_handle_key: %c", val); switch(tolower(val)) { case MPG123_BACK_KEY: out123_pause(ao); out123_drop(ao); if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr)); if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0) error1("Seek to begin failed: %s", mpg123_strerror(fr)); framenum=0; break; case MPG123_NEXT_KEY: out123_pause(ao); out123_drop(ao); next_track(); break; case MPG123_NEXT_DIR_KEY: out123_pause(ao); out123_drop(ao); next_dir(); break; case MPG123_QUIT_KEY: debug("QUIT"); if(stopped) { stopped = 0; out123_pause(ao); /* no chance for annoying underrun warnings */ out123_drop(ao); } set_intflag(); offset = 0; break; case MPG123_PAUSE_KEY: paused=1-paused; out123_pause(ao); /* underrun awareness */ out123_drop(ao); if(paused) { /* Not really sure if that is what is wanted This jumps in audio output, but has direct reaction to pausing loop. */ out123_param_float(ao, OUT123_PRELOAD, 0.); pause_recycle(fr); } else out123_param_float(ao, OUT123_PRELOAD, param.preload); if(stopped) stopped=0; if(param.verbose) print_stat(fr, 0, ao); else fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING); break; case MPG123_STOP_KEY: case ' ': /* TODO: Verify/ensure that there is no "chirp from the past" when seeking while stopped. */ stopped=1-stopped; if(paused) { paused=0; offset -= pause_cycle; } if(stopped) out123_pause(ao); else { if(offset) /* If position changed, old is outdated. */ out123_drop(ao); /* No out123_continue(), that's triggered by out123_play(). */ } if(param.verbose) print_stat(fr, 0, ao); else fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING); break; case MPG123_FINE_REWIND_KEY: seekmode(fr, ao); offset--; break; case MPG123_FINE_FORWARD_KEY: seekmode(fr, ao); offset++; break; case MPG123_REWIND_KEY: seekmode(fr, ao); offset-=10; break; case MPG123_FORWARD_KEY: seekmode(fr, ao); offset+=10; break; case MPG123_FAST_REWIND_KEY: seekmode(fr, ao); offset-=50; break; case MPG123_FAST_FORWARD_KEY: seekmode(fr, ao); offset+=50; break; case MPG123_VOL_UP_KEY: mpg123_volume_change(fr, 0.02); break; case MPG123_VOL_DOWN_KEY: mpg123_volume_change(fr, -0.02); break; case MPG123_PITCH_UP_KEY: case MPG123_PITCH_BUP_KEY: case MPG123_PITCH_DOWN_KEY: case MPG123_PITCH_BDOWN_KEY: case MPG123_PITCH_ZERO_KEY: { double new_pitch = param.pitch; switch(val) /* Not tolower here! */ { case MPG123_PITCH_UP_KEY: new_pitch += MPG123_PITCH_VAL; break; case MPG123_PITCH_BUP_KEY: new_pitch += MPG123_PITCH_BVAL; break; case MPG123_PITCH_DOWN_KEY: new_pitch -= MPG123_PITCH_VAL; break; case MPG123_PITCH_BDOWN_KEY: new_pitch -= MPG123_PITCH_BVAL; break; case MPG123_PITCH_ZERO_KEY: new_pitch = 0.0; break; } set_pitch(fr, ao, new_pitch); fprintf(stderr, "New pitch: %f\n", param.pitch); } break; case MPG123_VERBOSE_KEY: param.verbose++; if(param.verbose > VERBOSE_MAX) { param.verbose = 0; clear_stat(); } mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0); break; case MPG123_RVA_KEY: if(++param.rva > MPG123_RVA_MAX) param.rva = 0; if(param.verbose) fprintf(stderr, "\n"); mpg123_param(fr, MPG123_RVA, param.rva, 0); mpg123_volume_change(fr, 0.); break; case MPG123_PREV_KEY: out123_pause(ao); out123_drop(ao); prev_track(); break; case MPG123_PREV_DIR_KEY: out123_pause(ao); out123_drop(ao); prev_dir(); break; case MPG123_PLAYLIST_KEY: fprintf(stderr, "%s\nPlaylist (\">\" indicates current track):\n", param.verbose ? "\n" : ""); print_playlist(stderr, 1); fprintf(stderr, "\n"); break; case MPG123_TAG_KEY: fprintf(stderr, "%s\n", param.verbose ? "\n" : ""); print_id3_tag(fr, param.long_id3, stderr); fprintf(stderr, "\n"); break; case MPG123_MPEG_KEY: if(param.verbose) print_stat(fr,0,ao); /* Make sure that we are talking about the correct frame. */ fprintf(stderr, "\n"); if(param.verbose > 1) print_header(fr); else print_header_compact(fr); fprintf(stderr, "\n"); break; case MPG123_HELP_KEY: { /* This is more than the one-liner before, but it's less spaghetti. */ int i; fprintf(stderr,"\n\n -= terminal control keys =-\n"); for(i=0; i<(sizeof(term_help)/sizeof(struct keydef)); ++i) { if(term_help[i].key2) fprintf(stderr, "[%c] or [%c]", term_help[i].key, term_help[i].key2); else fprintf(stderr, "[%c]", term_help[i].key); fprintf(stderr, "\t%s\n", term_help[i].desc); } fprintf(stderr, "\nAlso, the number row (starting at 1, ending at 0) gives you jump points into the current track at 10%% intervals.\n"); fprintf(stderr, "\n"); } break; case MPG123_FRAME_INDEX_KEY: case MPG123_VARIOUS_INFO_KEY: if(param.verbose) fprintf(stderr, "\n"); switch(val) /* because of tolower() ... */ { case MPG123_FRAME_INDEX_KEY: print_index(fr); { long accurate; if(mpg123_getstate(fr, MPG123_ACCURATE, &accurate, NULL) == MPG123_OK) fprintf(stderr, "Accurate position: %s\n", (accurate == 0 ? "no" : "yes")); else error1("Unable to get state: %s", mpg123_strerror(fr)); } break; case MPG123_VARIOUS_INFO_KEY: { const char* curdec = mpg123_current_decoder(fr); if(curdec == NULL) fprintf(stderr, "Cannot get decoder info!\n"); else fprintf(stderr, "Active decoder: %s\n", curdec); } } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { off_t len; int num; num = val == '0' ? 10 : val - '0'; --num; /* from 0 to 9 */ /* Do not swith to seekmode() here, as we are jumping once to a specific position. Dropping buffer contents is enough and there is no race filling the buffer or waiting for more incremental seek orders. */ len = mpg123_length(fr); out123_pause(ao); out123_drop(ao); if(len > 0) mpg123_seek(fr, (off_t)( (num/10.)*len ), SEEK_SET); } break; case MPG123_BOOKMARK_KEY: continue_msg("BOOKMARK"); break; default: ; } }
static void term_handle_input(mpg123_handle *fr, audio_output_t *ao, int do_delay) { int n = 1; /* long offset = 0; */ while(n > 0) { fd_set r; struct timeval t; char val; t.tv_sec=0; t.tv_usec=(do_delay) ? 10*1000 : 0; FD_ZERO(&r); FD_SET(0,&r); n = select(1,&r,NULL,NULL,&t); if(n > 0 && FD_ISSET(0,&r)) { if(read(0,&val,1) <= 0) break; switch(tolower(val)) { case MPG123_BACK_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr)); if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0) error1("Seek to begin failed: %s", mpg123_strerror(fr)); framenum=0; break; case MPG123_NEXT_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); /* was: plain_buffer_resync */ next_track(); break; case MPG123_QUIT_KEY: debug("QUIT"); if(stopped) { stopped = 0; if(param.usebuffer) { buffer_resync(); buffer_start(); } } set_intflag(); offset = 0; break; case MPG123_PAUSE_KEY: paused=1-paused; if(paused) { /* Not really sure if that is what is wanted This jumps in audio output, but has direct reaction to pausing loop. */ if(param.usebuffer) buffer_resync(); pause_recycle(fr); } if(stopped) { stopped=0; if(param.usebuffer) buffer_start(); } fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING); break; case MPG123_STOP_KEY: case ' ': /* when seeking while stopped and then resuming, I want to prevent the chirp from the past */ if(!param.usebuffer) ao->flush(ao); stopped=1-stopped; if(paused) { paused=0; offset -= pause_cycle; } if(param.usebuffer) { if(stopped) buffer_stop(); else { /* When we stopped buffer for seeking, we must resync. */ if(offset) buffer_resync(); buffer_start(); } } fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING); break; case MPG123_FINE_REWIND_KEY: if(param.usebuffer) seekmode(); offset--; break; case MPG123_FINE_FORWARD_KEY: seekmode(); offset++; break; case MPG123_REWIND_KEY: seekmode(); offset-=10; break; case MPG123_FORWARD_KEY: seekmode(); offset+=10; break; case MPG123_FAST_REWIND_KEY: seekmode(); offset-=50; break; case MPG123_FAST_FORWARD_KEY: seekmode(); offset+=50; break; case MPG123_VOL_UP_KEY: mpg123_volume_change(fr, 0.02); break; case MPG123_VOL_DOWN_KEY: mpg123_volume_change(fr, -0.02); break; case MPG123_PITCH_UP_KEY: case MPG123_PITCH_BUP_KEY: case MPG123_PITCH_DOWN_KEY: case MPG123_PITCH_BDOWN_KEY: case MPG123_PITCH_ZERO_KEY: { double new_pitch = param.pitch; switch(val) /* Not tolower here! */ { case MPG123_PITCH_UP_KEY: new_pitch += MPG123_PITCH_VAL; break; case MPG123_PITCH_BUP_KEY: new_pitch += MPG123_PITCH_BVAL; break; case MPG123_PITCH_DOWN_KEY: new_pitch -= MPG123_PITCH_VAL; break; case MPG123_PITCH_BDOWN_KEY: new_pitch -= MPG123_PITCH_BVAL; break; case MPG123_PITCH_ZERO_KEY: new_pitch = 0.0; break; } set_pitch(fr, ao, new_pitch); fprintf(stderr, "New pitch: %f\n", param.pitch); } break; case MPG123_VERBOSE_KEY: param.verbose++; if(param.verbose > VERBOSE_MAX) { param.verbose = 0; clear_stat(); } mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0); break; case MPG123_RVA_KEY: if(++param.rva > MPG123_RVA_MAX) param.rva = 0; mpg123_param(fr, MPG123_RVA, param.rva, 0); mpg123_volume_change(fr, 0.); break; case MPG123_PREV_KEY: if(!param.usebuffer) ao->flush(ao); else buffer_resync(); /* was: plain_buffer_resync */ prev_track(); break; case MPG123_PLAYLIST_KEY: fprintf(stderr, "%s\nPlaylist (\">\" indicates current track):\n", param.verbose ? "\n" : ""); print_playlist(stderr, 1); fprintf(stderr, "\n"); break; case MPG123_TAG_KEY: fprintf(stderr, "%s\n", param.verbose ? "\n" : ""); print_id3_tag(fr, param.long_id3, stderr); fprintf(stderr, "\n"); break; case MPG123_MPEG_KEY: if(param.verbose) print_stat(fr,0,0); /* Make sure that we are talking about the correct frame. */ fprintf(stderr, "\n"); print_header(fr); fprintf(stderr, "\n"); break; case MPG123_HELP_KEY: { /* This is more than the one-liner before, but it's less spaghetti. */ int i; fprintf(stderr,"\n\n -= terminal control keys =-\n"); for(i=0; i<(sizeof(term_help)/sizeof(struct keydef)); ++i) { if(term_help[i].key2) fprintf(stderr, "[%c] or [%c]", term_help[i].key, term_help[i].key2); else fprintf(stderr, "[%c]", term_help[i].key); fprintf(stderr, "\t%s\n", term_help[i].desc); } fprintf(stderr, "\n"); } break; case MPG123_FRAME_INDEX_KEY: case MPG123_VARIOUS_INFO_KEY: if(param.verbose) fprintf(stderr, "\n"); switch(val) /* because of tolower() ... */ { case MPG123_FRAME_INDEX_KEY: print_index(fr); { long accurate; if(mpg123_getstate(fr, MPG123_ACCURATE, &accurate, NULL) == MPG123_OK) fprintf(stderr, "Accurate position: %s\n", (accurate == 0 ? "no" : "yes")); else error1("Unable to get state: %s", mpg123_strerror(fr)); } break; case MPG123_VARIOUS_INFO_KEY: { const char* curdec = mpg123_current_decoder(fr); if(curdec == NULL) fprintf(stderr, "Cannot get decoder info!\n"); else fprintf(stderr, "Active decoder: %s\n", curdec); } } break; default: ; } } } }
void command_loop(struct despotify_session* ds) { bool loop = true; char buf[80]; struct playlist* rootlist = NULL; struct playlist* searchlist = NULL; struct search_result *search = NULL; struct album_browse* playalbum = NULL; print_help(); do { wprintf(L"\n> "); fflush(stdout); bzero(buf, sizeof buf); fgets(buf, sizeof buf -1, stdin); buf[strlen(buf) - 1] = 0; /* remove newline */ /* list */ if (!strncmp(buf, "list", 4)) { int num = atoi(buf + 5); if (num) { struct playlist* p = get_playlist(rootlist, num); if (p) { print_tracks(p->tracks); lastlist = p; } } else { if (!rootlist) rootlist = despotify_get_stored_playlists(ds); print_list_of_lists(rootlist); } } /* rename */ else if (!strncmp(buf, "rename", 6)) { int num = 0; char *name = 0; if (strlen(buf) > 9) { num = atoi(buf + 7); name = strchr(buf + 7, ' ') + 1; } if (num && name && name[0]) { struct playlist* p = get_playlist(rootlist, num); if (p) { if (despotify_rename_playlist(ds, p, name)) wprintf(L"Renamed playlist %d to \"%s\".\n", num, name); else wprintf(L"Rename failed: %s\n", despotify_get_error(ds)); } } else wprintf(L"Usage: rename [num] [string]\n"); } /* collab */ else if (!strncmp(buf, "collab", 6)) { int num = 0; if (strlen(buf) > 7) num = atoi(buf + 7); if (num) { struct playlist* p = get_playlist(rootlist, num); if (p) { if (despotify_set_playlist_collaboration(ds, p, !p->is_collaborative)) wprintf(L"Changed playlist %d collaboration to %s.\n", num, p->is_collaborative ? "ON" : "OFF"); else wprintf(L"Setting playlist collaboration state failed: %s\n", despotify_get_error(ds)); } } else wprintf(L"Usage: collab [num]\n"); } /* search */ else if (!strncmp(buf, "search", 6)) { if (buf[7]) { if (search) despotify_free_search(search); despotify_stop(ds); /* since we replace the list */ search = despotify_search(ds, buf + 7, MAX_SEARCH_RESULTS); if (!search) { wprintf(L"Search failed: %s\n", despotify_get_error(ds)); continue; } searchlist = search->playlist; } else if (searchlist && (searchlist->num_tracks < search->total_tracks)) if (!despotify_search_more(ds, search, searchlist->num_tracks, MAX_SEARCH_RESULTS)) { wprintf(L"Search failed: %s\n", despotify_get_error(ds)); continue; } if (searchlist) { print_search(search); lastlist = searchlist; } else wprintf(L"No previous search\n"); } /* artist */ else if (!strncmp(buf, "artist", 6)) { int num = atoi(buf + 7); if (!num) { wprintf(L"usage: artist [num]\n"); continue; } if (!lastlist) { wprintf(L"No playlist\n"); continue; } /* find the requested track */ struct track* t = lastlist->tracks; for (int i=1; i<num; i++) t = t->next; for (struct artist* aptr = t->artist; aptr; aptr = aptr->next) { struct artist_browse* a = despotify_get_artist(ds, aptr->id); print_artist(a); despotify_free_artist_browse(a); } } /* album */ else if (!strncmp(buf, "album", 5)) { int num = atoi(buf + 6); if (!num) { wprintf(L"usage: album [num]\n"); continue; } if (!lastlist) { wprintf(L"No playlist\n"); continue; } /* find the requested track */ struct track* t = lastlist->tracks; for (int i=1; i<num; i++) t = t->next; if (t) { struct album_browse* a = despotify_get_album(ds, t->album_id); if (a) { print_album(a); despotify_free_album_browse(a); } else wprintf(L"Got no album for id %s\n", t->album_id); } } /* playalbum */ else if (!strncmp(buf, "playalbum", 9)) { int num = atoi(buf + 10); if (!num) { wprintf(L"usage: playalbum [num]\n"); continue; } if (!lastlist) { wprintf(L"No playlist\n"); continue; } /* find the requested track */ struct track* t = lastlist->tracks; for (int i=1; i<num; i++) t = t->next; if (t) { if (playalbum) despotify_free_album_browse(playalbum); despotify_stop(ds); playalbum = despotify_get_album(ds, t->album_id); if (playalbum) despotify_play(ds, playalbum->tracks, true); else wprintf(L"Got no album for id %s\n", t->album_id); } } /* uri */ else if (!strncmp(buf, "uri", 3)) { char *uri = buf + 4; if(strlen(uri) == 0) { wprintf(L"usage: info <uri>\n"); continue; } struct link* link = despotify_link_from_uri(uri); struct album_browse* al; struct artist_browse* ar; struct playlist* pls; struct search_result* s; struct track* t; switch(link->type) { case LINK_TYPE_ALBUM: al = despotify_link_get_album(ds, link); if(al) { print_album(al); despotify_free_album_browse(al); } break; case LINK_TYPE_ARTIST: ar = despotify_link_get_artist(ds, link); if(ar) { print_artist(ar); despotify_free_artist_browse(ar); } break; case LINK_TYPE_PLAYLIST: pls = despotify_link_get_playlist(ds, link); if(pls) { print_playlist(pls); despotify_free_playlist(pls); } break; case LINK_TYPE_SEARCH: s = despotify_link_get_search(ds, link); if(s) { print_search(s); despotify_free_search(s); } break; case LINK_TYPE_TRACK: t = despotify_link_get_track(ds, link); if(t) { print_track_full(t); despotify_free_track(t); } break; default: wprintf(L"%s is a invalid Spotify URI\n", uri); } despotify_free_link(link); } /* portrait */ else if (!strncmp(buf, "portrait", 8)) { int num = atoi(buf + 9); if (!num) { wprintf(L"usage: portrait [num]\n"); continue; } if (!lastlist) { wprintf(L"No playlist\n"); continue; } /* find the requested artist */ struct track* t = lastlist->tracks; for (int i=1; i<num; i++) t = t->next; struct artist_browse* a = despotify_get_artist(ds, t->artist->id); if (a && a->portrait_id[0]) { int len; void* portrait = despotify_get_image(ds, a->portrait_id, &len); if (portrait && len) { wprintf(L"Writing %d bytes into portrait.jpg\n", len); FILE* f = fopen("portrait.jpg", "w"); if (f) { fwrite(portrait, len, 1, f); fclose(f); } free(portrait); } } else wprintf(L"Artist %s has no portrait.\n", a->name); despotify_free_artist_browse(a); } /* play */ else if (!strncmp(buf, "play", 4)) { if (!lastlist) { wprintf(L"No list to play from. Use 'list' or 'search' to select a list.\n"); continue; } /* skip to track <num> */ listoffset = atoi(buf + 5); struct track* t = lastlist->tracks; for (int i=1; i<listoffset && t; i++) t = t->next; if (t) despotify_play(ds, t, true); else wprintf(L"Invalid track number %d\n", listoffset); } /* stop */ else if (!strncmp(buf, "stop", 4)) { despotify_stop(ds); } /* pause */ else if (!strncmp(buf, "pause", 5)) { despotify_pause(ds); } /* resume */ else if (!strncmp(buf, "resume", 5)) { despotify_resume(ds); } /* info */ else if (!strncmp(buf, "info", 4)) { print_info(ds); } /* help */ else if (!strncmp(buf, "help", 4)) { print_help(); } /* quit */ else if (!strncmp(buf, "quit", 4)) { loop = false; } } while(loop); if (rootlist) despotify_free_playlist(rootlist); if (search) despotify_free_search(search); if (playalbum) despotify_free_album_browse(playalbum); }