123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- /*
- * nanoalsa/test_async.c
- *
- * Copyright (C) 2023 bzt (bztsrc@gitlab) MIT license
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy,
- * modify, merge, publish, distribute, sublicense, and/or sell copies
- * of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * @brief Test playing PCM in the background
- * https://gitlab.com/bztsrc/nanoalsa
- */
- #define _POSIX_C_SOURCE 199309L /* needed for timespec */
- #include <stdio.h>
- /* all you need to do to enable async playback is to include pthread header */
- #include <pthread.h>
- #define ALSA_IMPLEMENTATION
- #include "nanoalsa.h"
- /* riff wave header */
- typedef struct {
- char str_riff[4]; /* "RIFF" */
- int wav_size; /* (file size) - 8 */
- char str_wave[4]; /* "WAVE" */
-
- /* Format Header */
- char str_fmt[4]; /* "fmt " */
- int fmt_chunk_size; /* Should be 16 for PCM */
- short audio_format; /* Should be 1 for PCM. 3 for IEEE Float */
- short channels; /* number of channels */
- int sample_rate; /* eg: 8000, 44100, 48000 */
- int byte_rate; /* Number of bytes per second. sample_rate * channels * Bytes Per Sample */
- short frame_size; /* channels * Bytes Per Sample */
- short bit_depth; /* bits per sample, eg: 8, 16, 24 */
-
- /* Data chunk */
- char str_data[4]; /* "data" */
- int data_bytes; /* (file size) - 44 */
- } wav_header_t;
- wav_header_t *wav;
- unsigned char *data, *end, *playback;
- /* helper to read file into memory */
- unsigned char* readfileall(char *file, int *size)
- {
- unsigned char *data = NULL;
- FILE *f;
- *size = 0;
- f = fopen(file, "rb");
- if(f){
- fseek(f, 0L, SEEK_END);
- *size = ftell(f);
- fseek(f, 0L, SEEK_SET);
- data = (unsigned char*)malloc(*size);
- if(!data) { fprintf(stderr, "unable to allocate memory\n"); exit(1); }
- fread(data, 1, *size, f);
- fclose(f);
- }
- return data;
- }
- /* textual error messages. nanoalsa just returns error codes */
- const char *alsa_err[] = {
- "ALSA_SUCCESS - ok",
- "ALSA_ERR_INP - bad function input arguments",
- "ALSA_ERR_DEV - no such device",
- "ALSA_ERR_HWPAR - ioctl hw parameter not supported",
- "ALSA_ERR_SWPAR - ioctl sw parameter not supported",
- "ALSA_ERR_MMAP - failed to mmap status or control registers",
- "ALSA_ERR_PREP - failed to prepare channel",
- "ALSA_ERR_THREAD - failed to create worker thread"
- };
- /**
- * The callback function. It's only job is to fill in the audio buffer
- */
- void callback(void *buf, int samplesize, int channels, int frames, void *priv)
- {
- int len = samplesize * channels * frames;
- /* we don't need private data. We could have placed the `playback` and `end` pointers there, but we just use globals */
- (void)priv;
- /* let's see if we have enough remaining samples. If not, then start over */
- if(playback + len >= end) len = end - playback;
- if(len < 1) { playback = data + sizeof(wav_header_t); len = samplesize * channels * frames; }
- /* fill in the buffer with PCM data and move pointer to the next batch */
- memcpy(buf, playback, len);
- playback += len;
- }
- /**
- * Main procedure, entry point
- */
- int main(int argc, char **argv)
- {
- alsa_t ctx;
- int ret, len = 0, fmt = SNDRV_PCM_FORMAT_LAST;
- /*** read in wav file ***/
- if(argc < 2) { fprintf(stderr, "./test_async <wav>\n"); exit(1); }
- data = readfileall(argv[1], &len);
- if(!data || len < (int)sizeof(wav_header_t)) { fprintf(stderr, "unable to read wav\n"); exit(1); }
- wav = (wav_header_t*)data;
- switch(wav->audio_format) {
- case 1:
- switch(wav->bit_depth) {
- case 8: fmt = SNDRV_PCM_FORMAT_U8; break;
- case 16: fmt = SNDRV_PCM_FORMAT_S16_LE; break;
- case 32: fmt = SNDRV_PCM_FORMAT_S32_LE; break;
- }
- break;
- case 3:
- switch(wav->bit_depth) {
- case 32: fmt = SNDRV_PCM_FORMAT_FLOAT_LE; break;
- case 64: fmt = SNDRV_PCM_FORMAT_FLOAT64_LE; break;
- }
- break;
- }
- printf("wav file: %s %u bits, %u channels, length %u frames\n",
- wav->audio_format == 3 ? "float" : "int", wav->bit_depth, wav->channels,
- wav->data_bytes / wav->frame_size);
- /*** open the PCM context ***/
- if((ret = alsa_open(&ctx, 0, 0, fmt, wav->sample_rate, wav->channels))) {
- fprintf(stderr, "unable to open sound card device: %s\n", alsa_err[ret]);
- exit(0);
- }
- /*** play PCM data ***/
- /* set pointers for out callback */
- playback = data + sizeof(wav_header_t); end = data + wav->data_bytes;
-
- /* specify the callback function and start playing in the background */
- alsa_start(&ctx, callback, NULL);
- /* do something in the foreground... */
- sleep(3);
- /* stop the background playback */
- alsa_stop(&ctx);
- /*** free resources ***/
- alsa_close(&ctx);
- free(data);
- return 0;
- }
|