mirror of
http://mpg123.de/trunk/.git
synced 2025-07-30 02:01:12 +03:00
mpg123, libout123, fmt123: Make a very special friend happy, introduce output mute.
This introduces software muting in libout123, to be triggered via terminal control key 'u' (m was taken) or the remote control commands 'mute' and 'unmute'. For this, libout123 needs to know what a zero looks like in the current encoding. I hope I handled that smartly enough with the MPG123_ZEROSAMPLE macro in fmt123. I explicitly decided against linking in libsyn123. That makes only sense when going all-in and deciding that libout123 shall convert, resample, and mix on-the-fly to make input data match the output. This might be nice to have, but it is also nice to have a library that does not really care about the content it transports. It is a simple transporter with a buffer. Said buffer necessitates that the transporter knows what empty sound looks like, but I really don't want to burden it with more knowledge for simplicity. This muting needs to be inside libout123 whe the buffer is used. Feeding silence from the client application does not have latency you expect when (un)pause is already negotiated with the buffer. git-svn-id: svn://scm.orgis.org/mpg123/trunk@4589 35dc7657-300d-0410-a2e5-dc2837fedb53
This commit is contained in:
12
README
12
README
@ -73,13 +73,14 @@ Note that this Ctrl+C behaviour is special to this mode; when any of the followi
|
||||
3.2 Advanced Console Usage
|
||||
|
||||
You can specify the option -C to enable a terminal control interface enabling to influence playback on current title/playlist by pressing some key:
|
||||
|
||||
-= terminal control keys =-
|
||||
[s] or [ ] interrupt/restart playback (i.e. 'pause')
|
||||
[s] or [ ] interrupt/restart playback (i.e. '(un)pause')
|
||||
[f] next track
|
||||
[d] previous track
|
||||
[]] next directory (next track until directory part changes)
|
||||
[[] previous directory (previous track until directory part changes)
|
||||
[b] back to beginning of track
|
||||
[p] pause while looping current sound chunk
|
||||
[p] loop around current position (don't combine with output buffer)
|
||||
[.] forward
|
||||
[,] rewind
|
||||
[:] fast forward
|
||||
@ -88,11 +89,16 @@ You can specify the option -C to enable a terminal control interface enabling to
|
||||
[<] fine rewind
|
||||
[+] volume up
|
||||
[-] volume down
|
||||
[u] (un)mute volume
|
||||
[r] RVA switch
|
||||
[v] verbose switch
|
||||
[l] list current playlist, indicating current track there
|
||||
[t] display tag info (again)
|
||||
[m] print MPEG header info (again)
|
||||
[c] or [C] pitch up (small step, big step)
|
||||
[x] or [X] pitch down (small step, big step)
|
||||
[w] reset pitch to zero
|
||||
[k] print out current position in playlist and track, for the benefit of some external tool to store bookmarks
|
||||
[h] this help
|
||||
[q] quit
|
||||
|
||||
|
@ -22,6 +22,7 @@ COMMAND CODES
|
||||
|
||||
You can get this info via the control command "help".
|
||||
|
||||
|
||||
HELP/H: command listing (LONG/SHORT forms), command case insensitve
|
||||
|
||||
LOAD/L <trackname>: load and start playing resource <trackname>
|
||||
@ -38,6 +39,10 @@ JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> o
|
||||
|
||||
VOLUME/V <percent>: set volume in % (0..100...); float value
|
||||
|
||||
MUTE: turn on software mute in output
|
||||
|
||||
UNMUTE: turn off software mute in output
|
||||
|
||||
RVA off|(mix|radio)|(album|audiophile): set rva mode
|
||||
|
||||
EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel 1 (left) or 2 (right) or 3 (both)
|
||||
@ -52,25 +57,36 @@ SCAN: scan through the file, building seek index
|
||||
|
||||
SAMPLE: print out the sample position and total number of samples
|
||||
|
||||
FORMAT: print out sampling rate in Hz and channel count
|
||||
|
||||
SEQ <bass> <mid> <treble>: simple eq setting...
|
||||
|
||||
PITCH <[+|-]value>: adjust playback speed (+0.01 is 1 % faster)
|
||||
|
||||
SILENCE: be silent during playback (meaning silence in text form)
|
||||
|
||||
STATE: Print auxilliary state info in several lines (just try it to see what info is there).
|
||||
STATE: Print auxiliary state info in several lines (just try it to see what info is there).
|
||||
|
||||
TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names. NOTE: ID3v2 data will be deleted on non-forward seeks.
|
||||
|
||||
TAG/T: Print all available (ID3) tag info, for ID3v2 that gives output of all collected text fields, using the ID3v2.3/4 4-character names.
|
||||
The output is multiple lines, begin marked by "@T {", end by "@T }".
|
||||
|
||||
ID3v1 data is like in the @I info lines (see below), just with "@T" in front.
|
||||
|
||||
An ID3v2 data field is introduced via ([ ... ] means optional):
|
||||
|
||||
@T ID3v2.<NAME>[ [lang(<LANG>)] desc(<description>)]:
|
||||
|
||||
The lines of data follow with "=" prefixed:
|
||||
|
||||
@T =<one line of content in UTF-8 encoding>
|
||||
|
||||
meaning of the @S stream info:
|
||||
|
||||
S <mpeg-version> <layer> <sampling freq> <mode(stereo/mono/...)> <mode_ext> <framesize> <stereo> <copyright> <error_protected> <emphasis> <bitrate> <extension> <vbr(0/1=yes/no)>
|
||||
|
||||
The @I lines after loading a track give some ID3 info, the format:
|
||||
|
||||
@I ID3:artist album year comment genretext
|
||||
where artist,album and comment are exactly 30 characters each, year is 4 characters, genre text unspecified.
|
||||
You will encounter "@I ID3.genre:<number>" and "@I ID3.track:<number>".
|
||||
|
@ -561,8 +561,10 @@ complete junk on the input side. Fatal errors were only considered
|
||||
for output. With version 1.26.0, this changed to the behaviour
|
||||
described below.
|
||||
.P
|
||||
The process exit code is zero (success) if all tracks in a playlist
|
||||
had at least one frame parsed, even if it did not decode cleanly, or
|
||||
When not using the remote control interface (which returns input
|
||||
errors as text messages), the process exit code is zero (success)
|
||||
only if all tracks in a playlist had at least one frame parsed,
|
||||
even if it did not decode cleanly, or
|
||||
are empty, MPEG-wise (perhaps only metadata, or really an empty file).
|
||||
When you decode nothing, nothing is the result and that is fine. When
|
||||
a track later aborts because of parser errors or breakdown of the
|
||||
|
@ -239,3 +239,10 @@ int set_pitch(mpg123_handle *fr, out123_handle *ao, double new_pitch)
|
||||
}
|
||||
return out123_start(ao, pitch_rate(rate), channels, format);
|
||||
}
|
||||
|
||||
int set_mute(out123_handle *ao, int mutestate)
|
||||
{
|
||||
return out123_param( ao
|
||||
, mutestate ? OUT123_ADD_FLAGS : OUT123_REMOVE_FLAGS
|
||||
, OUT123_MUTE, 0, NULL );
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ void print_capabilities(out123_handle *ao, mpg123_handle *mh);
|
||||
Returns 1 if pitch setting succeeded, 0 otherwise.
|
||||
*/
|
||||
int set_pitch(mpg123_handle *fr, out123_handle *ao, double new_pitch);
|
||||
// Enable/disable software mute state.
|
||||
int set_mute(out123_handle *ao, int mutestate);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
int stopped = 0;
|
||||
int paused = 0;
|
||||
int muted = 0;
|
||||
static int term_is_fun = -1;
|
||||
|
||||
int term_have_fun(int fd, struct parameter *param)
|
||||
@ -338,9 +339,10 @@ void print_stat(mpg123_handle *fr, long offset, out123_handle *ao, int draw_bar
|
||||
if(len >= 0 && len < linelen)
|
||||
{ /* Volume info. */
|
||||
int len_add = snprintf( line+len, linelen-len
|
||||
, " %s %03u=%03u"
|
||||
, rva_statname[param->rva], roundui(basevol*100), roundui(realvol*100)
|
||||
);
|
||||
, " %s %03u%c%03u"
|
||||
, rva_statname[param->rva]
|
||||
, roundui(basevol*100), muted ? 'm' : '='
|
||||
, roundui(realvol*100) );
|
||||
if(len_add > 0)
|
||||
len += len_add;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
common: anything can happen here... frame reading, output, messages
|
||||
|
||||
copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
|
||||
copyright ?-2020 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
|
||||
*/
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
extern int stopped;
|
||||
extern int paused;
|
||||
extern int muted;
|
||||
|
||||
/* Return non-zero if full terminal fun is desired/possible. */
|
||||
int term_have_fun(int fd, struct parameter *param);
|
||||
|
@ -513,6 +513,18 @@ int control_generic (mpg123_handle *fr)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!strcasecmp(comstr, "MUTE")) {
|
||||
set_mute(ao, muted=TRUE);
|
||||
generic_sendmsg("mute");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!strcasecmp(comstr, "UNMUTE")) {
|
||||
set_mute(ao, muted=FALSE);
|
||||
generic_sendmsg("unmute");
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!strcasecmp(comstr, "T") || !strcasecmp(comstr, "TAG")) {
|
||||
generic_sendalltag(fr);
|
||||
continue;
|
||||
@ -594,6 +606,8 @@ int control_generic (mpg123_handle *fr)
|
||||
generic_sendmsg("H STOP/S: stop playback (closes file)");
|
||||
generic_sendmsg("H JUMP/J <frame>|<+offset>|<-offset>|<[+|-]seconds>s: jump to mpeg frame <frame> or change position by offset, same in seconds if number followed by \"s\"");
|
||||
generic_sendmsg("H VOLUME/V <percent>: set volume in % (0..100...); float value");
|
||||
generic_sendmsg("H MUTE: turn on software mute in output");
|
||||
generic_sendmsg("H UNMUTE: turn off software mute in output");
|
||||
generic_sendmsg("H RVA off|(mix|radio)|(album|audiophile): set rva mode");
|
||||
generic_sendmsg("H EQ/E <channel> <band> <value>: set equalizer value for frequency band 0 to 31 on channel %i (left) or %i (right) or %i (both)", MPG123_LEFT, MPG123_RIGHT, MPG123_LR);
|
||||
generic_sendmsg("H EQFILE <filename>: load EQ settings from a file");
|
||||
|
@ -115,6 +115,28 @@ enum mpg123_enc_enum
|
||||
: 0 \
|
||||
) ) ) ) ) )
|
||||
|
||||
/** Representation of zero in differing encodings.
|
||||
* This exists to define proper silence in various encodings without
|
||||
* having to link to libsyn123 to do actual conversions at runtime.
|
||||
* You have to handle big/little endian order yourself, though.
|
||||
* This takes the shortcut that any signed encoding has a zero with
|
||||
* all-zero bits. Unsigned linear encodings just have the highest bit set
|
||||
* (2^(n-1) for n bits), while the nonlinear 8-bit ones are special.
|
||||
* \param enc the encoding (mpg123_enc_enum value)
|
||||
* \param siz bytes per sample (return value of MPG123_SAMPLESIZE(enc))
|
||||
* \param off byte (octet) offset counted from LSB
|
||||
* \return unsigned byte value for the designated octet
|
||||
*/
|
||||
#define MPG123_ZEROSAMPLE(enc, siz, off) ( \
|
||||
(enc) == MPG123_ENC_ULAW_8 \
|
||||
? (off == 0 ? 0xff : 0x00) \
|
||||
: ( (enc) == MPG123_ENC_ALAW_8 \
|
||||
? (off == 0 ? 0xd5 : 0x00) \
|
||||
: ( (((enc) & (MPG123_ENC_SIGNED|MPG123_ENC_FLOAT)) || (siz) != ((off)+1)) \
|
||||
? 0x00 \
|
||||
: 0x80 \
|
||||
) ) )
|
||||
|
||||
/** Structure defining an audio format.
|
||||
* Providing the members as individual function arguments to define a certain
|
||||
* output format is easy enough. This struct makes is more comfortable to deal
|
||||
|
@ -95,6 +95,7 @@ out123_handle* attribute_align_arg out123_new(void)
|
||||
ao->channels = -1;
|
||||
ao->format = -1;
|
||||
ao->framesize = 0;
|
||||
memset(ao->zerosample, 0, 8);
|
||||
ao->state = play_dead;
|
||||
ao->auxflags = 0;
|
||||
ao->preload = 0.;
|
||||
@ -220,6 +221,12 @@ out123_param( out123_handle *ao, enum out123_parms code
|
||||
case OUT123_FLAGS:
|
||||
ao->flags = (int)value;
|
||||
break;
|
||||
case OUT123_ADD_FLAGS:
|
||||
ao->flags |= (int)value;
|
||||
break;
|
||||
case OUT123_REMOVE_FLAGS:
|
||||
ao->flags &= ~((int)value);
|
||||
break;
|
||||
case OUT123_PRELOAD:
|
||||
ao->preload = fvalue;
|
||||
break;
|
||||
@ -279,6 +286,7 @@ out123_getparam( out123_handle *ao, enum out123_parms code
|
||||
switch(code)
|
||||
{
|
||||
case OUT123_FLAGS:
|
||||
case OUT123_ADD_FLAGS:
|
||||
value = ao->flags;
|
||||
break;
|
||||
case OUT123_PRELOAD:
|
||||
@ -528,7 +536,16 @@ out123_start(out123_handle *ao, long rate, int channels, int encoding)
|
||||
ao->rate = rate;
|
||||
ao->channels = channels;
|
||||
ao->format = encoding;
|
||||
ao->framesize = out123_encsize(encoding)*channels;
|
||||
int samplesize = out123_encsize(encoding);
|
||||
ao->framesize = samplesize*channels;
|
||||
// The most convoluted way to say nothing at all.
|
||||
for(int i=0; i<samplesize; ++i)
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
ao->zerosample[samplesize-1-i] =
|
||||
#else
|
||||
ao->zerosample[i] =
|
||||
#endif
|
||||
MPG123_ZEROSAMPLE(ao->format, samplesize, i);
|
||||
|
||||
#ifndef NOXFERMEM
|
||||
if(have_buffer(ao))
|
||||
@ -617,6 +634,30 @@ void attribute_align_arg out123_stop(out123_handle *ao)
|
||||
ao->state = play_stopped;
|
||||
}
|
||||
|
||||
// Replace the data in a given block of audio data with zeroes
|
||||
// in the correct encoding.
|
||||
static void mute_block( unsigned char *bytes, int count
|
||||
, unsigned char* zerosample, int samplesize )
|
||||
{
|
||||
// The count is expected to be a multiple of samplesize,
|
||||
// this is just to ensure that the loop ends properly, should be noop.
|
||||
count -= count % samplesize;
|
||||
if(!count)
|
||||
return;
|
||||
// Initialize with one zero sample, then multiply that
|
||||
// to eventually cover the whole buffer.
|
||||
memcpy(bytes, zerosample, samplesize);
|
||||
int offset = samplesize;
|
||||
count -= samplesize;
|
||||
while(count)
|
||||
{
|
||||
int block = offset > count ? count : offset;
|
||||
memcpy(bytes+offset, bytes, block);
|
||||
offset += block;
|
||||
count -= block;
|
||||
}
|
||||
}
|
||||
|
||||
size_t attribute_align_arg
|
||||
out123_play(out123_handle *ao, void *bytes, size_t count)
|
||||
{
|
||||
@ -649,11 +690,15 @@ out123_play(out123_handle *ao, void *bytes, size_t count)
|
||||
return buffer_write(ao, bytes, count);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(ao->flags & OUT123_MUTE)
|
||||
mute_block( bytes, count, ao->zerosample
|
||||
, MPG123_SAMPLESIZE(ao->format) );
|
||||
do /* Playback in a loop to be able to continue after interruptions. */
|
||||
{
|
||||
errno = 0;
|
||||
int block = count > INT_MAX ? INT_MAX : count;
|
||||
written = ao->write(ao, (unsigned char*)bytes, block);
|
||||
written = ao->write(ao, bytes, block);
|
||||
debug4( "written: %d errno: %i (%s), keep_on=%d"
|
||||
, written, errno, strerror(errno)
|
||||
, ao->flags & OUT123_KEEP_PLAYING );
|
||||
@ -667,7 +712,7 @@ out123_play(out123_handle *ao, void *bytes, size_t count)
|
||||
break;
|
||||
}
|
||||
} while(count && ao->flags & OUT123_KEEP_PLAYING);
|
||||
|
||||
}
|
||||
debug3( "out123_play(%p, %p, ...) = %"SIZE_P
|
||||
, (void*)ao, bytes, (size_p)sum );
|
||||
return sum;
|
||||
|
@ -117,6 +117,8 @@ enum out123_parms
|
||||
* (e.g. ../lib/mpg123 or ./plugins). The environment variable MPG123_MODDIR
|
||||
* is always tried first and the in-built installation path last.
|
||||
*/
|
||||
, OUT123_ADD_FLAGS /**< enable given flags */
|
||||
, OUT123_REMOVE_FLAGS /**< disable diven flags */
|
||||
};
|
||||
|
||||
/** Flags to tune out123 behaviour */
|
||||
@ -136,6 +138,7 @@ enum out123_flags
|
||||
* over the data given to it via out123_play(), unless a communication error
|
||||
* arises.
|
||||
*/
|
||||
, OUT123_MUTE = 0x20 /**< software mute (play silent audio) */
|
||||
};
|
||||
|
||||
/** Read-only output driver/device property flags (OUT123_PROPFLAGS). */
|
||||
@ -510,7 +513,7 @@ void out123_stop(out123_handle *ao);
|
||||
* Also note that it is no accident that the buffer parameter is not marked
|
||||
* as constant. Some output drivers might need to do things like swap
|
||||
* byte order. This is done in-place instead of wasting memory on yet
|
||||
* another copy.
|
||||
* another copy. Software muting also overwrites the data.
|
||||
* \param ao handle
|
||||
* \param buffer pointer to raw audio data to be played
|
||||
* \param bytes number of bytes to read from the buffer
|
||||
|
@ -83,6 +83,7 @@ struct out123_struct
|
||||
int channels; /* number of channels */
|
||||
int format; /* encoding (TODO: rename this to "encoding"!) */
|
||||
int framesize; /* Output needs data in chunks of framesize bytes. */
|
||||
char zerosample[8]; /* Zero in current encoding, max 64 bit. */
|
||||
enum playstate state; /* ... */
|
||||
int auxflags; /* For now just one: quiet mode (for probing). */
|
||||
int propflags; /* Property flags, set by driver. */
|
||||
|
@ -49,17 +49,18 @@ struct keydef term_help[] =
|
||||
,{ MPG123_FINE_REWIND_KEY, 0, "fine rewind" }
|
||||
,{ MPG123_VOL_UP_KEY, 0, "volume up" }
|
||||
,{ MPG123_VOL_DOWN_KEY, 0, "volume down" }
|
||||
,{ MPG123_VOL_MUTE_KEY, 0, "(un)mute volume" }
|
||||
,{ MPG123_RVA_KEY, 0, "RVA switch" }
|
||||
,{ MPG123_VERBOSE_KEY, 0, "verbose switch" }
|
||||
,{ MPG123_PLAYLIST_KEY, 0, "list current playlist, indicating current track there" }
|
||||
,{ MPG123_TAG_KEY, 0, "display tag info (again)" }
|
||||
,{ MPG123_MPEG_KEY, 0, "print MPEG header info (again)" }
|
||||
,{ MPG123_HELP_KEY, 0, "this help" }
|
||||
,{ MPG123_QUIT_KEY, 0, "quit" }
|
||||
,{ MPG123_PITCH_UP_KEY, MPG123_PITCH_BUP_KEY, "pitch up (small step, big step)" }
|
||||
,{ MPG123_PITCH_DOWN_KEY, MPG123_PITCH_BDOWN_KEY, "pitch down (small step, big step)" }
|
||||
,{ MPG123_PITCH_ZERO_KEY, 0, "reset pitch to zero" }
|
||||
,{ MPG123_BOOKMARK_KEY, 0, "print out current position in playlist and track, for the benefit of some external tool to store bookmarks" }
|
||||
,{ MPG123_HELP_KEY, 0, "this help" }
|
||||
,{ MPG123_QUIT_KEY, 0, "quit" }
|
||||
};
|
||||
|
||||
void term_sigcont(int sig);
|
||||
@ -386,6 +387,9 @@ static void term_handle_key(mpg123_handle *fr, out123_handle *ao, char val)
|
||||
case MPG123_VOL_DOWN_KEY:
|
||||
mpg123_volume_change(fr, -0.02);
|
||||
break;
|
||||
case MPG123_VOL_MUTE_KEY:
|
||||
set_mute(ao, muted=!muted);
|
||||
break;
|
||||
case MPG123_PITCH_UP_KEY:
|
||||
case MPG123_PITCH_BUP_KEY:
|
||||
case MPG123_PITCH_DOWN_KEY:
|
||||
|
@ -46,6 +46,7 @@
|
||||
|
||||
#define MPG123_VOL_UP_KEY '+'
|
||||
#define MPG123_VOL_DOWN_KEY '-'
|
||||
#define MPG123_VOL_MUTE_KEY 'u'
|
||||
#define MPG123_VERBOSE_KEY 'v'
|
||||
#define MPG123_RVA_KEY 'r'
|
||||
#define MPG123_PLAYLIST_KEY 'l'
|
||||
|
Reference in New Issue
Block a user