| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- // SPDX-License-Identifier: Apache-2.0
- /*
- * Copyright (C) 2014 Intel Corporation
- *
- */
- #define _GNU_SOURCE
- #include <pthread.h>
- #include <unistd.h>
- #include <math.h>
- #include "if-main.h"
- #include "../hal-utils.h"
- audio_hw_device_t *if_audio = NULL;
- static struct audio_stream_out *stream_out = NULL;
- static size_t buffer_size = 0;
- static pthread_t play_thread = 0;
- static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
- static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
- enum state {
- STATE_STOPPED,
- STATE_STOPPING,
- STATE_PLAYING,
- STATE_SUSPENDED,
- STATE_MAX
- };
- SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
- DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
- DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
- DELEMENT(AUDIO_CHANNEL_OUT_MONO),
- DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
- DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
- #if ANDROID_VERSION < PLATFORM_VER(5, 0, 0)
- DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
- #else
- DELEMENT(AUDIO_CHANNEL_OUT_QUAD_BACK),
- DELEMENT(AUDIO_CHANNEL_OUT_QUAD_SIDE),
- DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_BACK),
- DELEMENT(AUDIO_CHANNEL_OUT_5POINT1_SIDE),
- #endif
- DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
- DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
- DELEMENT(AUDIO_CHANNEL_OUT_ALL),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
- ENDMAP
- SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
- DELEMENT(AUDIO_FORMAT_DEFAULT),
- DELEMENT(AUDIO_FORMAT_PCM),
- DELEMENT(AUDIO_FORMAT_MP3),
- DELEMENT(AUDIO_FORMAT_AMR_NB),
- DELEMENT(AUDIO_FORMAT_AMR_WB),
- DELEMENT(AUDIO_FORMAT_AAC),
- DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
- DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
- DELEMENT(AUDIO_FORMAT_VORBIS),
- DELEMENT(AUDIO_FORMAT_MAIN_MASK),
- DELEMENT(AUDIO_FORMAT_SUB_MASK),
- DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
- DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
- DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
- DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
- ENDMAP
- static int current_state = STATE_STOPPED;
- #define SAMPLERATE 44100
- static short sample[SAMPLERATE];
- static uint16_t sample_pos;
- static void init_p(int argc, const char **argv)
- {
- int err;
- const hw_module_t *module;
- audio_hw_device_t *device;
- err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
- AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
- if (err) {
- haltest_error("hw_get_module_by_class returned %d\n", err);
- return;
- }
- err = audio_hw_device_open(module, &device);
- if (err) {
- haltest_error("audio_hw_device_open returned %d\n", err);
- return;
- }
- if_audio = device;
- }
- static int feed_from_file(short *buffer, void *data)
- {
- FILE *in = data;
- return fread(buffer, buffer_size, 1, in);
- }
- static int feed_from_generator(short *buffer, void *data)
- {
- size_t i = 0;
- float volume = 0.5;
- float *freq = data;
- float f = 1;
- if (freq)
- f = *freq;
- /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
- for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
- if (sample_pos >= SAMPLERATE)
- sample_pos = sample_pos % SAMPLERATE;
- /* Use the same sample for both channels */
- buffer[i++] = sample[sample_pos] * volume;
- buffer[i++] = sample[sample_pos] * volume;
- sample_pos += f;
- }
- return buffer_size;
- }
- static void prepare_sample(void)
- {
- int x;
- double s;
- haltest_info("Preparing audio sample...\n");
- for (x = 0; x < SAMPLERATE; x++) {
- /* prepare sinusoidal 1Hz sample */
- s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
- s = sin(s);
- /* remap <-1, 1> to signed 16bit PCM range */
- sample[x] = s * 32767;
- }
- sample_pos = 0;
- }
- static void *playback_thread(void *data)
- {
- int (*filbuff_cb) (short*, void*);
- short buffer[buffer_size / sizeof(short)];
- size_t len = 0;
- ssize_t w_len = 0;
- FILE *in = data;
- void *cb_data = NULL;
- float freq = 440.0;
- /* Use file or fall back to generator */
- if (in) {
- filbuff_cb = feed_from_file;
- cb_data = in;
- } else {
- prepare_sample();
- filbuff_cb = feed_from_generator;
- cb_data = &freq;
- }
- pthread_mutex_lock(&state_mutex);
- current_state = STATE_PLAYING;
- pthread_mutex_unlock(&state_mutex);
- do {
- pthread_mutex_lock(&state_mutex);
- if (current_state == STATE_STOPPING) {
- pthread_mutex_unlock(&state_mutex);
- break;
- } else if (current_state == STATE_SUSPENDED) {
- pthread_mutex_unlock(&state_mutex);
- usleep(500);
- continue;
- }
- pthread_mutex_unlock(&state_mutex);
- len = filbuff_cb(buffer, cb_data);
- pthread_mutex_lock(&outstream_mutex);
- if (!stream_out) {
- pthread_mutex_unlock(&outstream_mutex);
- break;
- }
- w_len = stream_out->write(stream_out, buffer, buffer_size);
- pthread_mutex_unlock(&outstream_mutex);
- } while (len && w_len > 0);
- if (in)
- fclose(in);
- pthread_mutex_lock(&state_mutex);
- current_state = STATE_STOPPED;
- pthread_mutex_unlock(&state_mutex);
- haltest_info("Done playing.\n");
- return NULL;
- }
- static void play_p(int argc, const char **argv)
- {
- const char *fname = NULL;
- FILE *in = NULL;
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- if (argc < 3) {
- haltest_error("Invalid audio file path.\n");
- haltest_info("Using sound generator.\n");
- } else {
- fname = argv[2];
- in = fopen(fname, "r");
- if (in == NULL) {
- haltest_error("Cannot open file: %s\n", fname);
- return;
- }
- haltest_info("Playing file: %s\n", fname);
- }
- if (buffer_size == 0) {
- haltest_error("Invalid buffer size. Was stream_out opened?\n");
- goto fail;
- }
- pthread_mutex_lock(&state_mutex);
- if (current_state != STATE_STOPPED) {
- haltest_error("Already playing or stream suspended!\n");
- pthread_mutex_unlock(&state_mutex);
- goto fail;
- }
- pthread_mutex_unlock(&state_mutex);
- if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
- haltest_error("Cannot create playback thread!\n");
- goto fail;
- }
- return;
- fail:
- if (in)
- fclose(in);
- }
- static void stop_p(int argc, const char **argv)
- {
- pthread_mutex_lock(&state_mutex);
- if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
- pthread_mutex_unlock(&state_mutex);
- return;
- }
- current_state = STATE_STOPPING;
- pthread_mutex_unlock(&state_mutex);
- pthread_mutex_lock(&outstream_mutex);
- stream_out->common.standby(&stream_out->common);
- pthread_mutex_unlock(&outstream_mutex);
- }
- static void open_output_stream_p(int argc, const char **argv)
- {
- int err;
- RETURN_IF_NULL(if_audio);
- pthread_mutex_lock(&state_mutex);
- if (current_state == STATE_PLAYING) {
- haltest_error("Already playing!\n");
- pthread_mutex_unlock(&state_mutex);
- return;
- }
- pthread_mutex_unlock(&state_mutex);
- #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
- err = if_audio->open_output_stream(if_audio,
- 0,
- AUDIO_DEVICE_OUT_ALL_A2DP,
- AUDIO_OUTPUT_FLAG_NONE,
- NULL,
- &stream_out, NULL);
- #else
- err = if_audio->open_output_stream(if_audio,
- 0,
- AUDIO_DEVICE_OUT_ALL_A2DP,
- AUDIO_OUTPUT_FLAG_NONE,
- NULL,
- &stream_out);
- #endif
- if (err < 0) {
- haltest_error("open output stream returned %d\n", err);
- return;
- }
- buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
- if (buffer_size == 0)
- haltest_error("Invalid buffer size received!\n");
- else
- haltest_info("Using buffer size: %zu\n", buffer_size);
- }
- static void close_output_stream_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- stop_p(argc, argv);
- haltest_info("Waiting for playback thread...\n");
- pthread_join(play_thread, NULL);
- if_audio->close_output_stream(if_audio, stream_out);
- stream_out = NULL;
- buffer_size = 0;
- }
- static void cleanup_p(int argc, const char **argv)
- {
- int err;
- RETURN_IF_NULL(if_audio);
- pthread_mutex_lock(&state_mutex);
- if (current_state != STATE_STOPPED) {
- pthread_mutex_unlock(&state_mutex);
- close_output_stream_p(0, NULL);
- } else {
- pthread_mutex_unlock(&state_mutex);
- }
- err = audio_hw_device_close(if_audio);
- if (err < 0) {
- haltest_error("audio_hw_device_close returned %d\n", err);
- return;
- }
- if_audio = NULL;
- }
- static void suspend_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- pthread_mutex_lock(&state_mutex);
- if (current_state != STATE_PLAYING) {
- pthread_mutex_unlock(&state_mutex);
- return;
- }
- current_state = STATE_SUSPENDED;
- pthread_mutex_unlock(&state_mutex);
- pthread_mutex_lock(&outstream_mutex);
- stream_out->common.standby(&stream_out->common);
- pthread_mutex_unlock(&outstream_mutex);
- }
- static void resume_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- pthread_mutex_lock(&state_mutex);
- if (current_state == STATE_SUSPENDED)
- current_state = STATE_PLAYING;
- pthread_mutex_unlock(&state_mutex);
- }
- static void get_latency_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- haltest_info("Output audio stream latency: %d\n",
- stream_out->get_latency(stream_out));
- }
- static void get_buffer_size_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- haltest_info("Current output buffer size: %zu\n",
- stream_out->common.get_buffer_size(&stream_out->common));
- }
- static void get_channels_p(int argc, const char **argv)
- {
- audio_channel_mask_t channels;
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- channels = stream_out->common.get_channels(&stream_out->common);
- haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
- }
- static void get_format_p(int argc, const char **argv)
- {
- audio_format_t format;
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- format = stream_out->common.get_format(&stream_out->common);
- haltest_info("Format: %s\n", audio_format_t2str(format));
- }
- static void get_sample_rate_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- haltest_info("Current sample rate: %d\n",
- stream_out->common.get_sample_rate(&stream_out->common));
- }
- static void get_parameters_p(int argc, const char **argv)
- {
- const char *keystr;
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- if (argc < 3) {
- haltest_info("No keys given.\n");
- keystr = "";
- } else {
- keystr = argv[2];
- }
- haltest_info("Current parameters: %s\n",
- stream_out->common.get_parameters(&stream_out->common,
- keystr));
- }
- static void set_parameters_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- if (argc < 3) {
- haltest_error("No key=value; pairs given.\n");
- return;
- }
- stream_out->common.set_parameters(&stream_out->common, argv[2]);
- }
- static void set_sample_rate_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- RETURN_IF_NULL(stream_out);
- if (argc < 3)
- return;
- stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
- }
- static void init_check_p(int argc, const char **argv)
- {
- RETURN_IF_NULL(if_audio);
- haltest_info("Init check result: %d\n", if_audio->init_check(if_audio));
- }
- static struct method methods[] = {
- STD_METHOD(init),
- STD_METHOD(cleanup),
- STD_METHOD(open_output_stream),
- STD_METHOD(close_output_stream),
- STD_METHODH(play, "<path to pcm file>"),
- STD_METHOD(stop),
- STD_METHOD(suspend),
- STD_METHOD(resume),
- STD_METHOD(get_latency),
- STD_METHOD(get_buffer_size),
- STD_METHOD(get_channels),
- STD_METHOD(get_format),
- STD_METHOD(get_sample_rate),
- STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
- STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
- STD_METHODH(set_sample_rate, "<sample rate>"),
- STD_METHOD(init_check),
- END_METHOD
- };
- const struct interface audio_if = {
- .name = "audio",
- .methods = methods
- };
|