Return-Path: MIME-Version: 1.0 In-Reply-To: <1399452326-25009-5-git-send-email-Andrei.Emeltchenko.news@gmail.com> References: <1399452326-25009-1-git-send-email-Andrei.Emeltchenko.news@gmail.com> <1399452326-25009-5-git-send-email-Andrei.Emeltchenko.news@gmail.com> Date: Wed, 7 May 2014 13:58:03 +0300 Message-ID: Subject: Re: [PATCHv2 05/10] android/haltest: Add testinng for audio SCO HAL From: Luiz Augusto von Dentz To: Andrei Emeltchenko Cc: "linux-bluetooth@vger.kernel.org" Content-Type: text/plain; charset=UTF-8 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi Andrei, On Wed, May 7, 2014 at 11:45 AM, Andrei Emeltchenko wrote: > From: Andrei Emeltchenko > > Adds testing support for audio-sco HAL. It can probably be called sco alone dropping audio prefix. > --- > android/Android.mk | 1 + > android/Makefile.am | 1 + > android/client/haltest.c | 3 + > android/client/if-audio-sco.c | 520 ++++++++++++++++++++++++++++++++++++++++++ > android/client/if-main.h | 1 + > 5 files changed, 526 insertions(+) > create mode 100644 android/client/if-audio-sco.c > > diff --git a/android/Android.mk b/android/Android.mk > index 9226ff0..ba06145 100644 > --- a/android/Android.mk > +++ b/android/Android.mk > @@ -149,6 +149,7 @@ LOCAL_SRC_FILES := \ > bluez/android/client/history.c \ > bluez/android/client/tabcompletion.c \ > bluez/android/client/if-audio.c \ > + bluez/android/client/if-audio-sco.c \ > bluez/android/client/if-av.c \ > bluez/android/client/if-rc.c \ > bluez/android/client/if-bt.c \ > diff --git a/android/Makefile.am b/android/Makefile.am > index 856524f..3b91548 100644 > --- a/android/Makefile.am > +++ b/android/Makefile.am > @@ -109,6 +109,7 @@ android_haltest_SOURCES = android/client/haltest.c \ > android/client/if-hl.c \ > android/client/if-sock.c \ > android/client/if-audio.c \ > + android/client/if-audio-sco.c \ > android/hardware/hardware.c \ > android/hal-utils.h android/hal-utils.c > > diff --git a/android/client/haltest.c b/android/client/haltest.c > index 5d05b75..4a9d1db 100644 > --- a/android/client/haltest.c > +++ b/android/client/haltest.c > @@ -32,6 +32,7 @@ > > const struct interface *interfaces[] = { > &audio_if, > + &audio_sco_if, > &bluetooth_if, > &av_if, > &rc_if, > @@ -394,10 +395,12 @@ static void init(void) > const struct method *m; > const char *argv[4]; > char init_audio[] = "audio init"; > + char init_audio_sco[] = "audio-sco init"; > char init_bt[] = "bluetooth init"; > uint32_t i; > > process_line(init_audio); > + process_line(init_audio_sco); > process_line(init_bt); > > m = get_interface_method("bluetooth", "get_profile_interface"); > diff --git a/android/client/if-audio-sco.c b/android/client/if-audio-sco.c > new file mode 100644 > index 0000000..f5daaa0 > --- /dev/null > +++ b/android/client/if-audio-sco.c > @@ -0,0 +1,520 @@ > +/* > + * Copyright (C) 2014 Intel 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 "if-main.h" > +#include "../hal-utils.h" > +#include "pthread.h" > +#include "unistd.h" > +#include > + > +audio_hw_device_t *if_audio_sco = 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), > + DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), > + 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, "hsp", &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_sco = 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) { > + haltest_info("Detected stopping\n"); > + 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_sco); > + 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); > + > + haltest_info("Ended %s\n", __func__); > +} > + > +static void open_output_stream_p(int argc, const char **argv) > +{ > + int err; > + > + RETURN_IF_NULL(if_audio_sco); > + > + 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); > + > + err = if_audio_sco->open_output_stream(if_audio_sco, > + 0, > + AUDIO_DEVICE_OUT_ALL_A2DP, > + AUDIO_OUTPUT_FLAG_NONE, > + NULL, > + &stream_out); > + 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_sco); > + RETURN_IF_NULL(stream_out); > + > + stop_p(argc, argv); > + > + haltest_info("Waiting for playback thread...\n"); > + pthread_join(play_thread, NULL); > + > + if_audio_sco->close_output_stream(if_audio_sco, stream_out); > + > + stream_out = NULL; > + buffer_size = 0; > +} > + > +static void cleanup_p(int argc, const char **argv) > +{ > + int err; > + > + RETURN_IF_NULL(if_audio_sco); > + > + 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_sco); > + if (err < 0) { > + haltest_error("audio_hw_device_close returned %d\n", err); > + return; > + } > + > + if_audio_sco = NULL; > +} > + > +static void suspend_p(int argc, const char **argv) > +{ > + RETURN_IF_NULL(if_audio_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + 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_sco); > + > + haltest_info("Init check result: %d\n", if_audio_sco->init_check(if_audio_sco)); > +} > + > +static struct method methods[] = { > + STD_METHOD(init), > + STD_METHOD(cleanup), > + STD_METHOD(open_output_stream), > + STD_METHOD(close_output_stream), > + STD_METHODH(play, ""), > + 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, ""), > + STD_METHODH(set_parameters, ""), Those A2dp parameter should not apply here so we probably should remove these methods. > + STD_METHODH(set_sample_rate, ""), > + STD_METHOD(init_check), > + END_METHOD > +}; > + > +const struct interface audio_sco_if = { > + .name = "audio-sco", > + .methods = methods > +}; > diff --git a/android/client/if-main.h b/android/client/if-main.h > index ff6006c..b77710c 100644 > --- a/android/client/if-main.h > +++ b/android/client/if-main.h > @@ -68,6 +68,7 @@ struct interface { > }; > > extern const struct interface audio_if; > +extern const struct interface audio_sco_if; > extern const struct interface bluetooth_if; > extern const struct interface av_if; > extern const struct interface rc_if; > -- > 1.8.3.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Luiz Augusto von Dentz