mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-03 09:01:52 -05:00
Add the support of WAV format in PCM file plugin
Added the support of WAV format in PCM file plugin. The infile is still only in raw format.
This commit is contained in:
parent
5c5f135812
commit
fe95bc237a
1 changed files with 104 additions and 4 deletions
|
|
@ -26,6 +26,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
#include <byteswap.h>
|
#include <byteswap.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "pcm_local.h"
|
#include "pcm_local.h"
|
||||||
|
|
@ -39,9 +40,20 @@ const char *_snd_module_pcm_file = "";
|
||||||
#ifndef DOC_HIDDEN
|
#ifndef DOC_HIDDEN
|
||||||
|
|
||||||
typedef enum _snd_pcm_file_format {
|
typedef enum _snd_pcm_file_format {
|
||||||
SND_PCM_FILE_FORMAT_RAW
|
SND_PCM_FILE_FORMAT_RAW,
|
||||||
|
SND_PCM_FILE_FORMAT_WAV
|
||||||
} snd_pcm_file_format_t;
|
} snd_pcm_file_format_t;
|
||||||
|
|
||||||
|
/* WAV format chunk */
|
||||||
|
struct wav_fmt {
|
||||||
|
short fmt;
|
||||||
|
short chan;
|
||||||
|
int rate;
|
||||||
|
int bps;
|
||||||
|
short bwidth;
|
||||||
|
short bits;
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
snd_pcm_generic_t gen;
|
snd_pcm_generic_t gen;
|
||||||
char *fname;
|
char *fname;
|
||||||
|
|
@ -60,14 +72,97 @@ typedef struct {
|
||||||
char *rbuf;
|
char *rbuf;
|
||||||
snd_pcm_channel_area_t *wbuf_areas;
|
snd_pcm_channel_area_t *wbuf_areas;
|
||||||
size_t buffer_bytes;
|
size_t buffer_bytes;
|
||||||
|
struct wav_fmt wav_header;
|
||||||
|
size_t filelen;
|
||||||
} snd_pcm_file_t;
|
} snd_pcm_file_t;
|
||||||
|
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
#define TO_LE32(x) (x)
|
||||||
|
#define TO_LE16(x) (x)
|
||||||
|
#else
|
||||||
|
#define TO_LE32(x) bswap_32(x)
|
||||||
|
#define TO_LE16(x) bswap_16(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setup_wav_header(snd_pcm_t *pcm, struct wav_fmt *fmt)
|
||||||
|
{
|
||||||
|
fmt->fmt = TO_LE16(0x01);
|
||||||
|
fmt->chan = TO_LE16(pcm->channels);
|
||||||
|
fmt->rate = TO_LE32(pcm->rate);
|
||||||
|
fmt->bwidth = pcm->frame_bits / 8;
|
||||||
|
fmt->bps = fmt->bwidth * pcm->rate;
|
||||||
|
fmt->bits = snd_pcm_format_width(pcm->format);
|
||||||
|
fmt->bps = TO_LE32(fmt->bps);
|
||||||
|
fmt->bwidth = TO_LE16(fmt->bwidth);
|
||||||
|
fmt->bits = TO_LE16(fmt->bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_wav_header(snd_pcm_t *pcm)
|
||||||
|
{
|
||||||
|
snd_pcm_file_t *file = pcm->private_data;
|
||||||
|
static char header[] = {
|
||||||
|
'R', 'I', 'F', 'F',
|
||||||
|
0x24, 0, 0, 0,
|
||||||
|
'W', 'A', 'V', 'E',
|
||||||
|
'f', 'm', 't', ' ',
|
||||||
|
0x10, 0, 0, 0,
|
||||||
|
};
|
||||||
|
static char header2[] = {
|
||||||
|
'd', 'a', 't', 'a',
|
||||||
|
0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
setup_wav_header(pcm, &file->wav_header);
|
||||||
|
|
||||||
|
if (write(file->fd, header, sizeof(header)) != sizeof(header) ||
|
||||||
|
write(file->fd, &file->wav_header, sizeof(file->wav_header)) !=
|
||||||
|
sizeof(file->wav_header) ||
|
||||||
|
write(file->fd, header2, sizeof(header2)) != sizeof(header2)) {
|
||||||
|
int err = errno;
|
||||||
|
SYSERR("Write error.\n");
|
||||||
|
return -err;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fix up the length fields in WAV header */
|
||||||
|
static void fixup_wav_header(snd_pcm_t *pcm)
|
||||||
|
{
|
||||||
|
snd_pcm_file_t *file = pcm->private_data;
|
||||||
|
int len, ret;
|
||||||
|
|
||||||
|
/* RIFF length */
|
||||||
|
if (lseek(file->fd, 4, SEEK_SET) == 4) {
|
||||||
|
len = (file->filelen + 0x24) > 0x7fffffff ?
|
||||||
|
0x7fffffff : (int)(file->filelen + 0x24);
|
||||||
|
len = TO_LE32(len);
|
||||||
|
ret = write(file->fd, &len, 4);
|
||||||
|
if (ret < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* data length */
|
||||||
|
if (lseek(file->fd, 0x28, SEEK_SET) == 0x28) {
|
||||||
|
len = file->filelen > 0x7fffffff ?
|
||||||
|
0x7fffffff : (int)file->filelen;
|
||||||
|
len = TO_LE32(len);
|
||||||
|
ret = write(file->fd, &len, 4);
|
||||||
|
if (ret < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* DOC_HIDDEN */
|
#endif /* DOC_HIDDEN */
|
||||||
|
|
||||||
static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
|
static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
|
||||||
{
|
{
|
||||||
snd_pcm_file_t *file = pcm->private_data;
|
snd_pcm_file_t *file = pcm->private_data;
|
||||||
assert(bytes <= file->wbuf_used_bytes);
|
assert(bytes <= file->wbuf_used_bytes);
|
||||||
|
|
||||||
|
if (file->format == SND_PCM_FILE_FORMAT_WAV &&
|
||||||
|
!file->wav_header.fmt) {
|
||||||
|
if (write_wav_header(pcm) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (bytes > 0) {
|
while (bytes > 0) {
|
||||||
snd_pcm_sframes_t err;
|
snd_pcm_sframes_t err;
|
||||||
size_t n = bytes;
|
size_t n = bytes;
|
||||||
|
|
@ -84,6 +179,7 @@ static void snd_pcm_file_write_bytes(snd_pcm_t *pcm, size_t bytes)
|
||||||
file->file_ptr_bytes += err;
|
file->file_ptr_bytes += err;
|
||||||
if (file->file_ptr_bytes == file->wbuf_size_bytes)
|
if (file->file_ptr_bytes == file->wbuf_size_bytes)
|
||||||
file->file_ptr_bytes = 0;
|
file->file_ptr_bytes = 0;
|
||||||
|
file->filelen += err;
|
||||||
if ((snd_pcm_uframes_t)err != n)
|
if ((snd_pcm_uframes_t)err != n)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +218,8 @@ static int snd_pcm_file_close(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
snd_pcm_file_t *file = pcm->private_data;
|
snd_pcm_file_t *file = pcm->private_data;
|
||||||
if (file->fname) {
|
if (file->fname) {
|
||||||
|
if (file->wav_header.fmt)
|
||||||
|
fixup_wav_header(pcm);
|
||||||
free((void *)file->fname);
|
free((void *)file->fname);
|
||||||
close(file->fd);
|
close(file->fd);
|
||||||
}
|
}
|
||||||
|
|
@ -395,7 +493,7 @@ static snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
|
||||||
* \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input
|
* \param ifd Input file descriptor (if (ifd < 0) && (ifname == NULL), no input
|
||||||
* redirection will be performed)
|
* redirection will be performed)
|
||||||
* \param trunc Truncate the file if it already exists
|
* \param trunc Truncate the file if it already exists
|
||||||
* \param fmt File format ("raw" is supported only)
|
* \param fmt File format ("raw" or "wav" are available)
|
||||||
* \param perm File permission
|
* \param perm File permission
|
||||||
* \param slave Slave PCM handle
|
* \param slave Slave PCM handle
|
||||||
* \param close_slave When set, the slave PCM handle is closed with copy PCM
|
* \param close_slave When set, the slave PCM handle is closed with copy PCM
|
||||||
|
|
@ -420,6 +518,8 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, const char *name,
|
||||||
if (fmt == NULL ||
|
if (fmt == NULL ||
|
||||||
strcmp(fmt, "raw") == 0)
|
strcmp(fmt, "raw") == 0)
|
||||||
format = SND_PCM_FILE_FORMAT_RAW;
|
format = SND_PCM_FILE_FORMAT_RAW;
|
||||||
|
else if (!strcmp(fmt, "wav"))
|
||||||
|
format = SND_PCM_FILE_FORMAT_WAV;
|
||||||
else {
|
else {
|
||||||
SNDERR("file format %s is unknown", fmt);
|
SNDERR("file format %s is unknown", fmt);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -528,10 +628,10 @@ pcm.name {
|
||||||
file STR # Output filename
|
file STR # Output filename
|
||||||
or
|
or
|
||||||
file INT # Output file descriptor number
|
file INT # Output file descriptor number
|
||||||
infile STR # Input filename
|
infile STR # Input filename - only raw format
|
||||||
or
|
or
|
||||||
infile INT # Input file descriptor number
|
infile INT # Input file descriptor number
|
||||||
[format STR] # File format (only "raw" at the moment)
|
[format STR] # File format ("raw" or "wav")
|
||||||
[perm INT] # Output file permission (octal, def. 0600)
|
[perm INT] # Output file permission (octal, def. 0600)
|
||||||
}
|
}
|
||||||
\endcode
|
\endcode
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue