* apps/audioutils/alsa-lib/aplay.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <alsa/asoundlib.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
* Pre-processor Definitions
****************************************************************************/
#define DEFAULT_RATE 44100
#define DEFAULT_CHANNELS 2
#define DEFAULT_FORMAT SND_PCM_FORMAT_S16
#define DEFAULT_DEVICE "default"
#define DEFAULT_PERIOD_TIME 20000
#define DEFAULT_BUFFER_TIME 80000
* Private Functions
****************************************************************************/
static void usage(FAR const char *command)
{
printf("Usage: %s [OPTION]... [FILE]...\n", command);
printf("\n");
printf(" -h, --help Display this help message and exit\n");
printf(" -D, --device=NAME Specify the PCM device (default: %s)\n",
DEFAULT_DEVICE);
printf(" -r, --rate=# Sampling rate (Hz), default %d\n",
DEFAULT_RATE);
printf(" -c, --channels=# Number of channels, default %d\n",
DEFAULT_CHANNELS);
printf(" -d, --duration=# interrupt after # seconds\n");
printf(" -F, --period-time=# distance between interrupts is # "
"microseconds, default %d\n",
DEFAULT_PERIOD_TIME);
printf(" -B, --buffer-time=# buffer duration is # microseconds, "
"default %d\n",
DEFAULT_BUFFER_TIME);
printf(" -f, --format=FORMAT Sample format, default S16_LE\n");
printf("The available format shortcuts are:\n");
printf("-f cd (16 bit little endian, 44100, stereo)\n");
printf("-f dat (16 bit little endian, 48000, stereo)\n");
}
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
snd_pcm_format_t format = DEFAULT_FORMAT;
unsigned int buffer_time = DEFAULT_BUFFER_TIME;
unsigned int period_time = DEFAULT_PERIOD_TIME;
FAR const char *device = DEFAULT_DEVICE;
unsigned int rate = DEFAULT_RATE;
unsigned int channels = DEFAULT_CHANNELS;
snd_pcm_uframes_t frames_limit = ULONG_MAX;
snd_pcm_uframes_t frames_written = 0;
snd_pcm_uframes_t frames;
FAR snd_pcm_hw_params_t *hwparams;
FAR const char *file_name = NULL;
FAR snd_pcm_t *handle;
unsigned int duration = 0;
size_t frame_bytes;
size_t buffer_size;
ssize_t read_bytes;
FAR char *endptr;
FAR char *buffer;
int option_index;
int fd = -1;
int opt;
int ret = EXIT_FAILURE;
static const struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"device", required_argument, 0, 'D'},
{"rate", required_argument, 0, 'r'},
{"channels", required_argument, 0, 'c'},
{"duration", required_argument, 0, 'd'},
{"period-time", required_argument, 0, 'F'},
{"buffer-time", required_argument, 0, 'B'},
{"format", required_argument, 0, 'f'},
{0, 0, 0, 0}
};
if (argc < 2)
{
usage(argv[0]);
return ret;
}
while ((opt = getopt_long(argc, argv, "hD:r:c:d:F:B:f:", long_options,
&option_index)) != -1)
{
switch (opt)
{
case 'h':
usage(argv[0]);
return EXIT_SUCCESS;
case 'D':
device = optarg;
break;
case 'r':
rate = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
{
printf("Invalid sampling rate: %s\n", optarg);
return ret;
}
break;
case 'd':
duration = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
{
printf("Invalid duration: %s\n", optarg);
return ret;
}
break;
case 'c':
channels = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
{
printf("Invalid number of channels: %s\n", optarg);
return ret;
}
break;
case 'F':
period_time = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
{
printf("Invalid period time: %s\n", optarg);
return ret;
}
break;
case 'B':
buffer_time = strtoul(optarg, &endptr, 10);
if (*endptr != '\0')
{
printf("Invalid buffer time: %s\n", optarg);
return ret;
}
break;
case 'f':
if (strcasecmp(optarg, "cd") == 0)
{
format = SND_PCM_FORMAT_S16_LE;
rate = 44100;
channels = 2;
}
else if (strcasecmp(optarg, "dat") == 0)
{
format = SND_PCM_FORMAT_S16_LE;
rate = 48000;
channels = 2;
}
else
{
format = snd_pcm_format_value(optarg);
if (format == SND_PCM_FORMAT_UNKNOWN)
{
printf("Unsupported sample format: %s\n", optarg);
return ret;
}
}
break;
default:
usage(argv[0]);
return ret;
}
}
if (optind < argc)
{
file_name = argv[optind];
}
if (file_name)
{
fd = open(file_name, O_RDONLY | O_BINARY);
if (fd == -1)
{
printf("unable to open %s err:%s\n", file_name, strerror(errno));
return ret;
}
}
if (snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0) < 0)
{
printf("Unable to open audio device %s\n", device);
goto file_exit;
}
snd_pcm_hw_params_alloca(&hwparams);
if (snd_pcm_hw_params_any(handle, hwparams) < 0)
{
printf("Unable to initialize hardware parameter structure\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_access(handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
{
printf("Failed to set interleaved mode\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_format(handle, hwparams, format) < 0)
{
printf("Failed to set sample format\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_channels(handle, hwparams, channels) < 0)
{
printf("Failed to set number of channels\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_rate(handle, hwparams, rate, 0) < 0)
{
printf("Failed to set sampling rate\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_period_time(handle, hwparams, period_time, 0) <
0)
{
printf("Failed to set period time\n");
goto snd_exit;
}
if (snd_pcm_hw_params_set_buffer_time(handle, hwparams, buffer_time, 0) <
0)
{
printf("Failed to set buffer time\n");
goto snd_exit;
}
if (snd_pcm_hw_params(handle, hwparams) < 0)
{
printf("Unable to set hardware parameters\n");
goto snd_exit;
}
frame_bytes = snd_pcm_format_physical_width(format) / 8 * channels;
if (snd_pcm_hw_params_get_buffer_size(hwparams, &frames) < 0)
{
printf("Unable to get buffer size\n");
goto snd_exit;
}
buffer_size = frames * frame_bytes;
buffer = malloc(buffer_size);
if (!buffer)
{
printf("Failed to allocate buffer, size:%zu\n", buffer_size);
goto snd_exit;
}
if (duration > 0)
{
frames_limit = duration * rate;
}
while ((read_bytes = read(fd, buffer, buffer_size)) > 0)
{
frames = read_bytes / frame_bytes;
frames_written += frames;
while (frames > 0)
{
ret = snd_pcm_writei(handle, buffer, frames);
if (ret < 0)
{
ret = snd_pcm_recover(handle, ret, 0);
if (ret < 0)
{
printf("snd_pcm_writei error: %s\n", snd_strerror(ret));
ret = EXIT_FAILURE;
goto buf_exit;
}
}
frames -= ret;
}
if (frames_written >= frames_limit)
{
break;
}
}
snd_pcm_drain(handle);
ret = EXIT_SUCCESS;
buf_exit:
free(buffer);
snd_exit:
snd_pcm_close(handle);
file_exit:
close(fd);
return ret;
}