mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 04:45:42 +01:00
aplay - Add stereo VU-meter support
Added the support of stereo VU-meter. Enabled via -Vs option. The new option, -V, can be used to enable the VU-meter. Now VU-meter can be enabled even without -vv.
This commit is contained in:
parent
4bdb0adef1
commit
c58817e8af
2 changed files with 153 additions and 40 deletions
|
@ -114,6 +114,11 @@ Show PCM structure and setup.
|
||||||
This option is accumulative. The VU meter is displayed when this
|
This option is accumulative. The VU meter is displayed when this
|
||||||
is given twice or three times.
|
is given twice or three times.
|
||||||
.TP
|
.TP
|
||||||
|
\fI\-V, \-\-vumeter=TYPE\fP
|
||||||
|
Specifies the VU-meter type, either \fIstereo\fP or \fImono\fP.
|
||||||
|
The stereo VU-meter is available only for 2-channel stereo samples
|
||||||
|
with interleaved format.
|
||||||
|
.TP
|
||||||
\fI\-I, \-\-separate\-channels\fP
|
\fI\-I, \-\-separate\-channels\fP
|
||||||
One file for each channel
|
One file for each channel
|
||||||
|
|
||||||
|
|
184
aplay/aplay.c
184
aplay/aplay.c
|
@ -71,6 +71,12 @@ static snd_pcm_sframes_t (*writei_func)(snd_pcm_t *handle, const void *buffer, s
|
||||||
static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
|
static snd_pcm_sframes_t (*readn_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
|
||||||
static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
|
static snd_pcm_sframes_t (*writen_func)(snd_pcm_t *handle, void **bufs, snd_pcm_uframes_t size);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VUMETER_NONE,
|
||||||
|
VUMETER_MONO,
|
||||||
|
VUMETER_STEREO
|
||||||
|
};
|
||||||
|
|
||||||
static char *command;
|
static char *command;
|
||||||
static snd_pcm_t *handle;
|
static snd_pcm_t *handle;
|
||||||
static struct {
|
static struct {
|
||||||
|
@ -96,6 +102,7 @@ static int avail_min = -1;
|
||||||
static int start_delay = 0;
|
static int start_delay = 0;
|
||||||
static int stop_delay = 0;
|
static int stop_delay = 0;
|
||||||
static int verbose = 0;
|
static int verbose = 0;
|
||||||
|
static int vumeter = VUMETER_NONE;
|
||||||
static int buffer_pos = 0;
|
static int buffer_pos = 0;
|
||||||
static size_t bits_per_sample, bits_per_frame;
|
static size_t bits_per_sample, bits_per_frame;
|
||||||
static size_t chunk_bytes;
|
static size_t chunk_bytes;
|
||||||
|
@ -174,6 +181,7 @@ _("Usage: %s [OPTION]... [FILE]...\n"
|
||||||
" (relative to buffer size if <= 0)\n"
|
" (relative to buffer size if <= 0)\n"
|
||||||
"-T, --stop-delay=# delay for automatic PCM stop is # microseconds from xrun\n"
|
"-T, --stop-delay=# delay for automatic PCM stop is # microseconds from xrun\n"
|
||||||
"-v, --verbose show PCM structure and setup (accumulative)\n"
|
"-v, --verbose show PCM structure and setup (accumulative)\n"
|
||||||
|
"-V, --vumeter=TYPE enable VU meter (TYPE: mono or stereo)\n"
|
||||||
"-I, --separate-channels one file for each channel\n"
|
"-I, --separate-channels one file for each channel\n"
|
||||||
" --disable-resample disable automatic rate resample\n"
|
" --disable-resample disable automatic rate resample\n"
|
||||||
" --disable-channels disable automatic channel conversions\n"
|
" --disable-channels disable automatic channel conversions\n"
|
||||||
|
@ -345,7 +353,7 @@ enum {
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int option_index;
|
int option_index;
|
||||||
char *short_options = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vIPC";
|
char *short_options = "hnlLD:qt:c:f:r:d:MNF:A:R:T:B:vV:IPC";
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
{"version", 0, 0, OPT_VERSION},
|
{"version", 0, 0, OPT_VERSION},
|
||||||
|
@ -369,6 +377,7 @@ int main(int argc, char *argv[])
|
||||||
{"buffer-time", 1, 0, 'B'},
|
{"buffer-time", 1, 0, 'B'},
|
||||||
{"buffer-size", 1, 0, OPT_BUFFER_SIZE},
|
{"buffer-size", 1, 0, OPT_BUFFER_SIZE},
|
||||||
{"verbose", 0, 0, 'v'},
|
{"verbose", 0, 0, 'v'},
|
||||||
|
{"vumeter", 1, 0, 'V'},
|
||||||
{"separate-channels", 0, 0, 'I'},
|
{"separate-channels", 0, 0, 'I'},
|
||||||
{"playback", 0, 0, 'P'},
|
{"playback", 0, 0, 'P'},
|
||||||
{"capture", 0, 0, 'C'},
|
{"capture", 0, 0, 'C'},
|
||||||
|
@ -514,6 +523,16 @@ int main(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
|
if (verbose > 1 && !vumeter)
|
||||||
|
vumeter = VUMETER_MONO;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
if (*optarg == 's')
|
||||||
|
vumeter = VUMETER_STEREO;
|
||||||
|
else if (*optarg == 'm')
|
||||||
|
vumeter = VUMETER_MONO;
|
||||||
|
else
|
||||||
|
vumeter = VUMETER_NONE;
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
mmap_flag = 1;
|
mmap_flag = 1;
|
||||||
|
@ -1048,6 +1067,12 @@ static void set_params(void)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
|
// fprintf(stderr, "real chunk_size = %i, frags = %i, total = %i\n", chunk_size, setup.buf.block.frags, setup.buf.block.frags * chunk_size);
|
||||||
|
|
||||||
|
/* stereo VU-meter isn't always available... */
|
||||||
|
if (vumeter == VUMETER_STEREO) {
|
||||||
|
if (hwparams.channels != 2 || !interleaved || verbose > 2)
|
||||||
|
vumeter = VUMETER_MONO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef timersub
|
#ifndef timersub
|
||||||
|
@ -1133,23 +1158,101 @@ static void suspend(void)
|
||||||
fprintf(stderr, _("Done.\n"));
|
fprintf(stderr, _("Done.\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_vu_meter_mono(int perc, int maxperc)
|
||||||
|
{
|
||||||
|
const int bar_length = 50;
|
||||||
|
char line[80];
|
||||||
|
int val;
|
||||||
|
|
||||||
|
for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
|
||||||
|
line[val] = '#';
|
||||||
|
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
|
||||||
|
line[val] = ' ';
|
||||||
|
line[val] = '+';
|
||||||
|
for (++val; val <= bar_length; val++)
|
||||||
|
line[val] = ' ';
|
||||||
|
if (maxperc > 99)
|
||||||
|
sprintf(line + val, "| MAX");
|
||||||
|
else
|
||||||
|
sprintf(line + val, "| %02i%%", maxperc);
|
||||||
|
fputs(line, stdout);
|
||||||
|
if (perc > 100)
|
||||||
|
printf(_(" !clip "));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_vu_meter_stereo(int *perc, int *maxperc)
|
||||||
|
{
|
||||||
|
const int bar_length = 35;
|
||||||
|
char line[80];
|
||||||
|
int c;
|
||||||
|
|
||||||
|
memset(line, ' ', sizeof(line) - 1);
|
||||||
|
line[bar_length + 3] = '|';
|
||||||
|
|
||||||
|
for (c = 0; c < 2; c++) {
|
||||||
|
int p = perc[c] * bar_length / 100;
|
||||||
|
char tmp[4];
|
||||||
|
if (p > bar_length)
|
||||||
|
p = bar_length;
|
||||||
|
if (c)
|
||||||
|
memset(line + bar_length + 6 + 1, '#', p);
|
||||||
|
else
|
||||||
|
memset(line + bar_length - p - 1, '#', p);
|
||||||
|
p = maxperc[c] * bar_length / 100;
|
||||||
|
if (p > bar_length)
|
||||||
|
p = bar_length;
|
||||||
|
if (c)
|
||||||
|
line[bar_length + 6 + 1 + p] = '+';
|
||||||
|
else
|
||||||
|
line[bar_length - p - 1] = '+';
|
||||||
|
if (maxperc[c] > 99)
|
||||||
|
sprintf(tmp, "MAX");
|
||||||
|
else
|
||||||
|
sprintf(tmp, "%02d%%", maxperc[c]);
|
||||||
|
if (c)
|
||||||
|
memcpy(line + bar_length + 3 + 1, tmp, 3);
|
||||||
|
else
|
||||||
|
memcpy(line + bar_length, tmp, 3);
|
||||||
|
}
|
||||||
|
line[bar_length * 2 + 6 + 2] = 0;
|
||||||
|
fputs(line, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_vu_meter(signed int *perc, signed int *maxperc)
|
||||||
|
{
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
print_vu_meter_stereo(perc, maxperc);
|
||||||
|
else
|
||||||
|
print_vu_meter_mono(*perc, *maxperc);
|
||||||
|
}
|
||||||
|
|
||||||
/* peak handler */
|
/* peak handler */
|
||||||
static void compute_max_peak(u_char *data, size_t count)
|
static void compute_max_peak(u_char *data, size_t count)
|
||||||
{
|
{
|
||||||
signed int val, max, max_peak = 0, perc;
|
signed int val, max, perc[2], max_peak[2];
|
||||||
static int run = 0;
|
static int run = 0;
|
||||||
size_t ocount = count;
|
size_t ocount = count;
|
||||||
int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
|
int format_little_endian = snd_pcm_format_little_endian(hwparams.format);
|
||||||
|
int ichans, c;
|
||||||
|
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
ichans = 2;
|
||||||
|
else
|
||||||
|
ichans = 1;
|
||||||
|
|
||||||
|
memset(max_peak, 0, sizeof(max_peak));
|
||||||
switch (bits_per_sample) {
|
switch (bits_per_sample) {
|
||||||
case 8: {
|
case 8: {
|
||||||
signed char *valp = (signed char *)data;
|
signed char *valp = (signed char *)data;
|
||||||
signed char mask = snd_pcm_format_silence(hwparams.format);
|
signed char mask = snd_pcm_format_silence(hwparams.format);
|
||||||
|
c = 0;
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
val = *valp++ ^ mask;
|
val = *valp++ ^ mask;
|
||||||
val = abs(val);
|
val = abs(val);
|
||||||
if (max_peak < val)
|
if (max_peak[c] < val)
|
||||||
max_peak = val;
|
max_peak[c] = val;
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
c = !c;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1159,14 +1262,18 @@ static void compute_max_peak(u_char *data, size_t count)
|
||||||
signed short sval;
|
signed short sval;
|
||||||
|
|
||||||
count /= 2;
|
count /= 2;
|
||||||
|
c = 0;
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
if (format_little_endian)
|
if (format_little_endian)
|
||||||
sval = __le16_to_cpu(*valp);
|
sval = __le16_to_cpu(*valp);
|
||||||
else sval = __be16_to_cpu(*valp);
|
else
|
||||||
|
sval = __be16_to_cpu(*valp);
|
||||||
sval = abs(sval) ^ mask;
|
sval = abs(sval) ^ mask;
|
||||||
if (max_peak < sval)
|
if (max_peak[c] < sval)
|
||||||
max_peak = sval;
|
max_peak[c] = sval;
|
||||||
valp++;
|
valp++;
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
c = !c;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1175,6 +1282,7 @@ static void compute_max_peak(u_char *data, size_t count)
|
||||||
signed int mask = snd_pcm_format_silence_32(hwparams.format);
|
signed int mask = snd_pcm_format_silence_32(hwparams.format);
|
||||||
|
|
||||||
count /= 3;
|
count /= 3;
|
||||||
|
c = 0;
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
if (format_little_endian) {
|
if (format_little_endian) {
|
||||||
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
|
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
|
||||||
|
@ -1186,24 +1294,31 @@ static void compute_max_peak(u_char *data, size_t count)
|
||||||
val |= 0xff<<24; /* Negate upper bits too */
|
val |= 0xff<<24; /* Negate upper bits too */
|
||||||
}
|
}
|
||||||
val = abs(val) ^ mask;
|
val = abs(val) ^ mask;
|
||||||
if (max_peak < val)
|
if (max_peak[c] < val)
|
||||||
max_peak = val;
|
max_peak[c] = val;
|
||||||
valp += 3;
|
valp += 3;
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
c = !c;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 32: {
|
case 32: {
|
||||||
signed int *valp = (signed int *)data;
|
signed int *valp = (signed int *)data;
|
||||||
signed int mask = snd_pcm_format_silence_32(hwparams.format);
|
signed int mask = snd_pcm_format_silence_32(hwparams.format);
|
||||||
|
|
||||||
count /= 4;
|
count /= 4;
|
||||||
|
c = 0;
|
||||||
while (count-- > 0) {
|
while (count-- > 0) {
|
||||||
if (format_little_endian)
|
if (format_little_endian)
|
||||||
val = __le32_to_cpu(*valp);
|
val = __le32_to_cpu(*valp);
|
||||||
else val = __be32_to_cpu(*valp);
|
else
|
||||||
|
val = __be32_to_cpu(*valp);
|
||||||
val = abs(val) ^ mask;
|
val = abs(val) ^ mask;
|
||||||
if (max_peak < val)
|
if (max_peak[c] < val)
|
||||||
max_peak = val;
|
max_peak[c] = val;
|
||||||
valp++;
|
valp++;
|
||||||
|
if (vumeter == VUMETER_STEREO)
|
||||||
|
c = !c;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1218,45 +1333,38 @@ static void compute_max_peak(u_char *data, size_t count)
|
||||||
if (max <= 0)
|
if (max <= 0)
|
||||||
max = 0x7fffffff;
|
max = 0x7fffffff;
|
||||||
|
|
||||||
|
for (c = 0; c < ichans; c++) {
|
||||||
if (bits_per_sample > 16)
|
if (bits_per_sample > 16)
|
||||||
perc = max_peak / (max / 100);
|
perc[c] = max_peak[c] / (max / 100);
|
||||||
else
|
else
|
||||||
perc = max_peak * 100 / max;
|
perc[c] = max_peak[c] * 100 / max;
|
||||||
|
}
|
||||||
|
|
||||||
if(verbose<=2) {
|
if (interleaved && verbose <= 2) {
|
||||||
static int maxperc=0;
|
static int maxperc[2];
|
||||||
static time_t t=0;
|
static time_t t=0;
|
||||||
const time_t tt=time(NULL);
|
const time_t tt=time(NULL);
|
||||||
if(tt>t) {
|
if(tt>t) {
|
||||||
t=tt;
|
t=tt;
|
||||||
maxperc=0;
|
maxperc[0] = 0;
|
||||||
|
maxperc[1] = 0;
|
||||||
}
|
}
|
||||||
if(perc>maxperc)
|
for (c = 0; c < ichans; c++)
|
||||||
maxperc=perc;
|
if (perc[c] > maxperc[c])
|
||||||
|
maxperc[c] = perc[c];
|
||||||
|
|
||||||
putchar('\r');
|
putchar('\r');
|
||||||
for (val = 0; val <= perc / 2 && val < 50; val++)
|
print_vu_meter(perc, maxperc);
|
||||||
putchar('#');
|
|
||||||
for (; val <= maxperc / 2 && val < 50; val++)
|
|
||||||
putchar(' ');
|
|
||||||
putchar('+');
|
|
||||||
for (++val; val <= 50; val++)
|
|
||||||
putchar(' ');
|
|
||||||
|
|
||||||
printf("| %02i%%", maxperc);
|
|
||||||
if (perc>99)
|
|
||||||
printf(_(" !clip "));
|
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
else if(verbose==3) {
|
else if(verbose==3) {
|
||||||
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak);
|
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
|
||||||
for (val = 0; val < 20; val++)
|
for (val = 0; val < 20; val++)
|
||||||
if (val <= perc / 5)
|
if (val <= perc[0] / 5)
|
||||||
putchar('#');
|
putchar('#');
|
||||||
else
|
else
|
||||||
putchar(' ');
|
putchar(' ');
|
||||||
printf(" %i%%\n", perc);
|
printf(" %i%%\n", perc[0]);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1287,7 +1395,7 @@ static ssize_t pcm_write(u_char *data, size_t count)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
if (verbose > 1)
|
if (vumeter)
|
||||||
compute_max_peak(data, r * hwparams.channels);
|
compute_max_peak(data, r * hwparams.channels);
|
||||||
result += r;
|
result += r;
|
||||||
count -= r;
|
count -= r;
|
||||||
|
@ -1328,7 +1436,7 @@ static ssize_t pcm_writev(u_char **data, unsigned int channels, size_t count)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
if (verbose > 1) {
|
if (vumeter) {
|
||||||
for (channel = 0; channel < channels; channel++)
|
for (channel = 0; channel < channels; channel++)
|
||||||
compute_max_peak(data[channel], r);
|
compute_max_peak(data[channel], r);
|
||||||
}
|
}
|
||||||
|
@ -1366,7 +1474,7 @@ static ssize_t pcm_read(u_char *data, size_t rcount)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
if (verbose > 1)
|
if (vumeter)
|
||||||
compute_max_peak(data, r * hwparams.channels);
|
compute_max_peak(data, r * hwparams.channels);
|
||||||
result += r;
|
result += r;
|
||||||
count -= r;
|
count -= r;
|
||||||
|
@ -1404,7 +1512,7 @@ static ssize_t pcm_readv(u_char **data, unsigned int channels, size_t rcount)
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
if (verbose > 1) {
|
if (vumeter) {
|
||||||
for (channel = 0; channel < channels; channel++)
|
for (channel = 0; channel < channels; channel++)
|
||||||
compute_max_peak(data[channel], r);
|
compute_max_peak(data[channel], r);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue