/* * 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 #include #include #include #include #include #include #include "audio_if.h" #include #include #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 -c -r -d -p -v \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 -c -r -d -p -v \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; }