Discussion:
[Alsa-user] Record wav file
k***@ece.upatras.gr
2011-07-18 19:17:54 UTC
Permalink
Hello all,

I'm looking for a way to modify the alsa recording example found here http://www.linuxjournal.com/article/6735?page=0,2 and shown below (or use any other sample program you may suggest), in order to produce a wav file rather than send the buffer contents to the standard output. I want to do something very specific, so using the arecord utility is not an option for me. Any help?

Thank you in advance

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

int main() {
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;

/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default",
SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0) {
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}

/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&params);

/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);

/* Set the desired hardware parameters. */

/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);

/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params,
SND_PCM_FORMAT_S16_LE);

/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(handle, params, 2);

/* 44100 bits/second sampling rate (CD quality) */
val = 44100;
snd_pcm_hw_params_set_rate_near(handle, params,
&val, &dir);

/* Set period size to 32 frames. */
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle,
params, &frames, &dir);

/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
exit(1);
}

/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params,
&frames, &dir);
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);

/* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(params,
&val, &dir);
loops = 5000000 / val;

while (loops > 0) {
loops--;
rc = snd_pcm_readi(handle, buffer, frames);
if (rc == -EPIPE) {
/* EPIPE means overrun */
fprintf(stderr, "overrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr,
"error from read: %s\n",
snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "short read, read %d frames\n", rc);
}
rc = write(1, buffer, size);
if (rc != size)
fprintf(stderr,
"short write: wrote %d bytes\n", rc);
}

snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);

return 0;
}
Konstantinos Birkos
2011-07-19 05:19:54 UTC
Permalink
Actually I want to perform sequential audio recordings with a fixed
duration, compress the tracks and send them via ftp to a remote
location. I want the minimum gap possible between successive tracks.
I mean that when the first track ends, recording the second track has to
be initiated almost immediately. Simply calling arecord on a periodical
basis introduces some gaps between tracks because I suppose the audio
device has to be opened and closed every time and configured with the
recording parameters. If I make the period I call arecord equal to the
recording period I receive errors from the audio device that indicate
that it is not yet ready from the previous recording.

So I thought of using a custom program like this, in which every x
seconds I could create a wav file. At the same time, the contents of the
buffer would be directly available for the new recording, minimizing losses.

Regards,
Konstantinos
Post by k***@ece.upatras.gr
Hello all,
I'm looking for a way to modify the alsa recording example found here
http://www.linuxjournal.com/article/6735?page=0,2 and shown below (or use any
other sample program you may suggest), in order to produce a wav file rather
than send the buffer contents to the standard output. I want to do something
very specific, so using the arecord utility is not an option for me. Any help?
Thank you in advance
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
int main() {
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;
/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default",
SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0) {
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&params);
/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params,
SND_PCM_FORMAT_S16_LE);
/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(handle, params, 2);
/* 44100 bits/second sampling rate (CD quality) */
val = 44100;
snd_pcm_hw_params_set_rate_near(handle, params,
&val, &dir);
/* Set period size to 32 frames. */
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle,
params, &frames, &dir);
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params,
&frames, &dir);
size = frames * 4; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
/* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(params,
&val, &dir);
loops = 5000000 / val;
while (loops > 0) {
loops--;
rc = snd_pcm_readi(handle, buffer, frames);
if (rc == -EPIPE) {
/* EPIPE means overrun */
fprintf(stderr, "overrun occurred\n");
snd_pcm_prepare(handle);
} else if (rc < 0) {
fprintf(stderr,
"error from read: %s\n",
snd_strerror(rc));
} else if (rc != (int)frames) {
fprintf(stderr, "short read, read %d frames\n", rc);
}
rc = write(1, buffer, size);
if (rc != size)
fprintf(stderr,
"short write: wrote %d bytes\n", rc);
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
return 0;
}
------------------------------------------------------------------------------
Study SoX: sox.sf.net . Or 'ecasound': http://www.eca.cx/ecasound/ .
But better first explain what is wrong with 'arecord'.
Regards,
Sergei.
--
Konstantinos Birkos
PhD Candidate
Wireless Telecommunication Laboratory
Department of Electrical and Computer Engineering
University of Patras
Patras, Greece
Tel.: +30 2610 996465
Sergei Steshenko
2011-07-19 05:55:49 UTC
Permalink
Post by Konstantinos Birkos
Actually I want to perform sequential audio recordings with a fixed
duration, compress the tracks and send them via ftp to a remote
location. I want the minimum gap possible between successive tracks.
I mean that when the first track ends, recording the second track has to
be initiated almost immediately. Simply calling arecord on a periodical
basis introduces some gaps between tracks because I suppose the audio
device has to be opened and closed every time and configured with the
recording parameters. If I make the period I call arecord equal to the
recording period I receive errors from the audio device that indicate
that it is not yet ready from the previous recording.
So I thought of using a custom program like this, in which every x
seconds I could create a wav file. At the same time, the contents of the
buffer would be directly available for the new recording, minimizing losses.
Regards,
Konstantinos
IIRC, 'arecord' can record in raw (i.e. headerless) format and can write to stdout. SoX and ecasound' can do this too.

So, just write a script (I'd recommend) Perl) that receives this headerless raw data from stdin and packs the data into W
John Haxby
2011-07-19 06:50:03 UTC
Permalink
Post by k***@ece.upatras.gr
I'm looking for a way to modify the alsa recording example found here http://www.linuxjournal.com/article/6735?page=0,2 and shown below (or use any other sample program you may suggest), in order to produce a wav file rather than send the buffer contents to the standard output. I want to do something very specific, so using the arecord utility is not an option for me. Any help?
converting a raw stream into a wav file is simply a question of putting a 44 byte wav header on at the begining of the file. This gives the sampling frequency (44.1kHz), number of channels (2) and bytes per sample (2, or possibly bits, I forget exactly); it also gives the duration for the recording which you won't know until you have finished.

What I did many years ago to do this was to seek to 44 bytes (on an empty output file) and record the raw data and then when I had finished, seek back to the beginning and write the header.

The header format is well-documented and well-known: Google will find it easily.

jch

Continue reading on narkive:
Loading...