| /* |
| * 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 <pthread.h> |
| #include <unistd.h> |
| #include <math.h> |
| |
| #include "if-main.h" |
| #include "../hal-utils.h" |
| |
| audio_hw_device_t *if_audio_sco = NULL; |
| static struct audio_stream_out *stream_out = NULL; |
| static struct audio_stream_in *stream_in = NULL; |
| |
| static size_t buffer_size = 0; |
| static size_t buffer_size_in = 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, "sco", &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 int feed_from_in(short *buffer, void *data) |
| { |
| return stream_in->read(stream_in, buffer, buffer_size_in); |
| } |
| |
| 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 mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples) |
| { |
| size_t i; |
| |
| for (i = 0; i < samples; i++) { |
| out[2 * i] = in[i]; |
| out[2 * i + 1] = in[i]; |
| } |
| } |
| |
| static void *playback_thread(void *data) |
| { |
| int (*filbuff_cb) (short*, void*); |
| short buffer[buffer_size / sizeof(short)]; |
| short buffer_in[buffer_size_in / 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) { |
| if (data == stream_in) |
| filbuff_cb = feed_from_in; |
| else { |
| 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); |
| |
| if (data && data == stream_in) { |
| int chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); |
| int chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); |
| |
| len = filbuff_cb(buffer_in, cb_data); |
| |
| if (chan_in == 1 && chan_out == 2) { |
| mono_to_stereo_pcm16(buffer_in, |
| buffer, |
| buffer_size_in / 2); |
| } |
| } else |
| 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 && data != stream_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 write_stereo_pcm16(const short *input, size_t len, FILE *out) |
| { |
| short sample[2]; |
| size_t i; |
| |
| for (i = 0; i < len / 2; i++) { |
| sample[0] = input[i]; |
| sample[1] = input[i]; |
| |
| fwrite(sample, sizeof(sample), 1, out); |
| } |
| } |
| |
| static void *read_thread(void *data) |
| { |
| int (*filbuff_cb) (short*, void*) = feed_from_in; |
| short buffer[buffer_size_in / sizeof(short)]; |
| ssize_t len = 0; |
| void *cb_data = NULL; |
| FILE *out = data; |
| |
| 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); |
| if (len < 0) { |
| haltest_error("Error receiving SCO data"); |
| break; |
| } |
| |
| haltest_info("Read %zd bytes\n", len); |
| |
| if (out) { |
| write_stereo_pcm16(buffer, len, out); |
| haltest_info("Written %zd bytes\n", len * 2); |
| } |
| } while (len); |
| |
| if (out) |
| fclose(out); |
| |
| pthread_mutex_lock(&state_mutex); |
| current_state = STATE_STOPPED; |
| pthread_mutex_unlock(&state_mutex); |
| |
| haltest_info("Done reading.\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 loop_p(int argc, const char **argv) |
| { |
| int chan_out, chan_in; |
| |
| RETURN_IF_NULL(if_audio_sco); |
| RETURN_IF_NULL(stream_out); |
| RETURN_IF_NULL(stream_in); |
| |
| chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); |
| chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); |
| |
| if (!buffer_size || !buffer_size_in) { |
| haltest_error("Invalid buffer sizes. Streams opened\n"); |
| return; |
| } |
| |
| if (buffer_size / chan_out != buffer_size_in / chan_in) { |
| haltest_error("read/write buffers differ, not supported\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, |
| stream_in) != 0) |
| haltest_error("Cannot create playback thread!\n"); |
| } |
| |
| static void read_p(int argc, const char **argv) |
| { |
| const char *fname = NULL; |
| FILE *out = NULL; |
| |
| RETURN_IF_NULL(if_audio_sco); |
| RETURN_IF_NULL(stream_in); |
| |
| 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 (argc < 3) { |
| haltest_error("Invalid audio file path.\n"); |
| haltest_info("Using read and through away\n"); |
| } else { |
| fname = argv[2]; |
| out = fopen(fname, "w"); |
| if (!out) { |
| haltest_error("Cannot open file: %s\n", fname); |
| return; |
| } |
| |
| haltest_info("Reading to file: %s\n", fname); |
| } |
| |
| if (!buffer_size_in) { |
| haltest_error("Invalid buffer size.\n"); |
| goto failed; |
| } |
| |
| if (pthread_create(&play_thread, NULL, read_thread, out) != 0) { |
| haltest_error("Cannot create playback thread!\n"); |
| goto failed; |
| } |
| |
| return; |
| failed: |
| if (out) |
| fclose(out); |
| } |
| |
| static void stop_p(int argc, const char **argv) |
| { |
| RETURN_IF_NULL(if_audio_sco); |
| RETURN_IF_NULL(play_thread); |
| |
| pthread_mutex_lock(&state_mutex); |
| if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { |
| pthread_mutex_unlock(&state_mutex); |
| return; |
| } |
| |
| if (stream_out) { |
| pthread_mutex_lock(&outstream_mutex); |
| stream_out->common.standby(&stream_out->common); |
| pthread_mutex_unlock(&outstream_mutex); |
| } |
| |
| current_state = STATE_STOPPING; |
| pthread_mutex_unlock(&state_mutex); |
| |
| pthread_join(play_thread, NULL); |
| play_thread = 0; |
| |
| haltest_info("Ended %s\n", __func__); |
| } |
| |
| static void open_output_stream_p(int argc, const char **argv) |
| { |
| struct audio_config *config; |
| 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); |
| |
| if (argc < 3) { |
| haltest_info("No sampling rate specified. Use default conf\n"); |
| config = NULL; |
| } else { |
| config = calloc(1, sizeof(struct audio_config)); |
| if (!config) |
| return; |
| |
| config->sample_rate = atoi(argv[2]); |
| config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; |
| config->format = AUDIO_FORMAT_PCM_16_BIT; |
| } |
| |
| #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) |
| err = if_audio_sco->open_output_stream(if_audio_sco, |
| 0, |
| AUDIO_DEVICE_OUT_ALL_SCO, |
| AUDIO_OUTPUT_FLAG_NONE, |
| config, |
| &stream_out, NULL); |
| #else |
| err = if_audio_sco->open_output_stream(if_audio_sco, |
| 0, |
| AUDIO_DEVICE_OUT_ALL_SCO, |
| AUDIO_OUTPUT_FLAG_NONE, |
| config, |
| &stream_out); |
| #endif |
| if (err < 0) { |
| haltest_error("open output stream returned %d\n", err); |
| goto failed; |
| } |
| |
| 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); |
| failed: |
| if (config) |
| free(config); |
| } |
| |
| static void close_output_stream_p(int argc, const char **argv) |
| { |
| RETURN_IF_NULL(if_audio_sco); |
| RETURN_IF_NULL(stream_out); |
| |
| if (play_thread) |
| stop_p(argc, argv); |
| |
| if_audio_sco->close_output_stream(if_audio_sco, stream_out); |
| |
| stream_out = NULL; |
| buffer_size = 0; |
| } |
| |
| static void open_input_stream_p(int argc, const char **argv) |
| { |
| struct audio_config *config; |
| 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); |
| |
| if (argc < 3) { |
| haltest_info("No sampling rate specified. Use default conf\n"); |
| config = NULL; |
| } else { |
| config = calloc(1, sizeof(struct audio_config)); |
| if (!config) |
| return; |
| |
| config->sample_rate = atoi(argv[2]); |
| config->channel_mask = AUDIO_CHANNEL_OUT_MONO; |
| config->format = AUDIO_FORMAT_PCM_16_BIT; |
| } |
| |
| #if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0) |
| err = if_audio_sco->open_input_stream(if_audio_sco, |
| 0, |
| AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, |
| config, |
| &stream_in, 0, NULL, 0); |
| #else |
| err = if_audio_sco->open_input_stream(if_audio_sco, |
| 0, |
| AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, |
| config, |
| &stream_in); |
| #endif |
| if (err < 0) { |
| haltest_error("open output stream returned %d\n", err); |
| goto failed; |
| } |
| |
| buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common); |
| if (buffer_size_in == 0) |
| haltest_error("Invalid buffer size received!\n"); |
| else |
| haltest_info("Using buffer size: %zu\n", buffer_size_in); |
| failed: |
| if (config) |
| free(config); |
| } |
| |
| static void close_input_stream_p(int argc, const char **argv) |
| { |
| RETURN_IF_NULL(if_audio_sco); |
| RETURN_IF_NULL(stream_in); |
| |
| if (play_thread) |
| stop_p(argc, argv); |
| |
| if_audio_sco->close_input_stream(if_audio_sco, stream_in); |
| |
| stream_in = NULL; |
| buffer_size_in = 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_METHODH(open_output_stream, "sample_rate"), |
| STD_METHOD(close_output_stream), |
| STD_METHODH(open_input_stream, "sampling rate"), |
| STD_METHOD(close_input_stream), |
| STD_METHODH(play, "<path to pcm file>"), |
| STD_METHOD(read), |
| STD_METHOD(loop), |
| 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, "<closing>"), |
| STD_METHODH(set_parameters, "<closing=value>"), |
| STD_METHODH(set_sample_rate, "<sample rate>"), |
| STD_METHOD(init_check), |
| END_METHOD |
| }; |
| |
| const struct interface sco_if = { |
| .name = "sco", |
| .methods = methods |
| }; |