|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
term: terminal control
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright ?-2015 by the mpg123 project - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Michael Hipp
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "mpg123app.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#ifdef HAVE_TERMIOS
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <termios.h>
|
|
Packit |
c32a2d |
#include <ctype.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "term.h"
|
|
Packit |
c32a2d |
#include "common.h"
|
|
Packit |
c32a2d |
#include "playlist.h"
|
|
Packit |
c32a2d |
#include "metaprint.h"
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int term_enable = 0;
|
|
Packit |
c32a2d |
static struct termios old_tio;
|
|
Packit |
c32a2d |
int seeking = FALSE;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
extern out123_handle *ao;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Buffered key from a signal or whatnot.
|
|
Packit |
c32a2d |
We ignore the null character... */
|
|
Packit |
c32a2d |
static char prekey = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Hm, next step would be some system in this, plus configurability...
|
|
Packit |
c32a2d |
Two keys for everything? It's just stop/pause for now... */
|
|
Packit |
c32a2d |
struct keydef { const char key; const char key2; const char* desc; };
|
|
Packit |
c32a2d |
struct keydef term_help[] =
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
{ MPG123_STOP_KEY, ' ', "interrupt/restart playback (i.e. '(un)pause')" }
|
|
Packit |
c32a2d |
,{ MPG123_NEXT_KEY, 0, "next track" }
|
|
Packit |
c32a2d |
,{ MPG123_PREV_KEY, 0, "previous track" }
|
|
Packit |
c32a2d |
,{ MPG123_NEXT_DIR_KEY, 0, "next directory (next track until directory part changes)" }
|
|
Packit |
c32a2d |
,{ MPG123_PREV_DIR_KEY, 0, "previous directory (previous track until directory part changes)" }
|
|
Packit |
c32a2d |
,{ MPG123_BACK_KEY, 0, "back to beginning of track" }
|
|
Packit |
c32a2d |
,{ MPG123_PAUSE_KEY, 0, "loop around current position (don't combine with output buffer)" }
|
|
Packit |
c32a2d |
,{ MPG123_FORWARD_KEY, 0, "forward" }
|
|
Packit |
c32a2d |
,{ MPG123_REWIND_KEY, 0, "rewind" }
|
|
Packit |
c32a2d |
,{ MPG123_FAST_FORWARD_KEY, 0, "fast forward" }
|
|
Packit |
c32a2d |
,{ MPG123_FAST_REWIND_KEY, 0, "fast rewind" }
|
|
Packit |
c32a2d |
,{ MPG123_FINE_FORWARD_KEY, 0, "fine forward" }
|
|
Packit |
c32a2d |
,{ MPG123_FINE_REWIND_KEY, 0, "fine rewind" }
|
|
Packit |
c32a2d |
,{ MPG123_VOL_UP_KEY, 0, "volume up" }
|
|
Packit |
c32a2d |
,{ MPG123_VOL_DOWN_KEY, 0, "volume down" }
|
|
Packit |
c32a2d |
,{ MPG123_RVA_KEY, 0, "RVA switch" }
|
|
Packit |
c32a2d |
,{ MPG123_VERBOSE_KEY, 0, "verbose switch" }
|
|
Packit |
c32a2d |
,{ MPG123_PLAYLIST_KEY, 0, "list current playlist, indicating current track there" }
|
|
Packit |
c32a2d |
,{ MPG123_TAG_KEY, 0, "display tag info (again)" }
|
|
Packit |
c32a2d |
,{ MPG123_MPEG_KEY, 0, "print MPEG header info (again)" }
|
|
Packit |
c32a2d |
,{ MPG123_HELP_KEY, 0, "this help" }
|
|
Packit |
c32a2d |
,{ MPG123_QUIT_KEY, 0, "quit" }
|
|
Packit |
c32a2d |
,{ MPG123_PITCH_UP_KEY, MPG123_PITCH_BUP_KEY, "pitch up (small step, big step)" }
|
|
Packit |
c32a2d |
,{ MPG123_PITCH_DOWN_KEY, MPG123_PITCH_BDOWN_KEY, "pitch down (small step, big step)" }
|
|
Packit |
c32a2d |
,{ MPG123_PITCH_ZERO_KEY, 0, "reset pitch to zero" }
|
|
Packit |
c32a2d |
,{ MPG123_BOOKMARK_KEY, 0, "print out current position in playlist and track, for the benefit of some external tool to store bookmarks" }
|
|
Packit |
c32a2d |
};
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void term_sigcont(int sig);
|
|
Packit |
c32a2d |
static void term_sigusr(int sig);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* This must call only functions safe inside a signal handler. */
|
|
Packit |
c32a2d |
int term_setup(struct termios *pattern)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
struct termios tio = *pattern;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* One might want to use sigaction instead. */
|
|
Packit |
c32a2d |
signal(SIGCONT, term_sigcont);
|
|
Packit |
c32a2d |
signal(SIGUSR1, term_sigusr);
|
|
Packit |
c32a2d |
signal(SIGUSR2, term_sigusr);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
tio.c_lflag &= ~(ICANON|ECHO);
|
|
Packit |
c32a2d |
tio.c_cc[VMIN] = 1;
|
|
Packit |
c32a2d |
tio.c_cc[VTIME] = 0;
|
|
Packit |
c32a2d |
return tcsetattr(0,TCSANOW,&tio;;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void term_sigcont(int sig)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
term_enable = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if (term_setup(&old_tio) < 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr,"Can't set terminal attributes\n");
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
term_enable = 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void term_sigusr(int sig)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
switch(sig)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case SIGUSR1: prekey=*param.term_usr1; break;
|
|
Packit |
c32a2d |
case SIGUSR2: prekey=*param.term_usr2; break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* initialze terminal */
|
|
Packit |
c32a2d |
void term_init(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char hide_cursor[] = "\x1b[?25l";
|
|
Packit |
c32a2d |
debug("term_init");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(term_have_fun(STDERR_FILENO))
|
|
Packit |
c32a2d |
write(STDERR_FILENO, hide_cursor, sizeof(hide_cursor));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug1("param.term_ctrl: %i", param.term_ctrl);
|
|
Packit |
c32a2d |
if(!param.term_ctrl)
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
term_enable = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(tcgetattr(0,&old_tio) < 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr,"Can't get terminal attributes\n");
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(term_setup(&old_tio) < 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr,"Can't set terminal attributes\n");
|
|
Packit |
c32a2d |
return;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
term_enable = 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void term_hint(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(term_enable)
|
|
Packit |
c32a2d |
fprintf(stderr, "\nTerminal control enabled, press 'h' for listing of keys and functions.\n\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void term_handle_input(mpg123_handle *, out123_handle *, int);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int pause_cycle;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int print_index(mpg123_handle *mh)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int err;
|
|
Packit |
c32a2d |
size_t c, fill;
|
|
Packit |
c32a2d |
off_t *index;
|
|
Packit |
c32a2d |
off_t step;
|
|
Packit |
c32a2d |
err = mpg123_index(mh, &index, &step, &fill;;
|
|
Packit |
c32a2d |
if(err == MPG123_ERR)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fprintf(stderr, "Error accessing frame index: %s\n", mpg123_strerror(mh));
|
|
Packit |
c32a2d |
return err;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
for(c=0; c < fill;++c)
|
|
Packit |
c32a2d |
fprintf(stderr, "[%lu] %lu: %li (+%li)\n",
|
|
Packit |
c32a2d |
(unsigned long) c,
|
|
Packit |
c32a2d |
(unsigned long) (c*step),
|
|
Packit |
c32a2d |
(long) index[c],
|
|
Packit |
c32a2d |
(long) (c ? index[c]-index[c-1] : 0));
|
|
Packit |
c32a2d |
return MPG123_OK;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static off_t offset = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Go back to the start for the cyclic pausing. */
|
|
Packit |
c32a2d |
void pause_recycle(mpg123_handle *fr)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Take care not to go backwards in time in steps of 1 frame
|
|
Packit |
c32a2d |
That is what the +1 is for. */
|
|
Packit |
c32a2d |
pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
|
|
Packit |
c32a2d |
offset-=pause_cycle;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Done with pausing, no offset anymore. Just continuous playback from now. */
|
|
Packit |
c32a2d |
void pause_uncycle(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
offset += pause_cycle;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
off_t term_control(mpg123_handle *fr, out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
offset = 0;
|
|
Packit |
c32a2d |
debug2("control for frame: %li, enable: %i", (long)mpg123_tellframe(fr), term_enable);
|
|
Packit |
c32a2d |
if(!term_enable) return 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(paused)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* pause_cycle counts the remaining frames _after_ this one, thus <0, not ==0 . */
|
|
Packit |
c32a2d |
if(--pause_cycle < 0)
|
|
Packit |
c32a2d |
pause_recycle(fr);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
do
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
off_t old_offset = offset;
|
|
Packit |
c32a2d |
term_handle_input(fr, ao, stopped|seeking);
|
|
Packit |
c32a2d |
if((offset < 0) && (-offset > framenum)) offset = - framenum;
|
|
Packit |
c32a2d |
if(param.verbose && offset != old_offset)
|
|
Packit |
c32a2d |
print_stat(fr,offset,ao,1);
|
|
Packit |
c32a2d |
} while (!intflag && stopped);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Make the seeking experience with buffer less annoying.
|
|
Packit |
c32a2d |
No sound during seek, but at least it is possible to go backwards. */
|
|
Packit |
c32a2d |
if(offset)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if((offset = mpg123_seek_frame(fr, offset, SEEK_CUR)) >= 0)
|
|
Packit |
c32a2d |
debug1("seeked to %li", (long)offset);
|
|
Packit |
c32a2d |
else error1("seek failed: %s!", mpg123_strerror(fr));
|
|
Packit |
c32a2d |
/* Buffer resync already happened on un-stop? */
|
|
Packit |
c32a2d |
/* if(param.usebuffer) audio_drop(ao);*/
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Stop playback while seeking if buffer is involved. */
|
|
Packit |
c32a2d |
static void seekmode(mpg123_handle *mh, out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(param.usebuffer && !stopped)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int channels = 0;
|
|
Packit |
c32a2d |
int encoding = 0;
|
|
Packit |
c32a2d |
int pcmframe;
|
|
Packit |
c32a2d |
off_t back_samples = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
stopped = TRUE;
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(mh, 0, ao, 0);
|
|
Packit |
c32a2d |
mpg123_getformat(mh, NULL, &channels, &encoding);
|
|
Packit |
c32a2d |
pcmframe = out123_encsize(encoding)*channels;
|
|
Packit |
c32a2d |
if(pcmframe > 0)
|
|
Packit |
c32a2d |
back_samples = out123_buffered(ao)/pcmframe;
|
|
Packit |
c32a2d |
if(param.verbose > 2)
|
|
Packit |
c32a2d |
fprintf(stderr, "\nseeking back %"OFF_P" samples from %"OFF_P"\n"
|
|
Packit |
c32a2d |
, (off_p)back_samples, (off_p)mpg123_tell(mh));
|
|
Packit |
c32a2d |
mpg123_seek(mh, -back_samples, SEEK_CUR);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
if(param.verbose > 2)
|
|
Packit |
c32a2d |
fprintf(stderr, "\ndropped, now at %"OFF_P"\n"
|
|
Packit |
c32a2d |
, (off_p)mpg123_tell(mh));
|
|
Packit |
c32a2d |
fprintf(stderr, "%s", MPG123_STOPPED_STRING);
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(mh, 0, ao, 1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get the next pressed key, if any.
|
|
Packit |
c32a2d |
Returns 1 when there is a key, 0 if not. */
|
|
Packit |
c32a2d |
static int get_key(int do_delay, char *val)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
fd_set r;
|
|
Packit |
c32a2d |
struct timeval t;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Shortcut: If some other means sent a key, use it. */
|
|
Packit |
c32a2d |
if(prekey)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug1("Got prekey: %c\n", prekey);
|
|
Packit |
c32a2d |
*val = prekey;
|
|
Packit |
c32a2d |
prekey = 0;
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
t.tv_sec=0;
|
|
Packit |
c32a2d |
t.tv_usec=(do_delay) ? 10*1000 : 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
FD_ZERO(&r);
|
|
Packit |
c32a2d |
FD_SET(0,&r);
|
|
Packit |
c32a2d |
if(select(1,&r,NULL,NULL,&t) > 0 && FD_ISSET(0,&r))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(read(0,val,1) <= 0)
|
|
Packit |
c32a2d |
return 0; /* Well, we couldn't read the key, so there is none. */
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug1("term_handle_key: %c", val);
|
|
Packit |
c32a2d |
switch(tolower(val))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case MPG123_BACK_KEY:
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
if(paused) pause_cycle=(int)(LOOP_CYCLES/mpg123_tpf(fr));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0)
|
|
Packit |
c32a2d |
error1("Seek to begin failed: %s", mpg123_strerror(fr));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
framenum=0;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_NEXT_KEY:
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
next_track();
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_NEXT_DIR_KEY:
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
next_dir();
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_QUIT_KEY:
|
|
Packit |
c32a2d |
debug("QUIT");
|
|
Packit |
c32a2d |
if(stopped)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
stopped = 0;
|
|
Packit |
c32a2d |
out123_pause(ao); /* no chance for annoying underrun warnings */
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
set_intflag();
|
|
Packit |
c32a2d |
offset = 0;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_PAUSE_KEY:
|
|
Packit |
c32a2d |
paused=1-paused;
|
|
Packit |
c32a2d |
out123_pause(ao); /* underrun awareness */
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
if(paused)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Not really sure if that is what is wanted
|
|
Packit |
c32a2d |
This jumps in audio output, but has direct reaction to pausing loop. */
|
|
Packit |
c32a2d |
out123_param_float(ao, OUT123_PRELOAD, 0.);
|
|
Packit |
c32a2d |
pause_recycle(fr);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
out123_param_float(ao, OUT123_PRELOAD, param.preload);
|
|
Packit |
c32a2d |
if(stopped)
|
|
Packit |
c32a2d |
stopped=0;
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr, 0, ao, 1);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_STOP_KEY:
|
|
Packit |
c32a2d |
case ' ':
|
|
Packit |
c32a2d |
/* TODO: Verify/ensure that there is no "chirp from the past" when
|
|
Packit |
c32a2d |
seeking while stopped. */
|
|
Packit |
c32a2d |
stopped=1-stopped;
|
|
Packit |
c32a2d |
if(paused) {
|
|
Packit |
c32a2d |
paused=0;
|
|
Packit |
c32a2d |
offset -= pause_cycle;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(stopped)
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(offset) /* If position changed, old is outdated. */
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
/* No out123_continue(), that's triggered by out123_play(). */
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr, 0, ao, 1);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FINE_REWIND_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset--;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FINE_FORWARD_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset++;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_REWIND_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset-=10;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FORWARD_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset+=10;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FAST_REWIND_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset-=50;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FAST_FORWARD_KEY:
|
|
Packit |
c32a2d |
seekmode(fr, ao);
|
|
Packit |
c32a2d |
offset+=50;
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_VOL_UP_KEY:
|
|
Packit |
c32a2d |
mpg123_volume_change(fr, 0.02);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_VOL_DOWN_KEY:
|
|
Packit |
c32a2d |
mpg123_volume_change(fr, -0.02);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_PITCH_UP_KEY:
|
|
Packit |
c32a2d |
case MPG123_PITCH_BUP_KEY:
|
|
Packit |
c32a2d |
case MPG123_PITCH_DOWN_KEY:
|
|
Packit |
c32a2d |
case MPG123_PITCH_BDOWN_KEY:
|
|
Packit |
c32a2d |
case MPG123_PITCH_ZERO_KEY:
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
double new_pitch = param.pitch;
|
|
Packit |
c32a2d |
switch(val) /* Not tolower here! */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case MPG123_PITCH_UP_KEY: new_pitch += MPG123_PITCH_VAL; break;
|
|
Packit |
c32a2d |
case MPG123_PITCH_BUP_KEY: new_pitch += MPG123_PITCH_BVAL; break;
|
|
Packit |
c32a2d |
case MPG123_PITCH_DOWN_KEY: new_pitch -= MPG123_PITCH_VAL; break;
|
|
Packit |
c32a2d |
case MPG123_PITCH_BDOWN_KEY: new_pitch -= MPG123_PITCH_BVAL; break;
|
|
Packit |
c32a2d |
case MPG123_PITCH_ZERO_KEY: new_pitch = 0.0; break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
set_pitch(fr, ao, new_pitch);
|
|
Packit |
c32a2d |
if(param.verbose > 1)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,0);
|
|
Packit |
c32a2d |
fprintf(stderr, "\nNew pitch: %f\n", param.pitch);
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,1);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_VERBOSE_KEY:
|
|
Packit |
c32a2d |
param.verbose++;
|
|
Packit |
c32a2d |
if(param.verbose > VERBOSE_MAX)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
param.verbose = 0;
|
|
Packit |
c32a2d |
clear_stat();
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
mpg123_param(fr, MPG123_VERBOSE, param.verbose, 0);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_RVA_KEY:
|
|
Packit |
c32a2d |
if(++param.rva > MPG123_RVA_MAX) param.rva = 0;
|
|
Packit |
c32a2d |
mpg123_param(fr, MPG123_RVA, param.rva, 0);
|
|
Packit |
c32a2d |
mpg123_volume_change(fr, 0.);
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,1);
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_PREV_KEY:
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
prev_track();
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_PREV_DIR_KEY:
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
prev_dir();
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_PLAYLIST_KEY:
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,0);
|
|
Packit |
c32a2d |
fprintf(stderr, "%s\nPlaylist (\">\" indicates current track):\n", param.verbose ? "\n" : "");
|
|
Packit |
c32a2d |
print_playlist(stderr, 1);
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_TAG_KEY:
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,0);
|
|
Packit |
c32a2d |
fprintf(stderr, "%s\n", param.verbose ? "\n" : "");
|
|
Packit |
c32a2d |
print_id3_tag(fr, param.long_id3, stderr);
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_MPEG_KEY:
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,0);
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
if(param.verbose > 1)
|
|
Packit |
c32a2d |
print_header(fr);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
print_header_compact(fr);
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_HELP_KEY:
|
|
Packit |
c32a2d |
{ /* This is more than the one-liner before, but it's less spaghetti. */
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
if(param.verbose)
|
|
Packit |
c32a2d |
print_stat(fr,0,ao,0);
|
|
Packit |
c32a2d |
fprintf(stderr,"\n\n -= terminal control keys =-\n");
|
|
Packit |
c32a2d |
for(i=0; i<(sizeof(term_help)/sizeof(struct keydef)); ++i)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(term_help[i].key2) fprintf(stderr, "[%c] or [%c]", term_help[i].key, term_help[i].key2);
|
|
Packit |
c32a2d |
else fprintf(stderr, "[%c]", term_help[i].key);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
fprintf(stderr, "\t%s\n", term_help[i].desc);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
fprintf(stderr, "\nAlso, the number row (starting at 1, ending at 0) gives you jump points into the current track at 10%% intervals.\n");
|
|
Packit |
c32a2d |
fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_FRAME_INDEX_KEY:
|
|
Packit |
c32a2d |
case MPG123_VARIOUS_INFO_KEY:
|
|
Packit |
c32a2d |
if(param.verbose) fprintf(stderr, "\n");
|
|
Packit |
c32a2d |
switch(val) /* because of tolower() ... */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
case MPG123_FRAME_INDEX_KEY:
|
|
Packit |
c32a2d |
print_index(fr);
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
long accurate;
|
|
Packit |
c32a2d |
if(mpg123_getstate(fr, MPG123_ACCURATE, &accurate, NULL) == MPG123_OK)
|
|
Packit |
c32a2d |
fprintf(stderr, "Accurate position: %s\n", (accurate == 0 ? "no" : "yes"));
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
error1("Unable to get state: %s", mpg123_strerror(fr));
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_VARIOUS_INFO_KEY:
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char* curdec = mpg123_current_decoder(fr);
|
|
Packit |
c32a2d |
if(curdec == NULL) fprintf(stderr, "Cannot get decoder info!\n");
|
|
Packit |
c32a2d |
else fprintf(stderr, "Active decoder: %s\n", curdec);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case '0':
|
|
Packit |
c32a2d |
case '1':
|
|
Packit |
c32a2d |
case '2':
|
|
Packit |
c32a2d |
case '3':
|
|
Packit |
c32a2d |
case '4':
|
|
Packit |
c32a2d |
case '5':
|
|
Packit |
c32a2d |
case '6':
|
|
Packit |
c32a2d |
case '7':
|
|
Packit |
c32a2d |
case '8':
|
|
Packit |
c32a2d |
case '9':
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
off_t len;
|
|
Packit |
c32a2d |
int num;
|
|
Packit |
c32a2d |
num = val == '0' ? 10 : val - '0';
|
|
Packit |
c32a2d |
--num; /* from 0 to 9 */
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Do not swith to seekmode() here, as we are jumping once to a
|
|
Packit |
c32a2d |
specific position. Dropping buffer contents is enough and there
|
|
Packit |
c32a2d |
is no race filling the buffer or waiting for more incremental
|
|
Packit |
c32a2d |
seek orders. */
|
|
Packit |
c32a2d |
len = mpg123_length(fr);
|
|
Packit |
c32a2d |
out123_pause(ao);
|
|
Packit |
c32a2d |
out123_drop(ao);
|
|
Packit |
c32a2d |
if(len > 0)
|
|
Packit |
c32a2d |
mpg123_seek(fr, (off_t)( (num/10.)*len ), SEEK_SET);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
case MPG123_BOOKMARK_KEY:
|
|
Packit |
c32a2d |
continue_msg("BOOKMARK");
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
default:
|
|
Packit |
c32a2d |
;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void term_handle_input(mpg123_handle *fr, out123_handle *ao, int do_delay)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char val;
|
|
Packit |
c32a2d |
/* Do we really want that while loop? This means possibly handling multiple inputs that come very rapidly in one go. */
|
|
Packit |
c32a2d |
while(get_key(do_delay, &val))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
term_handle_key(fr, ao, val);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
void term_exit(void)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char cursor_restore[] = "\x1b[?25h";
|
|
Packit |
c32a2d |
/* Bring cursor back. */
|
|
Packit |
c32a2d |
if(term_have_fun(STDERR_FILENO))
|
|
Packit |
c32a2d |
write(STDERR_FILENO, cursor_restore, sizeof(cursor_restore));
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!term_enable) return;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
tcsetattr(0,TCSAFLUSH,&old_tio);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#endif
|
|
Packit |
c32a2d |
|