This patch set adds audio support for haltest tool. It provides basic audio HAL
functionality and adds ability to play PCM audio files.
I've also added sound generator when no file path is provided for "play"
command. We can leave that be or push sample audio file to the tree, and/or
hardcode the path. I think that leaving audio file selection to the user and
providing fallback to sound generator is the right solution. Please let me know
what is yours preference.
Regards.
Jakub Tyszkowski (15):
android/client-av: Fix checking for a2dp interface
android/client-audio: Add open/close output stream
android/client-audio: Add audio file playback
android/client-audio: Add playback suspend/resume
android/client-audio: Add get_latency for output
android/client-audio: Add get_buffer_size for output
android/client-audio: Add get_channels
android/client-audio: Add getting audio format
android/client-audio: Add getting sample rate
android/client-audio: Add get_parameters for output
android/client-audio: Add set_parameters
android/client-audio: Add setting sample rate
android/client-audio: Add init_check
android/client-audio: Add sine generator as fallback
android/client-audio: Fix getting audio module on Android
android/Makefile.am | 2 +-
android/client/if-audio.c | 477 +++++++++++++++++++++++++++++++++++++++++++++-
android/client/if-av.c | 4 +-
3 files changed, 479 insertions(+), 4 deletions(-)
--
1.8.5.2
Getting audio module results with -EADDRINUSE on socket binding when
running on Android. Therefore we need to stop mediaserver for
successfull connection.
---
android/client/if-audio.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 66f8364..71f9dad 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -99,6 +99,9 @@ static void init_p(int argc, const char **argv)
int err;
const hw_module_t *module;
audio_hw_device_t *device;
+ int timeout = 1500; /* in ms */
+ int t = 300;
+
err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
@@ -107,8 +110,27 @@ static void init_p(int argc, const char **argv)
return;
}
+init_audio:
err = audio_hw_device_open(module, &device);
if (err) {
+ if (err == -EADDRINUSE && t < timeout) {
+ int err2;
+
+ /* We need to Stop media server to bind to socket. */
+ haltest_info("Trying to stop media server...\n");
+
+ err2 = system("/system/bin/setprop ctl.stop media");
+ if (err2) {
+ haltest_error("audio_hw_device_open returned",
+ " %d\n", err);
+ return;
+ }
+
+ usleep(t);
+ t += t;
+ goto init_audio;
+ }
+
haltest_error("audio_hw_device_open returned %d\n", err);
return;
}
@@ -358,6 +380,9 @@ static void cleanup_p(int argc, const char **argv)
}
if_audio = NULL;
+
+ /* get the media server back up and running */
+ err = system("/system/bin/setprop ctl.start media");
}
static void suspend_p(int argc, const char **argv)
--
1.8.5.2
When no audio file path is provided use sine generator.
---
android/Makefile.am | 2 +-
android/client/if-audio.c | 58 ++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/android/Makefile.am b/android/Makefile.am
index e065c0c..322210c 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -99,7 +99,7 @@ android_haltest_SOURCES = android/client/haltest.c \
android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
-DPLUGINDIR=\""$(android_plugindir)"\"
-android_haltest_LDFLAGS = -pthread -ldl
+android_haltest_LDFLAGS = -pthread -ldl -lm
noinst_PROGRAMS += android/android-tester
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index b481461..66f8364 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -19,6 +19,7 @@
#include "../hal-utils.h"
#include "pthread.h"
#include "unistd.h"
+#include <math.h>
audio_hw_device_t *if_audio = NULL;
struct audio_stream_out *stream_out = NULL;
@@ -89,6 +90,10 @@ 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;
@@ -126,6 +131,50 @@ static int feed_from_file(short *buffer, void *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*);
@@ -134,6 +183,7 @@ static void *playback_thread(void *data)
size_t w_len = 0;
FILE *in = data;
void *cb_data = NULL;
+ float freq = 440.0;
pthread_cleanup_push(playthread_cleanup, NULL);
@@ -142,8 +192,9 @@ static void *playback_thread(void *data)
filbuff_cb = feed_from_file;
cb_data = in;
} else {
- /* TODO: Use generator */
- goto end;
+ prepare_sample();
+ filbuff_cb = feed_from_generator;
+ cb_data = &freq;
}
pthread_mutex_lock(&state_mutex);
@@ -178,7 +229,7 @@ static void *playback_thread(void *data)
fclose(in);
in = NULL;
}
-end:
+
pthread_cleanup_pop(1);
return NULL;
}
@@ -193,6 +244,7 @@ static void play_p(int argc, const char **argv)
if (argc < 3) {
haltest_error("Invalid audio file path.\n");
+ haltest_info("Using sound generator.\n");
} else {
fname = argv[2];
in = fopen(fname, "r");
--
1.8.5.2
Add calling init check for audio device.
---
android/client/if-audio.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 48b973b..b481461 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -431,6 +431,13 @@ static void set_sample_rate_p(int argc, const char **argv)
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),
@@ -448,6 +455,7 @@ static struct method methods[] = {
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
};
--
1.8.5.2
Add seting output stream parameters using key=value pairs string
(key1=value1;key2=value2).
---
android/client/if-audio.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 0746124..bbd6a8c 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -407,6 +407,19 @@ static void get_parameters_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -422,6 +435,7 @@ static struct method methods[] = {
STD_METHOD(get_format),
STD_METHOD(get_sample_rate),
STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
+ STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
END_METHOD
};
--
1.8.5.2
Add setting sample rate for output stream.
---
android/client/if-audio.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index bbd6a8c..48b973b 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -420,6 +420,17 @@ static void set_parameters_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -436,6 +447,7 @@ static struct method methods[] = {
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>"),
END_METHOD
};
--
1.8.5.2
Get current parameters for the audio stream.
---
android/client/if-audio.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 4a45cac..0746124 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -388,6 +388,25 @@ static void get_sample_rate_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -402,6 +421,7 @@ static struct method methods[] = {
STD_METHOD(get_channels),
STD_METHOD(get_format),
STD_METHOD(get_sample_rate),
+ STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
END_METHOD
};
--
1.8.5.2
Get current sample rate for output stream.
---
android/client/if-audio.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 45c00f3..4a45cac 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -379,6 +379,15 @@ static void get_format_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -392,6 +401,7 @@ static struct method methods[] = {
STD_METHOD(get_buffer_size),
STD_METHOD(get_channels),
STD_METHOD(get_format),
+ STD_METHOD(get_sample_rate),
END_METHOD
};
--
1.8.5.2
Get audio channels configuration.
---
android/client/if-audio.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 2b158a5..1c9baff 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -36,6 +36,39 @@ enum state {
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
+
static int current_state = STATE_STOPPED;
static void init_p(int argc, const char **argv)
@@ -304,6 +337,18 @@ static void get_buffer_size_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -315,6 +360,7 @@ static struct method methods[] = {
STD_METHOD(resume),
STD_METHOD(get_latency),
STD_METHOD(get_buffer_size),
+ STD_METHOD(get_channels),
END_METHOD
};
--
1.8.5.2
Add opening and closing output stream.
---
android/client/if-audio.c | 41 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 203e088..4c1efce 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -19,6 +19,9 @@
#include "../hal-utils.h"
audio_hw_device_t *if_audio = NULL;
+struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
static void init_p(int argc, const char **argv)
{
@@ -34,12 +37,46 @@ static void init_p(int argc, const char **argv)
}
err = audio_hw_device_open(module, &device);
- if (err)
+ if (err) {
haltest_error("audio_hw_device_open returned %d\n", err);
+ return;
+ }
if_audio = device;
}
+static void open_output_stream_p(int argc, const char **argv)
+{
+ int err;
+
+ RETURN_IF_NULL(if_audio);
+
+ err = if_audio->open_output_stream(if_audio,
+ 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: %d\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+ RETURN_IF_NULL(if_audio);
+ RETURN_IF_NULL(stream_out);
+
+ if_audio->close_output_stream(if_audio, stream_out);
+}
+
static void cleanup_p(int argc, const char **argv)
{
int err;
@@ -58,6 +95,8 @@ static void cleanup_p(int argc, const char **argv)
static struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
+ STD_METHOD(open_output_stream),
+ STD_METHOD(close_output_stream),
END_METHOD
};
--
1.8.5.2
This patch adds audio file playback capability. Proper 16-bit signed
float stereo PCM file is needed.
---
android/client/if-audio.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 163 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 4c1efce..cd0a851 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -17,11 +17,26 @@
#include "if-main.h"
#include "../hal-utils.h"
+#include "pthread.h"
+#include "unistd.h"
audio_hw_device_t *if_audio = NULL;
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
+};
+
+static int current_state = STATE_STOPPED;
static void init_p(int argc, const char **argv)
{
@@ -45,12 +60,142 @@ static void init_p(int argc, const char **argv)
if_audio = device;
}
+static void playthread_cleanup(void *arg)
+{
+ pthread_mutex_lock(&state_mutex);
+ current_state = STATE_STOPPED;
+ pthread_mutex_unlock(&state_mutex);
+
+ haltest_info("Done playing.\n");
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+ FILE *in = data;
+ return fread(buffer, buffer_size, 1, in);
+}
+
+static void *playback_thread(void *data)
+{
+ int (*filbuff_cb) (short*, void*);
+ short buffer[buffer_size / sizeof(short)];
+ size_t len = 0;
+ size_t w_len = 0;
+ FILE *in = data;
+ void *cb_data = NULL;
+
+ pthread_cleanup_push(playthread_cleanup, NULL);
+
+ /* Use file or fall back to generator */
+ if (in) {
+ filbuff_cb = feed_from_file;
+ cb_data = in;
+ } else {
+ /* TODO: Use generator */
+ goto end;
+ }
+
+ 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;
+ }
+ 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);
+
+ if (in) {
+ fclose(in);
+ in = NULL;
+ }
+end:
+ pthread_cleanup_pop(1);
+ 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");
+ } 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");
+ return;
+ }
+
+ pthread_mutex_lock(&state_mutex);
+ if (current_state != STATE_STOPPED) {
+ haltest_error("Already playing or stream suspended!\n");
+ pthread_mutex_unlock(&state_mutex);
+ return;
+ }
+ pthread_mutex_unlock(&state_mutex);
+
+ if (pthread_create(&play_thread, NULL, playback_thread, in) != 0)
+ haltest_error("Cannot create playback thread!\n");
+}
+
+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);
+
err = if_audio->open_output_stream(if_audio,
0,
AUDIO_DEVICE_OUT_ALL_A2DP,
@@ -74,7 +219,15 @@ 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)
@@ -83,6 +236,14 @@ static void cleanup_p(int argc, const char **argv)
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);
@@ -97,6 +258,8 @@ static struct method methods[] = {
STD_METHOD(cleanup),
STD_METHOD(open_output_stream),
STD_METHOD(close_output_stream),
+ STD_METHODH(play, "<path to pcm file>"),
+ STD_METHOD(stop),
END_METHOD
};
--
1.8.5.2
Get current audio format for output stream.
---
android/client/if-audio.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 1c9baff..45c00f3 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -69,6 +69,24 @@ SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
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;
static void init_p(int argc, const char **argv)
@@ -349,6 +367,18 @@ static void get_channels_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -361,6 +391,7 @@ static struct method methods[] = {
STD_METHOD(get_latency),
STD_METHOD(get_buffer_size),
STD_METHOD(get_channels),
+ STD_METHOD(get_format),
END_METHOD
};
--
1.8.5.2
Get output stream latency.
---
android/client/if-audio.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 985b60c..1ca84f5 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -286,6 +286,15 @@ static void resume_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -295,6 +304,7 @@ static struct method methods[] = {
STD_METHOD(stop),
STD_METHOD(suspend),
STD_METHOD(resume),
+ STD_METHOD(get_latency),
END_METHOD
};
--
1.8.5.2
Add audio stream suspending and resuming.
---
android/client/if-audio.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index cd0a851..985b60c 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -104,6 +104,10 @@ static void *playback_thread(void *data)
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);
@@ -253,6 +257,35 @@ static void cleanup_p(int argc, const char **argv)
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 struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -260,6 +293,8 @@ static struct method methods[] = {
STD_METHOD(close_output_stream),
STD_METHODH(play, "<path to pcm file>"),
STD_METHOD(stop),
+ STD_METHOD(suspend),
+ STD_METHOD(resume),
END_METHOD
};
--
1.8.5.2
Get buffer size for output stream.
---
android/client/if-audio.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
index 1ca84f5..2b158a5 100644
--- a/android/client/if-audio.c
+++ b/android/client/if-audio.c
@@ -295,6 +295,15 @@ static void get_latency_p(int argc, const char **argv)
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: %d\n",
+ stream_out->common.get_buffer_size(&stream_out->common));
+}
+
static struct method methods[] = {
STD_METHOD(init),
STD_METHOD(cleanup),
@@ -305,6 +314,7 @@ static struct method methods[] = {
STD_METHOD(suspend),
STD_METHOD(resume),
STD_METHOD(get_latency),
+ STD_METHOD(get_buffer_size),
END_METHOD
};
--
1.8.5.2
---
android/client/if-av.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/android/client/if-av.c b/android/client/if-av.c
index 0470e0d..8d1f69b 100644
--- a/android/client/if-av.c
+++ b/android/client/if-av.c
@@ -80,7 +80,7 @@ static void connect_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
- RETURN_IF_NULL(if_hh);
+ RETURN_IF_NULL(if_av);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_av->connect, &addr);
@@ -101,7 +101,7 @@ static void disconnect_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
- RETURN_IF_NULL(if_hh);
+ RETURN_IF_NULL(if_av);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_av->disconnect, &addr);
--
1.8.5.2
Hi Jakub,
On Thursday 30 of January 2014 12:23:06 Jakub Tyszkowski wrote:
> This patch set adds audio support for haltest tool. It provides basic audio HAL
> functionality and adds ability to play PCM audio files.
>
> I've also added sound generator when no file path is provided for "play"
> command. We can leave that be or push sample audio file to the tree, and/or
> hardcode the path. I think that leaving audio file selection to the user and
> providing fallback to sound generator is the right solution. Please let me know
> what is yours preference.
>
> Regards.
>
> Jakub Tyszkowski (15):
> android/client-av: Fix checking for a2dp interface
> android/client-audio: Add open/close output stream
> android/client-audio: Add audio file playback
> android/client-audio: Add playback suspend/resume
> android/client-audio: Add get_latency for output
> android/client-audio: Add get_buffer_size for output
> android/client-audio: Add get_channels
> android/client-audio: Add getting audio format
> android/client-audio: Add getting sample rate
> android/client-audio: Add get_parameters for output
> android/client-audio: Add set_parameters
> android/client-audio: Add setting sample rate
> android/client-audio: Add init_check
> android/client-audio: Add sine generator as fallback
> android/client-audio: Fix getting audio module on Android
>
> android/Makefile.am | 2 +-
> android/client/if-audio.c | 477 +++++++++++++++++++++++++++++++++++++++++++++-
> android/client/if-av.c | 4 +-
> 3 files changed, 479 insertions(+), 4 deletions(-)
Despite it being RFC it looks good for me so I've applied patches 1-14.
For 15 I think haltest should not try to stop media server as this might
have unexpected side effects on Android. If one wants to use haltest for
audio testing on Android he can disable media server by hand.
--
Best regards,
Szymon Janc