这段时间在探索ALSA架构,从ALSA Core到ALSA Lib,再到Android Audio System。在看ALSA Lib时,写了一个比较典型的基于ALSA的播放录音程序。程序包包含四个部分:
WAV Parser是对WAV文件的分析和封装,这里只针对Standard WAV File;
SND Common是Playback 和Record共同操作,如SetParams、ReadPCM和WritePCM等;
Playback和Record就分别是播放录音的主体了。
原理很简单,以Playback为例:从WAV文件读取PCM数据,通过I2S或AC97依次送到Audio Codec。难点在于对snd_pcm_hw_params_t的设置,尤其要确定每次要送到Audio Codec的数据帧大小(peroid_size),这个稍后解释。
1、从WAV文件的头信息可以分析出:sample_format、channels number、sample_rate、sample_length,这些参数要通过snd_pcm_hw_params_set_XXX()接口设置到snd_pcm_hw_params_t中。
2、接着我们要设置buffer_time 和peroid_time。通过snd_pcm_hw_params_get_buffer_time_max()接口可以获取该Audio Codec可以支持的最大buffer_time,这里我们设置buffer_time = (MAX_BUFFER_TIME > 500000) ? 500000 : MAX_BUFFER_TIME; peroid_time = buffer_time/4。
关于peroid的概念有这样的描述:The “period” is a term that corresponds to a fragment in the OSS world. The period defines the size at which a PCM interrupt is generated. 从底层驱动看来,应该是PCM DMA单次传送数据帧的大小。其实真正关注底层驱动的话,它并不是关心peroid_time,它关心的是peroid_size,这两者有转换关系。具体见struct snd_pcm_hardware结构体。
3、通过snd_pcm_hw_params_get_period_size()取得peroid_size,注意在ALSA中peroid_size是以frame为单位的。The configured buffer and period sizes are stored in “frames” in the runtime. 1 frame = channels * sample_size. 所以要对peroid_size进行转换:chunk_bytes = peroid_size * sample_length / 8。chunk_bytes就是我们单次从WAV读PCM数据的大小。
之后的过程就乏善可陈了。唯一要留意的是snd_pcm_writei()和snd_pcm_readi()的第三个参数size也是以frame为单位,不要忘记frames和bytes的转换。
//File : wav_parser.h //Author : Loon <[email protected]> #ifndef __WAV_PARSER_H #define __WAV_PARSER_H typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #if __BYTE_ORDER == __LITTLE_ENDIAN #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) #define LE_SHORT(v) (v) #define LE_INT(v) (v) #define BE_SHORT(v) bswap_16(v) #define BE_INT(v) bswap_32(v) #elif __BYTE_ORDER == __BIG_ENDIAN #define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24)) #define LE_SHORT(v) bswap_16(v) #define LE_INT(v) bswap_32(v) #define BE_SHORT(v) (v) #define BE_INT(v) (v) #else #error "Wrong endian" #endif #define WAV_RIFF COMPOSE_ID('R','I','F','F') #define WAV_WAVE COMPOSE_ID('W','A','V','E') #define WAV_FMT COMPOSE_ID('f','m','t',' ') #define WAV_DATA COMPOSE_ID('d','a','t','a') /* WAVE fmt block constants from Microsoft mmreg.h header */ #define WAV_FMT_PCM 0x0001 #define WAV_FMT_IEEE_FLOAT 0x0003 #define WAV_FMT_DOLBY_AC3_SPDIF 0x0092 #define WAV_FMT_EXTENSIBLE 0xfffe /* Used with WAV_FMT_EXTENSIBLE format */ #define WAV_GUID_TAG "/x00/x00/x00/x00/x10/x00/x80/x00/x00/xAA/x00/x38/x9B/x71" /* it's in chunks like .voc and AMIGA iff, but my source say there are in only in this combination, so I combined them in one header; it works on all WAVE-file I have */ typedef struct WAVHeader { uint32_t magic; /* 'RIFF' */ uint32_t length; /* filelen */ uint32_t type; /* 'WAVE' */ } WAVHeader_t; typedef struct WAVFmt { uint32_t magic; /* 'FMT '*/ uint32_t fmt_size; /* 16 or 18 */ uint16_t format; /* see WAV_FMT_* */ uint16_t channels; uint32_t sample_rate; /* frequence of sample */ uint32_t bytes_p_second; uint16_t blocks_align; /* samplesize; 1 or 2 bytes */ uint16_t sample_length; /* 8, 12 or 16 bit */ } WAVFmt_t; typedef struct WAVFmtExtensible { WAVFmt_t format; uint16_t ext_size; uint16_t bit_p_spl; uint32_t channel_mask; uint16_t guid_format; /* WAV_FMT_* */ uint8_t guid_tag[14]; /* WAV_GUID_TAG */ } WAVFmtExtensible_t; typedef struct WAVChunkHeader { uint32_t type; /* 'data' */ uint32_t length; /* samplecount */ } WAVChunkHeader_t; typedef struct WAVContainer { WAVHeader_t header; WAVFmt_t format; WAVChunkHeader_t chunk; } WAVContainer_t; int WAV_ReadHeader(int fd, WAVContainer_t *container); int WAV_WriteHeader(int fd, WAVContainer_t *container); #endif /* #ifndef __WAV_PARSER_H */
//File : wav_parser.c //Author : Loon <[email protected]> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include "wav_parser.h" #define WAV_PRINT_MSG char *WAV_P_FmtString(uint16_t fmt) { switch (fmt) { case WAV_FMT_PCM: return "PCM"; break; case WAV_FMT_IEEE_FLOAT: return "IEEE FLOAT"; break; case WAV_FMT_DOLBY_AC3_SPDIF: return "DOLBY AC3 SPDIF"; break; case WAV_FMT_EXTENSIBLE: return "EXTENSIBLE"; break; default: break; } return "NON Support Fmt"; } void WAV_P_PrintHeader(WAVContainer_t *container) { printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/n"); printf("/n"); printf("File Magic: [%c%c%c%c]/n", (char)(container->header.magic), (char)(container->header.magic>>8), (char)(container->header.magic>>16), (char)(container->header.magic>>24)); printf("File Length: [%d]/n", container->header.length); printf("File Type: [%c%c%c%c]/n", (char)(container->header.type), (char)(container->header.type>>8), (char)(container->header.type>>16), (char)(container->header.type>>24)); printf("/n"); printf("Fmt Magic: [%c%c%c%c]/n", (char)(container->format.magic), (char)(container->format.magic>>8), (char)(container->format.magic>>16), (char)(container->format.magic>>24)); printf("Fmt Size: [%d]/n", container->format.fmt_size); printf("Fmt Format: [%s]/n", WAV_P_FmtString(container->format.format)); printf("Fmt Channels: [%d]/n", container->format.channels); printf("Fmt Sample_rate: [%d](HZ)/n", container->format.sample_rate); printf("Fmt Bytes_p_second: [%d]/n", container->format.bytes_p_second); printf("Fmt Blocks_align: [%d]/n", container->format.blocks_align); printf("Fmt Sample_length: [%d]/n", container->format.sample_length); printf("/n"); printf("Chunk Type: [%c%c%c%c]/n", (char)(container->chunk.type), (char)(container->chunk.type>>8), (char)(container->chunk.type>>16), (char)(container->chunk.type>>24)); printf("Chunk Length: [%d]/n", container->chunk.length); printf("/n"); printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/n"); } int WAV_P_CheckValid(WAVContainer_t *container) { if (container->header.magic != WAV_RIFF || container->header.type != WAV_WAVE || container->format.magic != WAV_FMT || container->format.fmt_size != LE_INT(16) || (container->format.channels != LE_SHORT(1) && container->format.channels != LE_SHORT(2)) || container->chunk.type != WAV_DATA) { fprintf(stderr, "non standard wav file./n"); return -1; } return 0; } int WAV_ReadHeader(int fd, WAVContainer_t *container) { assert((fd >=0) && container); if (read(fd, &container->header, sizeof(container->header)) != sizeof(container->header) || read(fd, &container->format, sizeof(container->format)) != sizeof(container->format) || read(fd, &container->chunk, sizeof(container->chunk)) != sizeof(container->chunk)) { fprintf(stderr, "Error WAV_ReadHeader/n"); return -1; } if (WAV_P_CheckValid(container) < 0) return -1; #ifdef WAV_PRINT_MSG WAV_P_PrintHeader(container); #endif return 0; } int WAV_WriteHeader(int fd, WAVContainer_t *container) { assert((fd >=0) && container); if (WAV_P_CheckValid(container) < 0) return -1; if (write(fd, &container->header, sizeof(container->header)) != sizeof(container->header) || write(fd, &container->format, sizeof(container->format)) != sizeof(container->format) || write(fd, &container->chunk, sizeof(container->chunk)) != sizeof(container->chunk)) { fprintf(stderr, "Error WAV_WriteHeader/n"); return -1; } #ifdef WAV_PRINT_MSG WAV_P_PrintHeader(container); #endif return 0; }
//File : sndwav_common.h //Author : Loon <[email protected]> #ifndef __SNDWAV_COMMON_H #define __SNDWAV_COMMON_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include "wav_parser.h" typedef long long off64_t; typedef struct SNDPCMContainer { snd_pcm_t *handle; snd_output_t *log; snd_pcm_uframes_t chunk_size; snd_pcm_uframes_t buffer_size; snd_pcm_format_t format; uint16_t channels; size_t chunk_bytes; size_t bits_per_sample; size_t bits_per_frame; uint8_t *data_buf; } SNDPCMContainer_t; ssize_t SNDWAV_ReadPcm(SNDPCMContainer_t *sndpcm, size_t rcount); ssize_t SNDWAV_WritePcm(SNDPCMContainer_t *sndpcm, size_t wcount); int SNDWAV_SetParams(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav); #endif /* #ifndef __SNDWAV_COMMON_H */
//File : sndwav_common.c //Author : Loon <[email protected]> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <alsa/asoundlib.h> #include "sndwav_common.h" int SNDWAV_P_GetFormat(WAVContainer_t *wav, snd_pcm_format_t *snd_format) { if (LE_SHORT(wav->format.format) != WAV_FMT_PCM) return -1; switch (LE_SHORT(wav->format.sample_length)) { case 16: *snd_format = SND_PCM_FORMAT_S16_LE; break; case 8: *snd_format = SND_PCM_FORMAT_U8; break; default: *snd_format = SND_PCM_FORMAT_UNKNOWN; break; } return 0; } ssize_t SNDWAV_ReadPcm(SNDPCMContainer_t *sndpcm, size_t rcount) { ssize_t r; size_t result = 0; size_t count = rcount; uint8_t *data = sndpcm->data_buf; if (count != sndpcm->chunk_size) { count = sndpcm->chunk_size; } while (count > 0) { r = snd_pcm_readi(sndpcm->handle, data, count); if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) { snd_pcm_wait(sndpcm->handle, 1000); } else if (r == -EPIPE) { snd_pcm_prepare(sndpcm->handle); fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>/n"); } else if (r == -ESTRPIPE) { fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>/n"); } else if (r < 0) { fprintf(stderr, "Error snd_pcm_writei: [%s]", snd_strerror(r)); exit(-1); } if (r > 0) { result += r; count -= r; data += r * sndpcm->bits_per_frame / 8; } } return rcount; } ssize_t SNDWAV_WritePcm(SNDPCMContainer_t *sndpcm, size_t wcount) { ssize_t r; ssize_t result = 0; uint8_t *data = sndpcm->data_buf; if (wcount < sndpcm->chunk_size) { snd_pcm_format_set_silence(sndpcm->format, data + wcount * sndpcm->bits_per_frame / 8, (sndpcm->chunk_size - wcount) * sndpcm->channels); wcount = sndpcm->chunk_size; } while (wcount > 0) { r = snd_pcm_writei(sndpcm->handle, data, wcount); if (r == -EAGAIN || (r >= 0 && (size_t)r < wcount)) { snd_pcm_wait(sndpcm->handle, 1000); } else if (r == -EPIPE) { snd_pcm_prepare(sndpcm->handle); fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>/n"); } else if (r == -ESTRPIPE) { fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>/n"); } else if (r < 0) { fprintf(stderr, "Error snd_pcm_writei: [%s]", snd_strerror(r)); exit(-1); } if (r > 0) { result += r; wcount -= r; data += r * sndpcm->bits_per_frame / 8; } } return result; } int SNDWAV_SetParams(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav) { snd_pcm_hw_params_t *hwparams; snd_pcm_format_t format; uint32_t exact_rate; uint32_t buffer_time, period_time; /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_alloca(&hwparams); /* Init hwparams with full configuration space */ if (snd_pcm_hw_params_any(sndpcm->handle, hwparams) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_any/n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_access(sndpcm->handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_access/n"); goto ERR_SET_PARAMS; } /* Set sample format */ if (SNDWAV_P_GetFormat(wav, &format) < 0) { fprintf(stderr, "Error get_snd_pcm_format/n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_format(sndpcm->handle, hwparams, format) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_format/n"); goto ERR_SET_PARAMS; } sndpcm->format = format; /* Set number of channels */ if (snd_pcm_hw_params_set_channels(sndpcm->handle, hwparams, LE_SHORT(wav->format.channels)) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_channels/n"); goto ERR_SET_PARAMS; } sndpcm->channels = LE_SHORT(wav->format.channels); /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_rate = LE_INT(wav->format.sample_rate); if (snd_pcm_hw_params_set_rate_near(sndpcm->handle, hwparams, &exact_rate, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_rate_near/n"); goto ERR_SET_PARAMS; } if (LE_INT(wav->format.sample_rate) != exact_rate) { fprintf(stderr, "The rate %d Hz is not supported by your hardware./n ==> Using %d Hz instead./n", LE_INT(wav->format.sample_rate), exact_rate); } if (snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_get_buffer_time_max/n"); goto ERR_SET_PARAMS; } if (buffer_time > 500000) buffer_time = 500000; period_time = buffer_time / 4; if (snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle, hwparams, &buffer_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_buffer_time_near/n"); goto ERR_SET_PARAMS; } if (snd_pcm_hw_params_set_period_time_near(sndpcm->handle, hwparams, &period_time, 0) < 0) { fprintf(stderr, "Error snd_pcm_hw_params_set_period_time_near/n"); goto ERR_SET_PARAMS; } /* Set hw params */ if (snd_pcm_hw_params(sndpcm->handle, hwparams) < 0) { fprintf(stderr, "Error snd_pcm_hw_params(handle, params)/n"); goto ERR_SET_PARAMS; } snd_pcm_hw_params_get_period_size(hwparams, &sndpcm->chunk_size, 0); snd_pcm_hw_params_get_buffer_size(hwparams, &sndpcm->buffer_size); if (sndpcm->chunk_size == sndpcm->buffer_size) { fprintf(stderr, ("Can't use period equal to buffer size (%lu == %lu)/n"), sndpcm->chunk_size, sndpcm->buffer_size); goto ERR_SET_PARAMS; } sndpcm->bits_per_sample = snd_pcm_format_physical_width(format); sndpcm->bits_per_frame = sndpcm->bits_per_sample * LE_SHORT(wav->format.channels); sndpcm->chunk_bytes = sndpcm->chunk_size * sndpcm->bits_per_frame / 8; /* Allocate audio data buffer */ sndpcm->data_buf = (uint8_t *)malloc(sndpcm->chunk_bytes); if (!sndpcm->data_buf) { fprintf(stderr, "Error malloc: [data_buf]/n"); goto ERR_SET_PARAMS; } return 0; ERR_SET_PARAMS: return -1; }
//File : lplay.c //Author : Loon <[email protected]> #include <stdio.h> #include <malloc.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <fcntl.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <time.h> #include <locale.h> #include <sys/unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <alsa/asoundlib.h> #include <assert.h> #include "wav_parser.h" #include "sndwav_common.h" ssize_t SNDWAV_P_SaveRead(int fd, void *buf, size_t count) { ssize_t result = 0, res; while (count > 0) { if ((res = read(fd, buf, count)) == 0) break; if (res < 0) return result > 0 ? result : res; count -= res; result += res; buf = (char *)buf + res; } return result; } void SNDWAV_Play(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav, int fd) { int load, ret; off64_t written = 0; off64_t c; off64_t count = LE_INT(wav->chunk.length); load = 0; while (written < count) { /* Must read [chunk_bytes] bytes data enough. */ do { c = count - written; if (c > sndpcm->chunk_bytes) c = sndpcm->chunk_bytes; c -= load; if (c == 0) break; ret = SNDWAV_P_SaveRead(fd, sndpcm->data_buf + load, c); if (ret < 0) { fprintf(stderr, "Error safe_read/n"); exit(-1); } if (ret == 0) break; load += ret; } while ((size_t)load < sndpcm->chunk_bytes); /* Transfer to size frame */ load = load * 8 / sndpcm->bits_per_frame; ret = SNDWAV_WritePcm(sndpcm, load); if (ret != load) break; ret = ret * sndpcm->bits_per_frame / 8; written += ret; load = 0; } } int main(int argc, char *argv[]) { char *filename; char *devicename = "default"; int fd; WAVContainer_t wav; SNDPCMContainer_t playback; if (argc != 2) { fprintf(stderr, "Usage: ./lplay <FILENAME>/n"); return -1; } memset(&playback, 0x0, sizeof(playback)); filename = argv[1]; fd = open(filename, O_RDONLY); if (fd < 0) { fprintf(stderr, "Error open [%s]/n", filename); return -1; } if (WAV_ReadHeader(fd, &wav) < 0) { fprintf(stderr, "Error WAV_Parse [%s]/n", filename); goto Err; } if (snd_output_stdio_attach(&playback.log, stderr, 0) < 0) { fprintf(stderr, "Error snd_output_stdio_attach/n"); goto Err; } if (snd_pcm_open(&playback.handle, devicename, SND_PCM_STREAM_PLAYBACK, 0) < 0) { fprintf(stderr, "Error snd_pcm_open [ %s]/n", devicename); goto Err; } if (SNDWAV_SetParams(&playback, &wav) < 0) { fprintf(stderr, "Error set_snd_pcm_params/n"); goto Err; } snd_pcm_dump(playback.handle, playback.log); SNDWAV_Play(&playback, &wav, fd); snd_pcm_drain(playback.handle); close(fd); free(playback.data_buf); snd_output_close(playback.log); snd_pcm_close(playback.handle); return 0; Err: close(fd); if (playback.data_buf) free(playback.data_buf); if (playback.log) snd_output_close(playback.log); if (playback.handle) snd_pcm_close(playback.handle); return -1; }
//File : lrecord.c //Author : Loon <[email protected]> #include <stdio.h> #include <malloc.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <fcntl.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <time.h> #include <locale.h> #include <sys/unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <alsa/asoundlib.h> #include <assert.h> #include "wav_parser.h" #include "sndwav_common.h" #define DEFAULT_CHANNELS (2) #define DEFAULT_SAMPLE_RATE (8000) #define DEFAULT_SAMPLE_LENGTH (16) #define DEFAULT_DURATION_TIME (10) int SNDWAV_PrepareWAVParams(WAVContainer_t *wav) { assert(wav); uint16_t channels = DEFAULT_CHANNELS; uint16_t sample_rate = DEFAULT_SAMPLE_RATE; uint16_t sample_length = DEFAULT_SAMPLE_LENGTH; uint32_t duration_time = DEFAULT_DURATION_TIME; /* Const */ wav->header.magic = WAV_RIFF; wav->header.type = WAV_WAVE; wav->format.magic = WAV_FMT; wav->format.fmt_size = LE_INT(16); wav->format.format = LE_SHORT(WAV_FMT_PCM); wav->chunk.type = WAV_DATA; /* User definition */ wav->format.channels = LE_SHORT(channels); wav->format.sample_rate = LE_INT(sample_rate); wav->format.sample_length = LE_SHORT(sample_length); /* See format of wav file */ wav->format.blocks_align = LE_SHORT(channels * sample_length / 8); wav->format.bytes_p_second = LE_INT((uint16_t)(wav->format.blocks_align) * sample_rate); wav->chunk.length = LE_INT(duration_time * (uint32_t)(wav->format.bytes_p_second)); wav->header.length = LE_INT((uint32_t)(wav->chunk.length) +/ sizeof(wav->chunk) + sizeof(wav->format) + sizeof(wav->header) - 8); return 0; } void SNDWAV_Record(SNDPCMContainer_t *sndpcm, WAVContainer_t *wav, int fd) { off64_t rest; size_t c, frame_size; if (WAV_WriteHeader(fd, wav) < 0) { exit(-1); } rest = wav->chunk.length; while (rest > 0) { c = (rest <= (off64_t)sndpcm->chunk_bytes) ? (size_t)rest : sndpcm->chunk_bytes; frame_size = c * 8 / sndpcm->bits_per_frame; if (SNDWAV_ReadPcm(sndpcm, frame_size) != frame_size) break; if (write(fd, sndpcm->data_buf, c) != c) { fprintf(stderr, "Error SNDWAV_Record[write]/n"); exit(-1); } rest -= c; } } int main(int argc, char *argv[]) { char *filename; char *devicename = "default"; int fd; WAVContainer_t wav; SNDPCMContainer_t record; if (argc != 2) { fprintf(stderr, "Usage: ./lrecord <FILENAME>/n"); return -1; } memset(&record, 0x0, sizeof(record)); filename = argv[1]; remove(filename); if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) == -1) { fprintf(stderr, "Error open: [%s]/n", filename); return -1; } if (snd_output_stdio_attach(&record.log, stderr, 0) < 0) { fprintf(stderr, "Error snd_output_stdio_attach/n"); goto Err; } if (snd_pcm_open(&record.handle, devicename, SND_PCM_STREAM_CAPTURE, 0) < 0) { fprintf(stderr, "Error snd_pcm_open [ %s]/n", devicename); goto Err; } if (SNDWAV_PrepareWAVParams(&wav) < 0) { fprintf(stderr, "Error SNDWAV_PrepareWAVParams/n"); goto Err; } if (SNDWAV_SetParams(&record, &wav) < 0) { fprintf(stderr, "Error set_snd_pcm_params/n"); goto Err; } snd_pcm_dump(record.handle, record.log); SNDWAV_Record(&record, &wav, fd); snd_pcm_drain(record.handle); close(fd); free(record.data_buf); snd_output_close(record.log); snd_pcm_close(record.handle); return 0; Err: close(fd); remove(filename); if (record.data_buf) free(record.data_buf); if (record.log) snd_output_close(record.log); if (record.handle) snd_pcm_close(record.handle); return -1; }