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 */
static void compute_max_peak(u_char *data, size_t count)
{
signed int val, max, perc[2], max_peak[2];
static int run = 0;
size_t ocount = count;
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) {
case 8: {
signed char *valp = (signed char *)data;
signed char mask = snd_pcm_format_silence(hwparams.format);
c = 0;
while (count-- > 0) {
val = *valp++ ^ mask;
val = abs(val);
if (max_peak[c] < val)
max_peak[c] = val;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 16: {
signed short *valp = (signed short *)data;
signed short mask = snd_pcm_format_silence_16(hwparams.format);
signed short sval;
count /= 2;
c = 0;
while (count-- > 0) {
if (format_little_endian)
sval = __le16_to_cpu(*valp);
else
sval = __be16_to_cpu(*valp);
sval = abs(sval) ^ mask;
if (max_peak[c] < sval)
max_peak[c] = sval;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 24: {
unsigned char *valp = data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 3;
c = 0;
while (count-- > 0) {
if (format_little_endian) {
val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
} else {
val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
}
/* Correct signed bit in 32-bit value */
if (val & (1<<(bits_per_sample-1))) {
val |= 0xff<<24; /* Negate upper bits too */
}
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp += 3;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
case 32: {
signed int *valp = (signed int *)data;
signed int mask = snd_pcm_format_silence_32(hwparams.format);
count /= 4;
c = 0;
while (count-- > 0) {
if (format_little_endian)
val = __le32_to_cpu(*valp);
else
val = __be32_to_cpu(*valp);
val = abs(val) ^ mask;
if (max_peak[c] < val)
max_peak[c] = val;
valp++;
if (vumeter == VUMETER_STEREO)
c = !c;
}
break;
}
default:
if (run == 0) {
fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
run = 1;
}
return;
}
max = 1 << (bits_per_sample-1);
if (max <= 0)
max = 0x7fffffff;
for (c = 0; c < ichans; c++) {
if (bits_per_sample > 16)
perc[c] = max_peak[c] / (max / 100);
else
perc[c] = max_peak[c] * 100 / max;
}
if (interleaved && verbose <= 2) {
static int maxperc[2];
static time_t t=0;
const time_t tt=time(NULL);
if(tt>t) {
t=tt;
maxperc[0] = 0;
maxperc[1] = 0;
}
for (c = 0; c < ichans; c++)
if (perc[c] > maxperc[c])
maxperc[c] = perc[c];
putchar('\r');
print_vu_meter(perc, maxperc);
fflush(stdout);
}
else if(verbose==3) {
printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
for (val = 0; val < 20; val++)
if (val <= perc[0] / 5)
putchar('#');
else
putchar(' ');
printf(" %i%%\n", perc[0]);
fflush(stdout);
}
}
static void do_test_position(void)
{
static long counter = 0;
static time_t tmr = -1;
time_t now;
static float availsum, delaysum, samples;
static snd_pcm_sframes_t maxavail, maxdelay;
static snd_pcm_sframes_t minavail, mindelay;
static snd_pcm_sframes_t badavail = 0, baddelay = 0;
snd_pcm_sframes_t outofrange;
snd_pcm_sframes_t avail, delay;
int err;
err = snd_pcm_avail_delay(handle, &avail, &delay);
if (err < 0)
return;
outofrange = (test_coef * (snd_pcm_sframes_t)buffer_frames) / 2;
if (avail > outofrange || avail < -outofrange ||
delay > outofrange || delay < -outofrange) {
badavail = avail; baddelay = delay;
availsum = delaysum = samples = 0;
maxavail = maxdelay = 0;
minavail = mindelay = buffer_frames * 16;
fprintf(stderr, _("Suspicious buffer position (%li total): "
"avail = %li, delay = %li, buffer = %li\n"),
++counter, (long)avail, (long)delay, (long)buffer_frames);
} else if (verbose) {
time(&now);
if (tmr == (time_t) -1) {
tmr = now;
availsum = delaysum = samples = 0;
maxavail = maxdelay = 0;
minavail = mindelay = buffer_frames * 16;
}
if (avail > maxavail)
maxavail = avail;
if (delay > maxdelay)
maxdelay = delay;
if (avail < minavail)
minavail = avail;
if (delay < mindelay)
mindelay = delay;
availsum += avail;
delaysum += delay;
samples++;
if (avail != 0 && now != tmr) {
fprintf(stderr, "BUFPOS: avg%li/%li "
"min%li/%li max%li/%li (%li) (%li:%li/%li)\n",
(long)(availsum / samples),
(long)(delaysum / samples),
(long)minavail, (long)mindelay,
(long)maxavail, (long)maxdelay,
(long)buffer_frames,
counter, badavail, baddelay);
tmr = now;
}
}
}
/*
* write function
*/
static ssize_t pcm_write(u_char *data, size_t count)
{
ssize_t r;
ssize_t result = 0;
if (count < chunk_size) {
snd_pcm_format_set_silence(hwparams.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwparams.channels);
count = chunk_size;
}
while (count > 0) {
if (test_position)
do_test_position();
r = writei_func(handle, data, count);
if (test_position)
do_test_position();
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
if (!test_nowait)
snd_pcm_wait(handle, 1000);
} else if (r == -EPIPE) {
xrun();
} else if (r == -ESTRPIPE) {
suspend();
} else if (r < 0) {
error(_("write error: %s"), snd_strerror(r));
prg_exit(EXIT_FAILURE);
}
if (r > 0) {
if (vumeter)
compute_max_peak(data, r * hwparams.channels);
result += r;
count -= r;
data += r * bits_per_frame / 8;
}
}
return result;
}
/*
* read function
*/
static ssize_t pcm_read(u_char *data, size_t rcount)
{
ssize_t r;
size_t result = 0;
size_t count = rcount;
if (count != chunk_size) {
count = chunk_size;
}
while (count > 0) {
if (test_position)
do_test_position();
r = readi_func(handle, data, count);
if (test_position)
do_test_position();
if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
if (!test_nowait)
snd_pcm_wait(handle, 1000);
} else if (r == -EPIPE) {
xrun();
} else if (r == -ESTRPIPE) {
suspend();
} else if (r < 0) {
error(_("read error: %s"), snd_strerror(r));
prg_exit(EXIT_FAILURE);
}
if (r > 0) {
if (vumeter)
compute_max_peak(data, r * hwparams.channels);
result += r;
count -= r;
data += r * bits_per_frame / 8;
}
}
return rcount;
}
/* calculate the data count to read from/to dsp */
static off64_t calc_count(void)
{
off64_t count;
if (timelimit == 0) {
count = pbrec_count;
} else {
count = snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
count *= (off64_t)timelimit;
}
return count < pbrec_count ? count : pbrec_count;
}
/* write a WAVE-header */
static void begin_wave(int fd, size_t cnt)
{
WaveHeader h;
WaveFmtBody f;
WaveChunkHeader cf, cd;
int bits;
u_int tmp;
u_short tmp2;
/* WAVE cannot handle greater than 32bit (signed?) int */
if (cnt == (size_t)-2)
cnt = 0x7fffff00;
bits = 8;
switch ((unsigned long) hwparams.format) {
case SND_PCM_FORMAT_U8:
bits = 8;
break;
case SND_PCM_FORMAT_S16_LE:
bits = 16;
break;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_FLOAT_LE:
bits = 32;
break;
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_3LE:
bits = 24;
break;
default:
error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
prg_exit(EXIT_FAILURE);
}
h.magic = WAV_RIFF;
tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8;
h.length = LE_INT(tmp);
h.type = WAV_WAVE;
cf.type = WAV_FMT;
cf.length = LE_INT(16);
if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
else
f.format = LE_SHORT(WAV_FMT_PCM);
f.channels = LE_SHORT(hwparams.channels);
f.sample_fq = LE_INT(hwparams.rate);
#if 0
tmp2 = (samplesize == 8) ? 1 : 2;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = dsp_speed * hwparams.channels * (u_int) tmp2;
#else
tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8;
f.byte_p_spl = LE_SHORT(tmp2);
tmp = (u_int) tmp2 * hwparams.rate;
#endif
f.byte_p_sec = LE_INT(tmp);
f.bit_p_spl = LE_SHORT(bits);
cd.type = WAV_DATA;
cd.length = LE_INT(cnt);
if (write(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) ||
write(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) ||
write(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) ||
write(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) {
error(_("write error"));
prg_exit(EXIT_FAILURE);
}
}
static void end_wave(int fd)
{ /* only close output */
WaveChunkHeader cd;
off64_t length_seek;
off64_t filelen;
u_int rifflen;
length_seek = sizeof(WaveHeader) +
sizeof(WaveChunkHeader) +
sizeof(WaveFmtBody);
cd.type = WAV_DATA;
cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount);
filelen = fdcount + 2*sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4;
rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen);
if (lseek64(fd, 4, SEEK_SET) == 4)
write(fd, &rifflen, 4);
if (lseek64(fd, length_seek, SEEK_SET) == length_seek)
write(fd, &cd, sizeof(WaveChunkHeader));
if (fd != 1)
close(fd);
}
static void header(int rtype, char *name)
{
if (!quiet_mode) {
if (! name)
name = (stream == SND_PCM_STREAM_PLAYBACK) ? "stdout" : "stdin";
fprintf(stderr, "%s %s '%s' : ",
(stream == SND_PCM_STREAM_PLAYBACK) ? _("Playing") : _("Recording"),
gettext(fmt_rec_table[rtype].what),
name);
fprintf(stderr, "%s, ", snd_pcm_format_description(hwparams.format));
fprintf(stderr, _("Rate %d Hz, "), hwparams.rate);
if (hwparams.channels == 1)
fprintf(stderr, _("Mono"));
else if (hwparams.channels == 2)
fprintf(stderr, _("Stereo"));
else
fprintf(stderr, _("Channels %i"), hwparams.channels);
fprintf(stderr, "\n");
}
}
/* playing raw data */
static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *name)
{
int l, r;
off64_t written = 0;
off64_t c;
header(rtype, name);
set_params();
while (loaded > chunk_bytes && written < count) {
if (pcm_write(audiobuf + written, chunk_size) <= 0)
return;
written += chunk_bytes;
loaded -= chunk_bytes;
}
if (written > 0 && loaded > 0)
memmove(audiobuf, audiobuf + written, loaded);
l = loaded;
while (written < count) {
do {
c = count - written;
if (c > chunk_bytes)
c = chunk_bytes;
c -= l;
if (c == 0)
break;
r = safe_read(fd, audiobuf + l, c);
if (r < 0) {
perror(name);
prg_exit(EXIT_FAILURE);
}
fdcount += r;
if (r == 0)
break;
l += r;
} while ((size_t)l < chunk_bytes);
l = l * 8 / bits_per_frame;
r = pcm_write(audiobuf, l);
if (r != l)
break;
r = r * bits_per_frame / 8;
written += r;
l = 0;
}
snd_pcm_nonblock(handle, 0);
snd_pcm_drain(handle);
snd_pcm_nonblock(handle, nonblock);
}
/*
* let's play or capture it (capture_type says VOC/WAVE/raw)
*/
static void playback(char *name)
{
int ofs;
size_t dta;
ssize_t dtawave;
pbrec_count = LLONG_MAX;
fdcount = 0;
if (!name || !strcmp(name, "-")) {
fd = fileno(stdin);
name = "stdin";
} else {
if ((fd = open64(name, O_RDONLY, 0)) == -1) {
perror(name);
prg_exit(EXIT_FAILURE);
}
}
/* read the file header */
dta = sizeof(AuHeader);
if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
error(_("read error"));
prg_exit(EXIT_FAILURE);
}
/* read bytes for WAVE-header */
if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
pbrec_count = calc_count();
playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name);
}
__end:
if (fd != 0)
close(fd);
}
/**
* mystrftime
*
* Variant of strftime(3) that supports additional format
* specifiers in the format string.
*
* Parameters:
*
* s - destination string
* max - max number of bytes to write
* userformat - format string
* tm - time information
* filenumber - the number of the file, starting at 1
*
* Returns: number of bytes written to the string s
*/
size_t mystrftime(char *s, size_t max, const char *userformat,
const struct tm *tm, const int filenumber)
{
char formatstring[PATH_MAX] = "";
char tempstring[PATH_MAX] = "";
char *format, *tempstr;
const char *pos_userformat;
format = formatstring;
/* if mystrftime is called with userformat = NULL we return a zero length string */
if (userformat == NULL) {
*s = '\0';
return 0;
}
for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
if (*pos_userformat == '%') {
tempstr = tempstring;
tempstr[0] = '\0';
switch (*++pos_userformat) {
case '\0': // end of string
--pos_userformat;
break;
case 'v': // file number
sprintf(tempstr, "%02d", filenumber);
break;
default: // All other codes will be handled by strftime
*format++ = '%';
*format++ = *pos_userformat;
continue;
}
/* If a format specifier was found and used, copy the result. */
if (tempstr[0]) {
while ((*format = *tempstr++) != '\0')
++format;
continue;
}
}
/* For any other character than % we simply copy the character */
*format++ = *pos_userformat;
}
*format = '\0';
format = formatstring;
return strftime(s, max, format, tm);
}
static int new_capture_file(char *name, char *namebuf, size_t namelen,
int filecount)
{
char *s;
char buf[PATH_MAX+1];
time_t t;
struct tm *tmp;
if (use_strftime) {
t = time(NULL);
tmp = localtime(&t);
if (tmp == NULL) {
perror("localtime");
prg_exit(EXIT_FAILURE);
}
if (mystrftime(namebuf, namelen, name, tmp, filecount+1) == 0) {
fprintf(stderr, "mystrftime returned 0");
prg_exit(EXIT_FAILURE);
}
return filecount;
}
/* get a copy of the original filename */
strncpy(buf, name, sizeof(buf));
/* separate extension from filename */
s = buf + strlen(buf);
while (s > buf && *s != '.' && *s != '/')
--s;
if (*s == '.')
*s++ = 0;
else if (*s == '/')
s = buf + strlen(buf);
/* upon first jump to this if block rename the first file */
if (filecount == 1) {
if (*s)
snprintf(namebuf, namelen, "%s-01.%s", buf, s);
else
snprintf(namebuf, namelen, "%s-01", buf);
remove(namebuf);
rename(name, namebuf);
filecount = 2;
}
/* name of the current file */
if (*s)
snprintf(namebuf, namelen, "%s-%02i.%s", buf, filecount, s);
else
snprintf(namebuf, namelen, "%s-%02i", buf, filecount);
return filecount;
}
/**
* create_path
*
* This function creates a file path, like mkdir -p.
*
* Parameters:
*
* path - the path to create
*
* Returns: 0 on success, -1 on failure
* On failure, a message has been printed to stderr.
*/
int create_path(const char *path)
{
char *start;
mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
if (path[0] == '/')
start = strchr(path + 1, '/');
else
start = strchr(path, '/');
while (start) {
char *buffer = strdup(path);
buffer[start-path] = 0x00;
if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
fprintf(stderr, "Problem creating directory %s", buffer);
perror(" ");
free(buffer);
return -1;
}
free(buffer);
start = strchr(start + 1, '/');
}
return 0;
}
static int safe_open(const char *name)
{
int fd;
fd = open64(name, O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
if (errno != ENOENT || !use_strftime)
return -1;
if (create_path(name) == 0)
fd = open64(name, O_WRONLY | O_CREAT, 0644);
}
return fd;
}
static void capture(char *orig_name)
{
int tostdout=0; /* boolean which describes output stream */
int filecount=0; /* number of files written */
char *name = orig_name; /* current filename */
char namebuf[PATH_MAX+1];
off64_t count, rest; /* number of bytes to capture */
/* get number of bytes to capture */
count = calc_count();
if (count == 0)
count = LLONG_MAX;
/* compute the number of bytes per file */
max_file_size = max_file_time *
snd_pcm_format_size(hwparams.format,
hwparams.rate * hwparams.channels);
/* WAVE-file should be even (I'm not sure), but wasting one byte
isn't a problem (this can only be in 8 bit mono) */
if (count < LLONG_MAX)
count += count % 2;
else
count -= count % 2;
/* display verbose output to console */
header(file_type, name);
/* setup sound hardware */
set_params();
/* write to stdout? */
if (!name || !strcmp(name, "-")) {
fd = fileno(stdout);
name = "stdout";
tostdout=1;
if (count > fmt_rec_table[file_type].max_filesize)
count = fmt_rec_table[file_type].max_filesize;
}
do {
/* open a file to write */
if(!tostdout) {
/* upon the second file we start the numbering scheme */
if (filecount || use_strftime) {
filecount = new_capture_file(orig_name, namebuf,
sizeof(namebuf),
filecount);
name = namebuf;
}
/* open a new file */
remove(name);
fd = safe_open(name);
if (fd < 0) {
perror(name);
prg_exit(EXIT_FAILURE);
}
filecount++;
}
rest = count;
if (rest > fmt_rec_table[file_type].max_filesize)
rest = fmt_rec_table[file_type].max_filesize;
if (max_file_size && (rest > max_file_size))
rest = max_file_size;
/* setup sample header */
if (fmt_rec_table[file_type].start)
fmt_rec_table[file_type].start(fd, rest);
/* capture */
fdcount = 0;
while (rest > 0 && recycle_capture_file == 0) {
size_t c = (rest <= (off64_t)chunk_bytes) ?
(size_t)rest : chunk_bytes;
size_t f = c * 8 / bits_per_frame;
if (pcm_read(audiobuf, f) != f)
break;
if (write(fd, audiobuf, c) != c) {
perror(name);
prg_exit(EXIT_FAILURE);
}
count -= c;
rest -= c;
fdcount += c;
}
/* re-enable SIGUSR1 signal */
if (recycle_capture_file) {
recycle_capture_file = 0;
signal(SIGUSR1, signal_handler_recycle);
}
/* finish sample container */
if (fmt_rec_table[file_type].end && !tostdout) {
fmt_rec_table[file_type].end(fd);
fd = -1;
}
/* repeat the loop when format is raw without timelimit or
* requested counts of data are recorded
*/
} while ( count > 0);
}
|