Skip to content

Commit

Permalink
mpg123: first go at A-B looping
Browse files Browse the repository at this point in the history
git-svn-id: svn://scm.orgis.org/mpg123/trunk@5234 35dc7657-300d-0410-a2e5-dc2837fedb53
  • Loading branch information
thor committed Feb 9, 2023
1 parent 7c3c18d commit c7a743d
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 44 deletions.
5 changes: 4 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ TODO: fix up position calculations with syn123 resampling, drop 2to1, 4to1 decod
correct one is included (at least gcc picks the one in the same directory as the
including header first).
-- TODO: make headers build-independent (some lfs aliasing for non-sensitive)
- mpg123: added --libversion
- mpg123:
-- Added --libversion.
-- Added proper A-B looping with terminal control key 'o', renamed
--pauseloop to --presetloop.
- libmpg123, libout123, libsyn123: bumped API version for version query
functions
- libout123:
Expand Down
7 changes: 4 additions & 3 deletions man1/mpg123.1
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ Quiet. Suppress diagnostic messages.
Enable terminal control keys. This is enabled automatically if a terminal is detected.
By default use 's' or the space bar to stop/restart (pause, unpause) playback, 'f' to jump forward to the next song, 'b' to jump back to the
beginning of the song, ',' to rewind, '.' to fast forward, and 'q' to quit.
Type 'h' for a full list of available controls.
Type 'h' for a full list of available controls. The A-B loop feature with key 'o' changes the preset loop interval to the interval
between two presses of 'o', the third press (or 'p') ending the looped playback. The key 'p' will use the updated loop interval after that.
.TP
\fB\-\^\-no\-control
Disable terminal control even if terminal is detected.
Expand All @@ -422,8 +423,8 @@ In an xterm, rxvt, screen, iris-ansi (compatible, TERM environment variable is e
playing.
.TP
\fB\-\^\-pauseloop \fIseconds
Set the length of the loop interval in terminal control paused mode, away from the default of 0.5 seconds, as a floating
point number.
Set the length of the loop interval in terminal control fixed looping mode, away from the default of 0.5 seconds, as a floating
point number. This value can be overwritten at runtime using the A-B loop feature.
.TP
\fB\-\^\-name \fIname
Set the name of this instance, possibly used in various places. This sets the client name for JACK output.
Expand Down
8 changes: 4 additions & 4 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

#include "debug.h"

int stopped = 0;
int paused = 0;
enum player_state playstate = STATE_PLAYING;
const char playsym[STATE_COUNT] = { '>', '_', '=', '?' };
int muted = 0;

const char* rva_name[3] = { "off", "mix", "album" };
Expand Down Expand Up @@ -222,7 +222,7 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
Buffering makes the relationships between the numbers non-trivial. */
rframes = frames-frame;
// May be negative, a countdown. Buffer only confuses in paused (looping) mode, though.
elapsed = decoded + offset*spf - (paused ? 0 : buffered);
elapsed = decoded + offset*spf - (playstate==STATE_LOOPING ? 0 : buffered);
remain = elapsed > 0 ? length - elapsed : length;
if( MPG123_OK == mpg123_info(fr, &mi)
&& MPG123_OK == mpg123_getvolume(fr, &basevol, &realvol, NULL) )
Expand Down Expand Up @@ -285,7 +285,7 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
/* Start with position info. */
len = snprintf( line, linelen
, "%c %s+%s %c%02lu:%02lu%c%02lu+%02lu:%02lu%c%02lu"
, stopped ? '_' : (paused ? '=' : '>')
, playsym[playstate]
, framestr[0], framestr[1]
, sign[0]
, times[0][0], times[0][1], timesep[0], times[0][2]
Expand Down
11 changes: 8 additions & 3 deletions src/common.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
common: anything can happen here... frame reading, output, messages
copyright ?-2020 by the mpg123 project - free software under the terms of the LGPL 2.1
copyright ?-2022 by the mpg123 project - free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
initially written by Michael Hipp
*/
Expand All @@ -12,8 +12,13 @@
#include "mpg123app.h"
#include "out123.h"

extern int stopped;
extern int paused;
enum player_state
{
STATE_PLAYING=0
, STATE_STOPPED, STATE_LOOPING, STATE_AB
, STATE_COUNT
};
extern enum player_state playstate;
extern int muted;

void print_header(mpg123_handle *);
Expand Down
4 changes: 3 additions & 1 deletion src/mpg123.c
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ topt opts[] = {
{0, "resync-limit", GLO_ARG | GLO_LONG, 0, &param.resync_limit, 0},
{0, "pitch", GLO_ARG|GLO_DOUBLE, 0, &param.pitch, 0},
{0, "pauseloop", GLO_ARG|GLO_DOUBLE, 0, &param.pauseloop, 0},
{0, "presetloop", GLO_ARG|GLO_DOUBLE, 0, &param.pauseloop, 0},
#ifdef NETWORK
{0, "ignore-mime", GLO_LONG, set_appflag, &appflag, MPG123APP_IGNORE_MIME },
#endif
Expand Down Expand Up @@ -1343,6 +1344,7 @@ int main(int sys_argc, char ** sys_argv)
#endif
}
if(!APPFLAG(MPG123APP_CONTINUE)) frames_left = param.frame_number;
term_new_track();

debug1("Going to play %s", strcmp(fname, "-") ? fname : "standard input");
// If a previous track did not cause error, we forget the success to
Expand Down Expand Up @@ -1720,7 +1722,7 @@ static void long_usage(int err)
#ifndef GENERIC
fprintf(o," --title set terminal title to filename\n");
#endif
fprintf(o," --pauseloop <s> loop interval in (fractional) seconds\n");
fprintf(o," --presetloop <s> preset loop interval in (fractional) seconds\n");
fprintf(o," --name <n> set instance name (used in various places)\n");
fprintf(o," --long-tag spacy id3 display with every item on a separate line\n");
fprintf(o," --lyrics show lyrics (from ID3v2 USLT frame)\n");
Expand Down
102 changes: 71 additions & 31 deletions src/term.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

static int term_enable = 0;
static const char *extrabreak = "";
int seeking = FALSE;
static int seeking = FALSE;

extern out123_handle *ao;

Expand All @@ -36,7 +36,8 @@ struct keydef term_help[] =
,{ MPG123_NEXT_DIR_KEY, 0, "next directory" }
,{ MPG123_PREV_DIR_KEY, 0, "previous directory" }
,{ MPG123_BACK_KEY, 0, "back to beginning" }
,{ MPG123_PAUSE_KEY, 0, "loop (--pauseloop)" }
,{ MPG123_LOOP_KEY, 0, "A-B loop" }
,{ MPG123_PAUSE_KEY, 0, "preset loop" }
,{ MPG123_FORWARD_KEY, 0, "forward" }
,{ MPG123_REWIND_KEY, 0, "rewind" }
,{ MPG123_FAST_FORWARD_KEY, 0, "fast forward" }
Expand Down Expand Up @@ -104,7 +105,11 @@ void term_hint(void)

static void term_handle_input(mpg123_handle *, out123_handle *, int);

static int pause_cycle;
// A-B looping sets pause_cycle at runtime baseon the difference to
// pause_begin. Keeping the broken wording for 'pause' for now. To the
// outside world, it is 'looping'.
static double pause_begin = -1;
static off_t pause_cycle;

static int print_index(mpg123_handle *mh)
{
Expand All @@ -129,28 +134,27 @@ static int print_index(mpg123_handle *mh)

static off_t offset = 0;

void term_new_track(void)
{
pause_begin = -1;
}

/* Go back to the start for the cyclic pausing. */
void pause_recycle(mpg123_handle *fr)
{
/* Take care not to go backwards in time in steps of 1 frame
That is what the +1 is for. */
pause_cycle=(int)(param.pauseloop/mpg123_tpf(fr));
pause_cycle=(off_t)(param.pauseloop/mpg123_tpf(fr));
offset-=pause_cycle;
}

/* Done with pausing, no offset anymore. Just continuous playback from now. */
void pause_uncycle(void)
{
offset += pause_cycle;
}

off_t term_control(mpg123_handle *fr, out123_handle *ao)
{
offset = 0;
debug2("control for frame: %li, enable: %i", (long)mpg123_tellframe(fr), term_enable);
debug2("control for frame: %"OFF_P", enable: %i", (off_p)mpg123_tellframe(fr), term_enable);
if(!term_enable) return 0;

if(paused)
if(playstate==STATE_LOOPING)
{
/* pause_cycle counts the remaining frames _after_ this one, thus <0, not ==0 . */
if(--pause_cycle < 0)
Expand All @@ -164,14 +168,14 @@ off_t term_control(mpg123_handle *fr, out123_handle *ao)
if((offset < 0) && (-offset > framenum)) offset = - framenum;
if(param.verbose && offset != old_offset)
print_stat(fr,offset,ao,1,&param);
} while (!intflag && stopped);
} while (!intflag && playstate==STATE_STOPPED);

/* Make the seeking experience with buffer less annoying.
No sound during seek, but at least it is possible to go backwards. */
if(offset)
{
if((offset = mpg123_seek_frame(fr, offset, SEEK_CUR)) >= 0)
debug1("seeked to %li", (long)offset);
debug1("seeked to %"OFF_P, (off_p)offset);
else error1("seek failed: %s!", mpg123_strerror(fr));
/* Buffer resync already happened on un-stop? */
/* if(param.usebuffer) audio_drop(ao);*/
Expand All @@ -182,14 +186,14 @@ off_t term_control(mpg123_handle *fr, out123_handle *ao)
/* Stop playback while seeking if buffer is involved. */
static void seekmode(mpg123_handle *mh, out123_handle *ao)
{
if(param.usebuffer && !stopped)
if(param.usebuffer && playstate!=STATE_STOPPED)
{
int channels = 0;
int encoding = 0;
int pcmframe;
off_t back_samples = 0;

stopped = TRUE;
playstate = STATE_STOPPED;
out123_pause(ao);
if(param.verbose)
print_stat(mh, 0, ao, 0, &param);
Expand Down Expand Up @@ -233,7 +237,9 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
case MPG123_BACK_KEY:
out123_pause(ao);
out123_drop(ao);
if(paused) pause_cycle=(int)(param.pauseloop/mpg123_tpf(fr));
// Revisit: What does that really achieve?
if(playstate==STATE_LOOPING)
pause_cycle=(int)(param.pauseloop/mpg123_tpf(fr));

if(mpg123_seek_frame(fr, 0, SEEK_SET) < 0)
error1("Seek to begin failed: %s", mpg123_strerror(fr));
Expand All @@ -252,24 +258,61 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
break;
case MPG123_QUIT_KEY:
debug("QUIT");
if(stopped)
if(playstate==STATE_STOPPED)
{
if(param.verbose)
print_stat(fr,0,ao,0,&param);

stopped = 0;
playstate=STATE_PLAYING; // really necessary/sensible?
out123_pause(ao); /* no chance for annoying underrun warnings */
out123_drop(ao);
}
set_intflag();
offset = 0;
break;
case MPG123_LOOP_KEY:
// In paused (looping) state, the loop key ends the loop just like the other one.
// Otherwise, it starts playback and enters A-? mode. If in A-? mode, it
// sets the loop interval and then again falls through.
if(playstate != STATE_LOOPING)
{
playstate = STATE_AB;
// Careful with positioning, output might have
long outrate = 0;
int outframesize = 0;
long inrate = 0;
if(out123_getformat(ao, &outrate, NULL, NULL, &outframesize) || outrate==0)
break;
if(mpg123_getformat(fr, &inrate, NULL, NULL) || inrate==0)
break;

double position = (double)mpg123_tell(fr)/inrate + (double)out123_buffered(ao)/(outrate * outframesize);
if(pause_begin < 0)
{
pause_begin = position;
if(param.verbose)
print_stat(fr, 0, ao, 1, &param);
else
fprintf(stderr, "%s", MPG123_AB_STRING);
break;
} else if(position <= pause_begin)
{
// Pathological situation: You seeked around, whatever. No loop.
playstate=STATE_LOOPING; // Let fall-through fix up things.
}
{
param.pauseloop = (position > pause_begin) ? (position-pause_begin) : mpg123_tpf(fr);
// Fall throuth to start looping.
}
}
case MPG123_PAUSE_KEY:
paused=1-paused;
{
playstate = playstate == STATE_LOOPING ? STATE_PLAYING : STATE_LOOPING;
pause_begin = -1;
size_t buffered = out123_buffered(ao);
out123_pause(ao); /* underrun awareness */
out123_drop(ao);
if(paused)
if(playstate == STATE_LOOPING)
{
// Make output buffer react immediately, dropping decoded audio
// and (at least trying to) seeking back in input.
Expand All @@ -287,23 +330,20 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
}
else
out123_param_float(ao, OUT123_PRELOAD, param.preload);
if(stopped)
stopped=0;
if(param.verbose)
print_stat(fr, 0, ao, 1, &param);
else
fprintf(stderr, "%s", (paused) ? MPG123_PAUSED_STRING : MPG123_EMPTY_STRING);
fprintf(stderr, "%s", (playstate == STATE_LOOPING) ? 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;
if(playstate == STATE_LOOPING)
offset -= pause_cycle;
}
if(stopped)
playstate = playstate == STATE_STOPPED ? STATE_PLAYING : STATE_STOPPED;
if(playstate == STATE_STOPPED)
out123_pause(ao);
else
{
Expand All @@ -314,7 +354,7 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
if(param.verbose)
print_stat(fr, 0, ao, 1, &param);
else
fprintf(stderr, "%s", (stopped) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
fprintf(stderr, "%s", (playstate==STATE_STOPPED) ? MPG123_STOPPED_STRING : MPG123_EMPTY_STRING);
break;
case MPG123_FINE_REWIND_KEY:
seekmode(fr, ao);
Expand Down Expand Up @@ -547,7 +587,7 @@ static void term_handle_input(mpg123_handle *fr, out123_handle *ao, int do_delay
{
char val;
/* Do we really want that while loop? This means possibly handling multiple inputs that come very rapidly in one go. */
while(term_get_key(stopped, do_delay, &val))
while(term_get_key(playstate==STATE_STOPPED, do_delay, &val))
{
term_handle_key(fr, ao, val);
}
Expand Down
5 changes: 4 additions & 1 deletion src/term.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define MPG123_BACK_KEY 'b'
#define MPG123_NEXT_KEY 'f'
#define MPG123_PAUSE_KEY 'p'
#define MPG123_LOOP_KEY 'o'
#define MPG123_QUIT_KEY 'q'
/* space bar is alias for that */
#define MPG123_STOP_KEY 's'
Expand Down Expand Up @@ -76,8 +77,9 @@
#define MPG123_PITCH_VAL 0.001
#define MPG123_PITCH_BVAL 0.01

#define MPG123_PAUSED_STRING "Paused. \b\b\b\b\b\b\b\b"
#define MPG123_PAUSED_STRING "Looping.\b\b\b\b\b\b\b\b"
#define MPG123_STOPPED_STRING "Stopped.\b\b\b\b\b\b\b\b"
#define MPG123_AB_STRING "Loop A-?\b\b\b\b\b\b\b\b"
#define MPG123_EMPTY_STRING " \b\b\b\b\b\b\b\b"

/* Need it as string for the param struct, change according to the above. */
Expand All @@ -87,6 +89,7 @@

int term_init(void); // -1 on error, 0 success or no terminal desired
void term_exit(void);
void term_new_track(void); // prepare for a new track being played
off_t term_control(mpg123_handle *mh, out123_handle *ao);
void term_hint(void); /* Print a message hinting at terminal usage. */

Expand Down

0 comments on commit c7a743d

Please sign in to comment.