From 514ddea9f736b69919f2ee928554bff5d5831075 Mon Sep 17 00:00:00 2001 From: Ahmet Inan Date: Thu, 8 Sep 2011 18:14:44 +0200 Subject: [PATCH] added support for write to alsa devices and wav files --- alsa.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++-- alsa.h | 3 +- decode.c | 2 +- pcm.c | 22 +++++++++--- pcm.h | 6 ++-- wav.c | 103 ++++++++++++++++++++++++++++++++++++++----------------- wav.h | 3 +- 7 files changed, 186 insertions(+), 43 deletions(-) diff --git a/alsa.c b/alsa.c index 71ee798..011c62a 100644 --- a/alsa.c +++ b/alsa.c @@ -16,7 +16,7 @@ typedef struct { void (*info)(pcm_t *); int (*rate)(pcm_t *); int (*channels)(pcm_t *); - int (*read)(struct pcm *, short *, int); + int (*rw)(struct pcm *, short *, int); snd_pcm_t *pcm; int r; int c; @@ -58,14 +58,29 @@ int read_alsa(pcm_t *pcm, short *buff, int frames) return 1; } -int open_alsa(pcm_t **p, char *name) +int write_alsa(pcm_t *pcm, short *buff, int frames) +{ + alsa_t *alsa = (alsa_t *)pcm; + int got = 0; + while (0 < frames) { + while ((got = snd_pcm_writei(alsa->pcm, buff, frames)) < 0) { + snd_pcm_prepare(alsa->pcm); + fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Overrun >>>>>>>>>>>>>>>\n"); + } + buff += got * alsa->c; + frames -= got; + } + return 1; +} + +int open_alsa_read(pcm_t **p, char *name) { alsa_t *alsa = (alsa_t *)malloc(sizeof(alsa_t)); alsa->close = close_alsa; alsa->info = info_alsa; alsa->rate = rate_alsa; alsa->channels = channels_alsa; - alsa->read = read_alsa; + alsa->rw = read_alsa; snd_pcm_t *pcm; snd_pcm_hw_params_t *params; @@ -132,3 +147,72 @@ int open_alsa(pcm_t **p, char *name) return 1; } +int open_alsa_write(pcm_t **p, char *name, int rate, int channels) +{ + alsa_t *alsa = (alsa_t *)malloc(sizeof(alsa_t)); + alsa->close = close_alsa; + alsa->info = info_alsa; + alsa->rate = rate_alsa; + alsa->channels = channels_alsa; + alsa->rw = write_alsa; + + snd_pcm_t *pcm; + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca(¶ms); + + if (snd_pcm_open(&pcm, name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { + fprintf(stderr, "Error opening PCM device %s\n", name); + free(alsa); + return 0; + } + + if (snd_pcm_hw_params_any(pcm, params) < 0) { + fprintf(stderr, "Can not configure this PCM device.\n"); + snd_pcm_close(alsa->pcm); + free(alsa); + return 0; + } + + if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + fprintf(stderr, "Error setting access.\n"); + snd_pcm_close(alsa->pcm); + free(alsa); + return 0; + } + + if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) { + fprintf(stderr, "Error setting S16_LE format.\n"); + snd_pcm_close(alsa->pcm); + free(alsa); + return 0; + } + + if (snd_pcm_hw_params_set_rate_resample(pcm, params, 0) < 0) { + fprintf(stderr, "Error disabling resampling.\n"); + snd_pcm_close(alsa->pcm); + free(alsa); + return 0; + } + + if (snd_pcm_hw_params_set_rate_near(pcm, params, (unsigned int *)&rate, 0) < 0) { + fprintf(stderr, "Error setting rate.\n"); + return 0; + } + + if (snd_pcm_hw_params_set_channels_near(pcm, params, (unsigned int *)&channels) < 0) { + fprintf(stderr, "Error setting channels.\n"); + return 0; + } + + if (snd_pcm_hw_params(pcm, params) < 0) { + fprintf(stderr, "Error setting HW params.\n"); + snd_pcm_close(alsa->pcm); + free(alsa); + return 0; + } + alsa->pcm = pcm; + alsa->r = rate; + alsa->c = channels; + *p = (pcm_t *)alsa; + return 1; +} diff --git a/alsa.h b/alsa.h index c0971fb..d10f78b 100644 --- a/alsa.h +++ b/alsa.h @@ -9,6 +9,7 @@ You should have received a copy of the CC0 Public Domain Dedication along with t #ifndef ALSA_H #define ALSA_H #include "pcm.h" -int open_alsa(pcm_t **, char *); +int open_alsa_read(pcm_t **, char *); +int open_alsa_write(pcm_t **, char *, int, int); #endif diff --git a/decode.c b/decode.c index ec7b827..5af04c8 100644 --- a/decode.c +++ b/decode.c @@ -46,7 +46,7 @@ int main(int argc, char **argv) if (argc == 3) ppm_name = argv[2]; - if (!open_pcm(&pcm, pcm_name)) { + if (!open_pcm_read(&pcm, pcm_name)) { fprintf(stderr, "couldnt open %s\n", pcm_name); return 1; } diff --git a/pcm.c b/pcm.c index 90981ae..dd08a06 100644 --- a/pcm.c +++ b/pcm.c @@ -36,15 +36,29 @@ int channels_pcm(pcm_t *pcm) int read_pcm(pcm_t *pcm, short *buff, int frames) { - return pcm->read(pcm, buff, frames); + return pcm->rw(pcm, buff, frames); } -int open_pcm(pcm_t **p, char *name) +int write_pcm(pcm_t *pcm, short *buff, int frames) +{ + return pcm->rw(pcm, buff, frames); +} + +int open_pcm_read(pcm_t **p, char *name) { if (strstr(name, "plughw:") == name || strstr(name, "hw:") == name || strstr(name, "default") == name) - return open_alsa(p, name); + return open_alsa_read(p, name); if (strstr(name, ".wav") == (name + (strlen(name) - strlen(".wav")))) - return open_wav(p, name); + return open_wav_read(p, name); + return 0; +} + +int open_pcm_write(pcm_t **p, char *name, int rate, int channels) +{ + if (strstr(name, "plughw:") == name || strstr(name, "hw:") == name || strstr(name, "default") == name) + return open_alsa_write(p, name, rate, channels); + if (strstr(name, ".wav") == (name + (strlen(name) - strlen(".wav")))) + return open_wav_write(p, name, rate, channels); return 0; } diff --git a/pcm.h b/pcm.h index 624d586..00af0a8 100644 --- a/pcm.h +++ b/pcm.h @@ -14,7 +14,7 @@ typedef struct pcm { void (*info)(struct pcm *); int (*rate)(struct pcm *); int (*channels)(struct pcm *); - int (*read)(struct pcm *, short *, int); + int (*rw)(struct pcm *, short *, int); } pcm_t; void close_pcm(pcm_t *); @@ -22,7 +22,9 @@ void info_pcm(pcm_t *); int rate_pcm(pcm_t *); int channels_pcm(pcm_t *); int read_pcm(pcm_t *, short *, int); -int open_pcm(pcm_t **, char *); +int write_pcm(pcm_t *, short *, int); +int open_pcm_read(pcm_t **, char *); +int open_pcm_write(pcm_t **, char *, int, int); #endif diff --git a/wav.c b/wav.c index d51ce2d..311ee61 100644 --- a/wav.c +++ b/wav.c @@ -34,14 +34,12 @@ typedef struct { void (*info)(pcm_t *); int (*rate)(pcm_t *); int (*channels)(pcm_t *); - int (*read)(struct pcm *, short *, int); + int (*rw)(struct pcm *, short *, int); void *p; + wav_head_t *head; short *b; size_t size; - int r; - int c; - int samples; - int index; + unsigned int index; } wav_t; void close_wav(pcm_t *pcm) @@ -53,63 +51,106 @@ void close_wav(pcm_t *pcm) void info_wav(pcm_t *pcm) { wav_t *wav = (wav_t *)pcm; - fprintf(stderr, "%d channel(s), %d rate, %d samples\n", wav->c, wav->r, wav->samples); + fprintf(stderr, "%d channel(s), %d rate, %d samples\n", wav->head->NumChannels, wav->head->SampleRate, wav->head->Subchunk2Size / 2); } int rate_wav(pcm_t *pcm) { wav_t *wav = (wav_t *)pcm; - return wav->r; + return wav->head->SampleRate; } int channels_wav(pcm_t *pcm) { wav_t *wav = (wav_t *)pcm; - return wav->c; + return wav->head->NumChannels; } int read_wav(pcm_t *pcm, short *buff, int frames) { wav_t *wav = (wav_t *)pcm; - if ((wav->index + frames * wav->c) > wav->samples) + if ((wav->index + frames * wav->head->NumChannels) > (wav->head->Subchunk2Size / 2)) return 0; - memcpy(buff, wav->b + wav->index, sizeof(short) * frames * wav->c); - wav->index += frames * wav->c; + memcpy(buff, wav->b + wav->index, sizeof(short) * frames * wav->head->NumChannels); + wav->index += frames * wav->head->NumChannels; + return 1; +} +int write_wav(pcm_t *pcm, short *buff, int frames) +{ + wav_t *wav = (wav_t *)pcm; + if ((wav->index + frames * wav->head->NumChannels) > (wav->head->Subchunk2Size / 2)) + return 0; + memcpy(wav->b + wav->index, buff, sizeof(short) * frames * wav->head->NumChannels); + wav->index += frames * wav->head->NumChannels; return 1; } -int open_wav(pcm_t **p, char *name) +int open_wav_read(pcm_t **p, char *name) { wav_t *wav = (wav_t *)malloc(sizeof(wav_t)); wav->close = close_wav; wav->info = info_wav; wav->rate = rate_wav; wav->channels = channels_wav; - wav->read = read_wav; + wav->rw = read_wav; if (!mmap_file_ro(&wav->p, name, &wav->size)) { fprintf(stderr, "couldnt open wav file %s!\n", name); free(wav); return 0; } + wav->head = (wav_head_t *)wav->p; + wav->b = (short *)(wav->p + sizeof(wav_head_t)); + + if (wav->head->ChunkID != 0x46464952 || wav->head->Format != 0x45564157 || + wav->head->Subchunk1ID != 0x20746d66 || wav->head->Subchunk1Size != 16 || + wav->head->AudioFormat != 1 || wav->head->Subchunk2ID != 0x61746164) { + fprintf(stderr, "unsupported WAV file!\n"); + munmap_file(wav->p, wav->size); + free(wav); + return 0; + } + if (wav->head->BitsPerSample != 16) { + fprintf(stderr, "only 16bit WAV supported!\n"); + munmap_file(wav->p, wav->size); + free(wav); + return 0; + } + wav->index = 0; + *p = (pcm_t *)wav; + return 1; +} + +int open_wav_write(pcm_t **p, char *name, int rate, int channels) +{ + wav_t *wav = (wav_t *)malloc(sizeof(wav_t)); + wav->close = close_wav; + wav->info = info_wav; + wav->rate = rate_wav; + wav->channels = channels_wav; + wav->rw = write_wav; + wav->size = 4096; + if (!mmap_file_rw(&wav->p, name, wav->size)) { + fprintf(stderr, "couldnt open wav file %s!\n", name); + free(wav); + return 0; + } wav_head_t *head = (wav_head_t *)wav->p; wav->b = (short *)(wav->p + sizeof(wav_head_t)); - if (head->ChunkID != 0x46464952 || head->Format != 0x45564157 || - head->Subchunk1ID != 0x20746d66 || head->Subchunk1Size != 16 || - head->AudioFormat != 1 || head->Subchunk2ID != 0x61746164) { - fprintf(stderr, "unsupported WAV file!\n"); - munmap_file(wav->p, wav->size); - free(wav); - return 0; - } - if (head->BitsPerSample != 16) { - fprintf(stderr, "only 16bit WAV supported!\n"); - munmap_file(wav->p, wav->size); - free(wav); - return 0; - } - wav->c = head->NumChannels; - wav->samples = head->Subchunk2Size / 2; + int samples = (wav->size - 44) / 2; wav->index = 0; - wav->r = head->SampleRate; + + head->ChunkID = 0x46464952; + head->ChunkSize = 36 + 2 * samples; + head->Format = 0x45564157; + head->Subchunk1ID = 0x20746d66; + head->Subchunk1Size = 16; + head->AudioFormat = 1; + head->NumChannels = channels; + head->SampleRate = rate; + head->ByteRate = 2 * rate; + head->BlockAlign = 2; + head->BitsPerSample = 16; + head->Subchunk2ID = 0x61746164; + head->Subchunk2Size = 2 * samples; + *p = (pcm_t *)wav; return 1; } - diff --git a/wav.h b/wav.h index e8b4ac3..edd3ae5 100644 --- a/wav.h +++ b/wav.h @@ -9,5 +9,6 @@ You should have received a copy of the CC0 Public Domain Dedication along with t #ifndef WAV_H #define WAV_H #include "pcm.h" -int open_wav(pcm_t **, char *); +int open_wav_read(pcm_t **, char *); +int open_wav_write(pcm_t **, char *, int, int); #endif