|
@@ -0,0 +1,651 @@
|
|
|
+#include"libaudiomgr.h"
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+
|
|
|
+static uint32_t latency_ms = 15; // requested initial latency in milisec: 0 use max
|
|
|
+static pa_usec_t latency = 0; //real latency in usec (for timestamping)
|
|
|
+
|
|
|
+static int sink_index = 0;
|
|
|
+static int source_index = 0;
|
|
|
+
|
|
|
+AudioMgrImpl::AudioMgrImpl(IAudioMgrCallback* pCallback)
|
|
|
+{
|
|
|
+ m_audio_context = NULL;
|
|
|
+ m_callback = pCallback;
|
|
|
+}
|
|
|
+
|
|
|
+AudioMgrImpl::~AudioMgrImpl()
|
|
|
+{
|
|
|
+ if (NULL != m_audio_context){
|
|
|
+ if (NULL != m_audio_context->list_input_devices) {
|
|
|
+ free(m_audio_context->list_input_devices);
|
|
|
+ m_audio_context->list_input_devices = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NULL != m_audio_context->list_output_devices) {
|
|
|
+ free(m_audio_context->list_output_devices);
|
|
|
+ m_audio_context->list_output_devices = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NULL != m_audio_context->capture_buff) {
|
|
|
+ free(m_audio_context->capture_buff);
|
|
|
+ m_audio_context->capture_buff = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ free(m_audio_context);
|
|
|
+ m_audio_context = NULL;
|
|
|
+ }
|
|
|
+ m_callback = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+int AudioMgrImpl::audio_mgr_destroy()
|
|
|
+{
|
|
|
+ delete this;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * init pulseaudio api
|
|
|
+ * args:
|
|
|
+ * audio_ctx - pointer to audio context data
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: error code (0 = E_OK)
|
|
|
+ */
|
|
|
+int AudioMgrImpl::audio_init_pulseaudio()
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(NULL != m_audio_context);
|
|
|
+
|
|
|
+ if (pa_get_devicelist() < 0)
|
|
|
+ {
|
|
|
+ m_callback->debug( "pulse audio failed to get audio device list from pulse server\n");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * pa_mainloop will call this function when it's ready to tell us
|
|
|
+ * about a source (input).
|
|
|
+ * Since we're not threading when listing devices,
|
|
|
+ * there's no need for mutexes on the devicelist structure
|
|
|
+ * args:
|
|
|
+ * c - pointer to pulse context
|
|
|
+ * l - pointer to source info
|
|
|
+ * eol - end of list
|
|
|
+ * data - pointer to user data (audio context)
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * none
|
|
|
+ *
|
|
|
+ * returns: none
|
|
|
+ */
|
|
|
+static void pa_sourcelist_cb(pa_context* c, const pa_source_info* l, int eol, void* data)
|
|
|
+{
|
|
|
+ rvc_audio_context_t* audio_ctx = (rvc_audio_context_t*)data;
|
|
|
+
|
|
|
+ int channels = 1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If eol is set to a positive number,
|
|
|
+ * you're at the end of the list
|
|
|
+ */
|
|
|
+ if (eol > 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ source_index++;
|
|
|
+
|
|
|
+ if (l->sample_spec.channels < 1)
|
|
|
+ channels = 1;
|
|
|
+ else
|
|
|
+ channels = l->sample_spec.channels;
|
|
|
+
|
|
|
+ double my_latency = 0.0;
|
|
|
+ int verbosity = 1;
|
|
|
+
|
|
|
+ if (verbosity > 0)
|
|
|
+ {
|
|
|
+ printf("AUDIO: =======[ Input Device #%d ]=======\n", source_index);
|
|
|
+ printf(" Description: %s\n", l->description);
|
|
|
+ printf(" Name: %s\n", l->name);
|
|
|
+ printf(" Index: %d\n", l->index);
|
|
|
+ printf(" Channels: %d (default to: %d)\n", l->sample_spec.channels, channels);
|
|
|
+ printf(" SampleRate: %d\n", l->sample_spec.rate);
|
|
|
+ printf(" Latency: %llu (usec)\n", (long long unsigned) l->latency);
|
|
|
+ printf(" Configured Latency: %llu (usec)\n", (long long unsigned) l->configured_latency);
|
|
|
+ printf(" Card: %d\n", l->card);
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (my_latency <= 0.0)
|
|
|
+ my_latency = (double)latency_ms / 1000;
|
|
|
+
|
|
|
+ if (l->monitor_of_sink == PA_INVALID_INDEX)
|
|
|
+ {
|
|
|
+ audio_ctx->num_input_dev++;
|
|
|
+ /*add device to list*/
|
|
|
+ audio_ctx->list_input_devices = (rvc_audio_device_t*)realloc(audio_ctx->list_input_devices, audio_ctx->num_input_dev * sizeof(rvc_audio_device_t));
|
|
|
+ if (audio_ctx->list_input_devices == NULL)
|
|
|
+ {
|
|
|
+ printf("AUDIO: FATAL memory allocation failure (pa_sourcelist_cb): %s\n", strerror(errno));
|
|
|
+ exit(-1);
|
|
|
+ }
|
|
|
+ /*fill device data*/
|
|
|
+ audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].id = l->index; /*saves dev id*/
|
|
|
+ strncpy(audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].name, l->name, MAX_PATH_EX-1);
|
|
|
+ strncpy(audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].description, l->description, MAX_PATH-1);
|
|
|
+ audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].channels = channels;
|
|
|
+ audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].samprate = l->sample_spec.rate;
|
|
|
+ audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].low_latency = my_latency; /*in seconds*/
|
|
|
+ audio_ctx->list_input_devices[audio_ctx->num_input_dev - 1].high_latency = my_latency; /*in seconds*/
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * pa_mainloop will call this function when it's ready to tell us
|
|
|
+ * about a source (input).
|
|
|
+ * This callback is pretty much identical to the previous
|
|
|
+ * but it will only print the output devices
|
|
|
+ * args:
|
|
|
+ * c - pointer to pulse context
|
|
|
+ * l - pointer to sink info
|
|
|
+ * eol - end of list
|
|
|
+ * data - pointer to user data (audio context)
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * none
|
|
|
+ *
|
|
|
+ * returns: none
|
|
|
+ */
|
|
|
+static void pa_sinklist_cb(pa_context* c, const pa_sink_info* l, int eol, void* userdata)
|
|
|
+{
|
|
|
+ rvc_audio_context_t* audio_ctx = (rvc_audio_context_t*)userdata;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If eol is set to a positive number,
|
|
|
+ * you're at the end of the list
|
|
|
+ */
|
|
|
+ if (eol > 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ sink_index++;
|
|
|
+
|
|
|
+ double my_latency = 0.0;
|
|
|
+ int verbosity = 1;
|
|
|
+
|
|
|
+ if (verbosity > 0)
|
|
|
+ {
|
|
|
+ printf("AUDIO: =======[ Output Device #%d ]=======\n", sink_index);
|
|
|
+ printf(" Description: %s\n", l->description);
|
|
|
+ printf(" Name: %s\n", l->name);
|
|
|
+ printf(" Index: %d\n", l->index);
|
|
|
+ printf(" Channels: %d\n", l->channel_map.channels);
|
|
|
+ printf(" SampleRate: %d\n", l->sample_spec.rate);
|
|
|
+ printf(" Latency: %llu (usec)\n", (long long unsigned) l->latency);
|
|
|
+ printf(" Configured Latency: %llu (usec)\n", (long long unsigned) l->configured_latency);
|
|
|
+ printf(" Card: %d\n", l->card);
|
|
|
+ printf("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (my_latency <= 0.0)
|
|
|
+ my_latency = (double)latency_ms / 1000;
|
|
|
+
|
|
|
+ audio_ctx->num_output_dev++;
|
|
|
+ /*add device to list*/
|
|
|
+ audio_ctx->list_output_devices = (rvc_audio_device_t*)realloc(audio_ctx->list_output_devices, audio_ctx->num_output_dev * sizeof(rvc_audio_device_t));
|
|
|
+ if (audio_ctx->list_output_devices == NULL)
|
|
|
+ {
|
|
|
+ printf("AUDIO: FATAL memory allocation failure (pa_sinklist_cb): %s\n", strerror(errno));
|
|
|
+ exit(-1);
|
|
|
+ }
|
|
|
+ /*fill device data*/
|
|
|
+ audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].id = l->index; /*saves dev id*/
|
|
|
+ strncpy(audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].name, l->name, MAX_PATH_EX-1);
|
|
|
+ strncpy(audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].description, l->description, MAX_PATH-1);
|
|
|
+ audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].channels = l->channel_map.channels;
|
|
|
+ audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].samprate = l->sample_spec.rate;
|
|
|
+ audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].low_latency = my_latency; /*in seconds*/
|
|
|
+ audio_ctx->list_output_devices[audio_ctx->num_output_dev - 1].high_latency = my_latency; /*in seconds*/
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * This callback gets called when our context changes state.
|
|
|
+ * We really only care about when it's ready or if it has failed
|
|
|
+ * args:
|
|
|
+ * c -pointer to pulse context
|
|
|
+ * data - pointer to user data
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * none
|
|
|
+ *
|
|
|
+ * retuns: none
|
|
|
+ */
|
|
|
+static void pa_state_cb(pa_context* c, void* data)
|
|
|
+{
|
|
|
+ pa_context_state_t state;
|
|
|
+ int* pa_ready = (int*)data;
|
|
|
+ state = pa_context_get_state(c);
|
|
|
+ switch (state)
|
|
|
+ {
|
|
|
+ // These are just here for reference
|
|
|
+ case PA_CONTEXT_UNCONNECTED:
|
|
|
+ case PA_CONTEXT_CONNECTING:
|
|
|
+ case PA_CONTEXT_AUTHORIZING:
|
|
|
+ case PA_CONTEXT_SETTING_NAME:
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ case PA_CONTEXT_FAILED:
|
|
|
+ case PA_CONTEXT_TERMINATED:
|
|
|
+ *pa_ready = 2;
|
|
|
+ break;
|
|
|
+ case PA_CONTEXT_READY:
|
|
|
+ *pa_ready = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * clean up and disconnect
|
|
|
+ * args:
|
|
|
+ * pa_ctx - pointer to pulse context
|
|
|
+ * pa_ml - pointer to pulse mainloop
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * none
|
|
|
+ *
|
|
|
+ * returns:
|
|
|
+ * none
|
|
|
+ */
|
|
|
+static void finish(pa_context* pa_ctx, pa_mainloop* pa_ml)
|
|
|
+{
|
|
|
+ /* clean up and disconnect */
|
|
|
+ pa_context_disconnect(pa_ctx);
|
|
|
+ pa_context_unref(pa_ctx);
|
|
|
+ pa_mainloop_free(pa_ml);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * iterate the main loop until all devices are listed
|
|
|
+ * args:
|
|
|
+ * audio_ctx - pointer to audio context
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: error code
|
|
|
+ */
|
|
|
+int AudioMgrImpl::pa_get_devicelist()
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+
|
|
|
+ /* Define our pulse audio loop and connection variables */
|
|
|
+ pa_mainloop* pa_ml;
|
|
|
+ pa_mainloop_api* pa_mlapi;
|
|
|
+ pa_operation* pa_op = NULL;
|
|
|
+ pa_context* pa_ctx;
|
|
|
+
|
|
|
+ /* We'll need these state variables to keep track of our requests */
|
|
|
+ int state = 0;
|
|
|
+ int pa_ready = 0;
|
|
|
+
|
|
|
+ /* Create a mainloop API and connection to the default server */
|
|
|
+ pa_ml = pa_mainloop_new();
|
|
|
+ pa_mlapi = pa_mainloop_get_api(pa_ml);
|
|
|
+ pa_ctx = pa_context_new(pa_mlapi, "getDevices");
|
|
|
+
|
|
|
+ /* This function connects to the pulse server */
|
|
|
+ if (pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
|
|
|
+ {
|
|
|
+ m_callback->debug("AUDIO: PULSE - unable to connect to server: pa_context_connect failed");
|
|
|
+ finish(pa_ctx, pa_ml);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This function defines a callback so the server will tell us
|
|
|
+ * it's state.
|
|
|
+ * Our callback will wait for the state to be ready.
|
|
|
+ * The callback will modify the variable to 1 so we know when we
|
|
|
+ * have a connection and it's ready.
|
|
|
+ * If there's an error, the callback will set pa_ready to 2
|
|
|
+ */
|
|
|
+ pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now we'll enter into an infinite loop until we get the data
|
|
|
+ * we receive or if there's an error
|
|
|
+ */
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ * We can't do anything until PA is ready,
|
|
|
+ * so just iterate the mainloop and continue
|
|
|
+ */
|
|
|
+ if (pa_ready == 0)
|
|
|
+ {
|
|
|
+ pa_mainloop_iterate(pa_ml, 1, NULL);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ /* We couldn't get a connection to the server, so exit out */
|
|
|
+ if (pa_ready == 2)
|
|
|
+ {
|
|
|
+ finish(pa_ctx, pa_ml);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * At this point, we're connected to the server and ready
|
|
|
+ * to make requests
|
|
|
+ */
|
|
|
+ switch (state)
|
|
|
+ {
|
|
|
+ /* State 0: we haven't done anything yet */
|
|
|
+ case 0:
|
|
|
+ /*
|
|
|
+ * This sends an operation to the server.
|
|
|
+ * pa_sinklist_cb is our callback function and a pointer
|
|
|
+ * o our devicelist will be passed to the callback
|
|
|
+ * (audio_ctx) The operation ID is stored in the
|
|
|
+ * pa_op variable
|
|
|
+ */
|
|
|
+ pa_op = pa_context_get_sink_info_list(
|
|
|
+ pa_ctx,
|
|
|
+ pa_sinklist_cb,
|
|
|
+ (void*)m_audio_context);
|
|
|
+
|
|
|
+ /* Update state for next iteration through the loop */
|
|
|
+ state++;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ /*
|
|
|
+ * Now we wait for our operation to complete.
|
|
|
+ * When it's complete our pa_output_devicelist is
|
|
|
+ * filled out, and we move along to the next state
|
|
|
+ */
|
|
|
+ if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE)
|
|
|
+ {
|
|
|
+ pa_operation_unref(pa_op);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Now we perform another operation to get the
|
|
|
+ * source(input device) list just like before.
|
|
|
+ * This time we pass a pointer to our input structure
|
|
|
+ */
|
|
|
+ pa_op = pa_context_get_source_info_list(
|
|
|
+ pa_ctx,
|
|
|
+ pa_sourcelist_cb,
|
|
|
+ (void*)m_audio_context);
|
|
|
+ /* Update the state so we know what to do next */
|
|
|
+ state++;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE)
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ * Now we're done,
|
|
|
+ * clean up and disconnect and return
|
|
|
+ */
|
|
|
+ pa_operation_unref(pa_op);
|
|
|
+ finish(pa_ctx, pa_ml);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ /* We should never see this state */
|
|
|
+ printf("AUDIO: Pulseaudio in state %d\n", state);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * Iterate the main loop and go again. The second argument is whether
|
|
|
+ * or not the iteration should block until something is ready to be
|
|
|
+ * done. Set it to zero for non-blocking.
|
|
|
+ */
|
|
|
+ pa_mainloop_iterate(pa_ml, 1, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * get the number of available audio devices
|
|
|
+ *
|
|
|
+ * args:
|
|
|
+ * binput - is input device
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: number of listed audio devices
|
|
|
+ */
|
|
|
+int AudioMgrImpl::audio_get_device_count(bool binput)
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+ if (binput){
|
|
|
+ return m_audio_context->num_input_dev;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return m_audio_context->num_output_dev;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * get the audio device referenced by index
|
|
|
+ * args:
|
|
|
+ * index - index of audio device
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: audio device referenced by index
|
|
|
+ */
|
|
|
+rvc_audio_device_t* AudioMgrImpl::audio_get_input_device(int index)
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+
|
|
|
+ if (index >= m_audio_context->num_input_dev){
|
|
|
+ printf("AUDIO: (audio_get_input_device) bad index %i using %i\n",index, m_audio_context->num_input_dev - 1);
|
|
|
+ index = m_audio_context->num_input_dev - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (index < 0){
|
|
|
+ printf("AUDIO: (audio_get_input_device) bad index %i using 0\n", index);
|
|
|
+ index = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return &m_audio_context->list_input_devices[index];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * get the output audio device referenced by index
|
|
|
+ * args:
|
|
|
+ * index - index of output audio device
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: output audio device referenced by index
|
|
|
+ */
|
|
|
+rvc_audio_device_t* AudioMgrImpl::audio_get_output_device( int index)
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+
|
|
|
+ if (index >= m_audio_context->num_output_dev)
|
|
|
+ {
|
|
|
+ printf("AUDIO: (audio_get_output_device) bad index %i using %i\n",
|
|
|
+ index, m_audio_context->num_output_dev - 1);
|
|
|
+ index = m_audio_context->num_output_dev - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (index < 0)
|
|
|
+ {
|
|
|
+ printf("AUDIO: (audio_get_output_device) bad index %i using 0\n", index);
|
|
|
+ index = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return &m_audio_context->list_output_devices[index];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int AudioMgrImpl::audio_get_device_name(char* pstrbuf, size_t ulen, bool binput, int index)
|
|
|
+{
|
|
|
+ int iret = -1;
|
|
|
+
|
|
|
+ rvc_audio_device_t* audio_device = NULL;
|
|
|
+ if (binput){
|
|
|
+ audio_device = audio_get_input_device(index);
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ audio_device = audio_get_output_device(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ size_t unamelen = strlen(audio_device->description);
|
|
|
+ if (ulen > unamelen){
|
|
|
+ snprintf(pstrbuf, ulen, "%s", audio_device->description);
|
|
|
+ iret = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return iret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int AudioMgrImpl::audio_mgr_initialize()
|
|
|
+{
|
|
|
+ int iret = -1;
|
|
|
+ m_audio_context = (rvc_audio_context_t*)calloc(1, sizeof(rvc_audio_context_t));
|
|
|
+
|
|
|
+ if (NULL == m_audio_context){
|
|
|
+ m_callback->debug("%s:%d couldn't allocate audio context.", __FUNCTION__, __LINE__);
|
|
|
+ return iret;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*initialize the mutex*/
|
|
|
+ __INIT_MUTEX(&(m_audio_context->mutex));
|
|
|
+
|
|
|
+ iret = audio_init_pulseaudio();
|
|
|
+
|
|
|
+ /*force a valid number of channels*/
|
|
|
+ if (m_audio_context->channels > 2) {
|
|
|
+ m_audio_context->channels = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ return iret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * close and clean audio context for pulse audio api
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * none
|
|
|
+ *
|
|
|
+ * returns: none
|
|
|
+ */
|
|
|
+int AudioMgrImpl::audio_close_pulseaudio()
|
|
|
+{
|
|
|
+ int iret = -1;
|
|
|
+ if (m_audio_context == NULL){
|
|
|
+ return iret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_audio_context->stream_flag == AUDIO_STRM_ON) {
|
|
|
+ stop_audio_capture();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NULL != m_audio_context->list_input_devices) {
|
|
|
+ free(m_audio_context->list_input_devices);
|
|
|
+ m_audio_context->list_input_devices = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NULL != m_audio_context->list_output_devices){
|
|
|
+ free(m_audio_context->list_output_devices);
|
|
|
+ m_audio_context->list_output_devices = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (NULL != m_audio_context->capture_buff) {
|
|
|
+ free(m_audio_context->capture_buff);
|
|
|
+ m_audio_context->capture_buff = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ free(m_audio_context);
|
|
|
+ m_audio_context = NULL;
|
|
|
+
|
|
|
+ iret = 0;
|
|
|
+
|
|
|
+ return iret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * stop and join the main loop iteration thread
|
|
|
+ *
|
|
|
+ * asserts:
|
|
|
+ * audio_ctx is not null
|
|
|
+ *
|
|
|
+ * returns: error code
|
|
|
+ */
|
|
|
+int AudioMgrImpl::stop_audio_capture()
|
|
|
+{
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+
|
|
|
+ m_audio_context->stream_flag = AUDIO_STRM_OFF;
|
|
|
+
|
|
|
+ if (0 == __THREAD_JOIN(m_readthread)){
|
|
|
+ m_callback->debug("read thread join success!");
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ m_callback->debug("read thread join failed!");
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int AudioMgrImpl::audio_mgr_terminate()
|
|
|
+{
|
|
|
+ int iret = -1;
|
|
|
+ /*assertions*/
|
|
|
+ assert(m_audio_context != NULL);
|
|
|
+
|
|
|
+ /* thread must be joined before destroying the mutex
|
|
|
+ * so no need to unlock before destroying it
|
|
|
+ */
|
|
|
+
|
|
|
+ /*destroy the mutex*/
|
|
|
+ __CLOSE_MUTEX(&(m_audio_context->mutex));
|
|
|
+
|
|
|
+ iret = audio_close_pulseaudio();
|
|
|
+
|
|
|
+ //if (audio_buffers != NULL)
|
|
|
+ // audio_free_buffers();
|
|
|
+
|
|
|
+ return iret;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int AudioMgrImpl::set_audio_capture_params()
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int AudioMgrImpl::start_audio_capture()
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|