Files
2026-08-24 10:03:24 +08:00

438 lines
12 KiB
C

/*
* Copyright (C) 2021 Amlogic Corporation.
*
* Licensed 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.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "audio_if.h"
#include <signal.h>
#include <math.h>
#define VOLUME_MIN_DB (-100)
#define VOLUME_DEFAULT_DB (-50)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define WRITE_UNIT 4096
enum audio_format {
FORMAT_PCM16 = 0,
FORMAT_PCM32,
FORMAT_DD,
FORMAT_MAT,
FORMAT_IEC61937,
FORMAT_AC4 = 5,
FORMAT_MP3,
FORMAT_AAC,
FORMAT_OGG,
FORMAT_FLAC,
FORMAT_TRUE_HD,
FORMAT_DTS,
FORMAT_MAX
};
enum AudioPort_Out_t {
AUDIO_PORT_OUT_UNKNOWN = -1,
AUDIO_PORT_OUT_SPEAKER = 0,
AUDIO_PORT_OUT_HEADPHONE = 1,
AUDIO_PORT_OUT_MAX = 2,
};
static int format_tab[] = {
AUDIO_FORMAT_PCM_16_BIT,
AUDIO_FORMAT_PCM_32_BIT,
AUDIO_FORMAT_AC3,
AUDIO_FORMAT_MAT,
AUDIO_FORMAT_IEC61937,
AUDIO_FORMAT_AC4,
AUDIO_FORMAT_MP3,
AUDIO_FORMAT_AAC,
AUDIO_FORMAT_VORBIS,
AUDIO_FORMAT_FLAC,
AUDIO_FORMAT_DOLBY_TRUEHD,
AUDIO_FORMAT_DTS
};
static const char *format_str[] = {
"PCM_16",
"PCM_32",
"DOLBY DD/DD+",
"DOLBY MAT",
"IEC_61937",
"AC4",
"MP3",
"AAC",
"OGG",
"FLAC",
"TRUE_HD",
"DTS"
};
static int format_is_pcm(int format)
{
return (format == FORMAT_PCM16) || (format == FORMAT_PCM32);
}
static unsigned char *fmap(const char *fn, int *size, int *fd)
{
int fd_r, r;
struct stat st;
unsigned char *p;
fd_r = open(fn, O_RDWR);
if (fd_r < 0)
return NULL;
*fd = fd_r;
r = fstat(fd_r, &st);
*size = st.st_size;
return mmap(0, st.st_size, PROT_READ|PROT_EXEC, MAP_SHARED, fd_r, 0);
}
static void funmap(unsigned char *p, int size, int fd)
{
if (p && size > 0)
munmap(p, size);
if (fd >= 0)
close(fd);
}
int debug = 0;
static void get_buffer_size(struct audio_stream_out *stream)
{
int buffer_size = 0;
int buffer_latency = 0;
if (debug) {
buffer_size = stream->common.get_buffer_size(&(stream->common));
buffer_latency = stream->get_latency(stream);
printf("buffer_size: %d, buffer_latency: %d.", buffer_size, buffer_latency);
}
}
int isstop = 0;
static int test_stream(struct audio_stream_out *stream, unsigned char *buf, int size)
{
int ret;
int len = size;
unsigned char *data = buf;
ret = stream->common.standby(&stream->common);
if (ret) {
fprintf(stderr, "%s %d, ret:%x\n",
__func__, __LINE__, ret);
return -1;
}
ret = stream->set_volume(stream, 1.0f, 1.0f);
if (ret) {
fprintf(stderr, "%s %d, ret:%x\n", __func__, __LINE__, ret);
return -1;
}
while (len > 0) {
ssize_t s = stream->write(stream, data, min(len, WRITE_UNIT));
if (s < 0) {
fprintf(stderr, "stream writing error %d\n", s);
break;
}
if (isstop) {
break;
}
printf("stream writing %d \n", s);
len -= s;
data += s;
get_buffer_size(stream);
}
isstop = 0;
return 0;
}
static void test_output_stream(audio_hw_device_t *device, unsigned char *buf, int size, struct audio_config *config)
{
struct audio_stream_out *stream;
int ret;
printf("open output speaker...\n");
ret = device->open_output_stream(device,
0, AUDIO_DEVICE_OUT_SPEAKER,
AUDIO_OUTPUT_FLAG_PRIMARY, config,
&stream, NULL);
if (ret) {
printf("fail\n");
return;
} else {
printf("success\n");
}
test_stream(stream, buf, size);
printf("close output speaker...\n");
device->close_output_stream(device, stream);
}
void handler_halplay(int sig)
{
isstop=1;
while (isstop != 1);
printf("\n handler_halplay...\n");
}
void audio_server_died_cb()
{
printf("[HAL_PLAY]audio_server_died_cb!\n");
}
static float ampToDb(float amplification) {
if (amplification < 0.0f || amplification > 1.0f) {
printf("%s amplification is out of range", __func__);
return VOLUME_DEFAULT_DB;
}
if (amplification == 0) {
return VOLUME_MIN_DB;
} else {
return 20 * log10(amplification);
}
}
int main(int argc, char **argv)
{
audio_hw_device_t *device;
int ret, c = -1, format = FORMAT_MAX, ch = 0, sr = 0, help = 0;
struct audio_config config;
const char *fn = NULL;
int size = 0;
unsigned char *buf;
int fd = -1;
struct audio_port_config port_cfg = {0};
int device_type = AUDIO_PORT_OUT_UNKNOWN;
float volume = 0.5;
if (argc == 1) {
printf("Usage: halplay -f <format> -c <channel number> -r <sample rate> -d <debug flag> -p <vol set port> -v <volume> <filename> \n");
printf("more param Info: halplay -h\n");
return 0;
}
while ((c = getopt(argc, argv, "f:c:r:d:p:v:h")) != -1) {
switch (c) {
case 'f':
format = atoi(optarg);
break;
case 'c':
ch = atoi(optarg);
break;
case 'r':
sr = atoi(optarg);
break;
case 'd':
debug = atoi(optarg);
break;
case 'h':
help = 1;
break;
case 'p':
device_type = atoi(optarg);
break;
case 'v':
volume = atof(optarg);
break;
case '?':
fprintf(stderr, "Error in an argument.\n");
return -1;
default:
return -1;
}
}
if (help == 1) {
printf("Usage: halplay -f <format> -c <channel number> -r <sample rate> -d <debug flag> -p <vol set port> -v <volume> <filename>\n");
printf("\n-h, help\n");
printf("-f, sample format\n");
printf("-c, channels\n");
printf("-r, sample rate\n");
printf("Recognized sample formats are: 0:PCM16 1:PCM32 2:DD 3:MAT 4:IEC61937 5:AC4 6:MP3 7:AAC 8:OGG 9:FLAC 10:true-hd 11:DTS\n");
printf("Some of these may not be available on selected format\n");
printf("the available params for PCM16 and PCM32 are:\n");
printf("-c 1,2,6,8\n");
printf("-r 32000,44100,48000\n");
printf("-d debug flag: 1/0\n");
printf("-p which port you want to set volume, 0:spk 1:hp, default not set\n");
printf("-v volume value(0.0 ~ 1.0, default 0.5 if with '-p')\n");
return 0;
}
if (optind < argc) {
fn = argv[optind];
}
if (!fn) {
fprintf(stderr, "No file name specified\n");
return -1;
}
if ((format < 0) || (format >= FORMAT_MAX)) {
int i;
fprintf(stderr, "Wrong format, valid format:\n");
for (i = 0; i < FORMAT_MAX; i++)
fprintf(stderr, "\t%d: %s\n", i, format_str[i]);
return -1;
}
if (format_is_pcm(format)) {
if ((ch < 1) || (ch > 8)) {
fprintf(stderr, "Wrong channel number, valid range [1-8]\n");
return -1;
}
if ((sr != 32000) && (sr != 44100) && (sr != 48000)) {
fprintf(stderr, "Invalid sample rate, valid options [32000, 44100, 48000]\n");
return -1;
}
if ((ch != 1) && (ch != 2) && (ch != 6) && (ch != 8)) {
fprintf(stderr, "Invalid channel number, valid options [1, 2, 6, 8]\n");
return -1;
}
}
ret = audio_hw_load_interface(&device);
if (ret) {
fprintf(stderr, "%s %d error:%d\n", __func__, __LINE__, ret);
return ret;
}
printf("hw version: %x\n", device->common.version);
printf("hal api version: %x\n", device->common.module->hal_api_version);
printf("module id: %s\n", device->common.module->id);
printf("module name: %s\n", device->common.module->name);
if (device->get_supported_devices) {
uint32_t support_dev = 0;
support_dev = device->get_supported_devices(device);
printf("supported device: %x\n", support_dev);
}
int inited = device->init_check(device);
if (inited) {
printf("device not inited, quit\n");
goto exit;
}
buf = fmap(fn, &size, &fd);
if (!buf) {
fprintf(stderr, "Error, cannot open input file\n");
goto exit;
}
#if 0
if (0 != audio_hw_register_server_died_callback(device, (void*)audio_server_died_cb)) {
printf("[HAL_PLAY]ERROR:audio_server_died_cb fail\n");
} else {
printf("[HAL_PLAY]audio_server_died_cb success\n");
}
#endif
/* set audio config */
memset(&config, 0, sizeof(config));
switch (format) {
case FORMAT_PCM16:
case FORMAT_PCM32:
config.sample_rate = sr;
switch (ch) {
case 1:
config.channel_mask = AUDIO_CHANNEL_OUT_MONO;
break;
case 2:
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
break;
case 6:
config.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
break;
case 8:
config.channel_mask = AUDIO_CHANNEL_OUT_7POINT1;
break;
default:
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
break;
}
break;
case FORMAT_DD:
case FORMAT_MAT:
case FORMAT_IEC61937:
case FORMAT_AC4:
case FORMAT_DTS:
config.sample_rate = 48000;
config.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
break;
case FORMAT_MP3:
case FORMAT_AAC:
case FORMAT_OGG:
case FORMAT_FLAC:
config.sample_rate = sr;
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
break;
case FORMAT_TRUE_HD:
config.sample_rate = sr;
switch (ch) {
case 8:
config.channel_mask = AUDIO_CHANNEL_OUT_7POINT1;
break;
default:
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
break;
}
break;
/*coverity[deadcode]: Default processing is ok*/
default:
break;
}
config.format = format_tab[format];
signal(SIGINT, handler_halplay);
if (device_type > AUDIO_PORT_OUT_UNKNOWN && device_type < AUDIO_PORT_OUT_MAX) {
memset(&port_cfg, 0 , sizeof(struct audio_port_config));
port_cfg.id = 2;
port_cfg.role = AUDIO_PORT_ROLE_SINK;
port_cfg.type = AUDIO_PORT_TYPE_DEVICE;
port_cfg.config_mask = AUDIO_PORT_CONFIG_GAIN;
if (device_type == AUDIO_PORT_OUT_SPEAKER) {
port_cfg.ext.device.type = AUDIO_DEVICE_OUT_SPEAKER;
} else if (device_type == AUDIO_PORT_OUT_HEADPHONE) {
port_cfg.ext.device.type = AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
} else {
printf("invalid device!\n");
goto exit;
}
/* audio_hal use dB * 100 to keep the accuracy */
float dB = 0;
dB = ampToDb(volume);
port_cfg.gain.values[0] = dB * 100;
if (device && device->set_audio_port_config(
device, &port_cfg) == 0) {
ret = 0;
printf("set device %d, port %d, vol %f\n", device_type, port_cfg.ext.device.type, volume);
}
}
test_output_stream(device, buf, size, &config);
funmap(buf, size, fd);
exit:
audio_hw_unload_interface(device);
return 0;
}