4 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Seungbae Shin <seungbae.shin@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
29 #include <mm_sound_pa_client.h>
33 #define MM_SOUND_CHANNEL_MIN 1
34 #define MM_SOUND_CHANNEL_MAX 6
36 typedef struct _mm_sound_handle_t {
46 int period; /* open api retrun value. */
52 uint32_t handle_count; /* use amotic operations */
57 pa_threaded_mainloop *mainloop;
59 } mm_sound_handle_mgr;
61 #define CHECK_HANDLE_RANGE(x) \
64 debug_msg("invalid handle(%d)", x); \
65 return MM_ERROR_INVALID_ARGUMENT; \
69 #define ATOMIC_INC(l, x) \
71 pthread_mutex_lock(l); \
75 pthread_mutex_unlock(l); \
78 // phandle(ret), GList, userdata, coimpare func
79 #define GET_HANDLE_DATA(p, l, u, func) \
82 list = g_list_find_custom(l, u, func); \
84 p = (mm_sound_handle_t*)list->data; \
89 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT 1536 /* frames */
90 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE 4
91 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT 6
92 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP 2
93 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK 8
94 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE 12
95 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO 10
97 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC 20
98 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC 25
99 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC 50
100 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC 75
101 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC 20
103 __attribute__ ((constructor))
104 void __mm_sound_pa_init(void)
106 memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
107 mm_sound_handle_mgr.state = FALSE;
109 mm_sound_handle_mgr.handles = g_list_alloc();
110 mm_sound_handle_mgr.handle_count = 1;
111 pthread_mutex_init(&mm_sound_handle_mgr.lock, NULL);
114 __attribute__ ((destructor))
115 void __mm_sound_pa_deinit(void)
117 g_list_free(mm_sound_handle_mgr.handles);
118 pthread_mutex_destroy(&mm_sound_handle_mgr.lock);
119 mm_sound_handle_mgr.handle_count = 0;
121 if (mm_sound_handle_mgr.state) {
122 debug_msg("mainloop(%p), context(%p)", mm_sound_handle_mgr.mainloop, mm_sound_handle_mgr.context);
123 pa_threaded_mainloop_stop(mm_sound_handle_mgr.mainloop);
124 pa_context_disconnect(mm_sound_handle_mgr.context);
125 pa_context_unref(mm_sound_handle_mgr.context);
126 pa_threaded_mainloop_free(mm_sound_handle_mgr.mainloop);
130 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
132 mm_sound_handle_t *phandle = (mm_sound_handle_t *) a;
133 int *handle = (int *)b;
138 if (phandle->handle == *handle)
145 int mm_sound_pa_open(MMSoundHandleMode mode, int volume_config, pa_sample_spec * ss, pa_channel_map * channel_map,
146 int *size, char *stream_type, int stream_index)
152 int prop_vol_type = 0;
153 int prop_gain_type = VOLUME_GAIN_DEFAULT;
155 int err = MM_ERROR_SOUND_INTERNAL;
156 int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
157 int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
159 int handle_mode = mode;
161 pa_proplist *proplist = NULL;
163 mm_sound_handle_t *handle = NULL;
165 if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
166 return MM_ERROR_INVALID_ARGUMENT;
168 proplist = pa_proplist_new();
170 if (channel_map == NULL) {
171 pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
175 switch (ss->format) {
177 sample_size = 1 * ss->channels;
179 case PA_SAMPLE_S16LE:
180 sample_size = 2 * ss->channels;
184 debug_error("Invalid sample size (%d)", sample_size);
188 if (stream_index != -1) {
189 char stream_index_s[11];
190 debug_msg("Set stream index [%d]", stream_index);
192 snprintf(stream_index_s, sizeof(stream_index_s) - 1, "%d", stream_index);
193 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
194 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
196 /* Set stream type */
197 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
199 memset(&attr, '\0', sizeof(attr));
201 switch (handle_mode) {
202 case HANDLE_MODE_OUTPUT:
203 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
204 samples_per_period = (ss->rate * period_time) / 1000;
207 attr.tlength = (uint32_t)((ss->rate / 10) * pa_frame_size(ss));
211 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr,
216 err = MM_ERROR_SOUND_INTERNAL;
221 debug_error("Open pulseaudio handle has failed - %s", pa_strerror(err));
222 if (!strncmp(pa_strerror(err), "Access denied by security check", strlen(pa_strerror(err))))
223 err = MM_ERROR_SOUND_PERMISSION_DENIED;
225 err = MM_ERROR_SOUND_INTERNAL;
229 handle = (mm_sound_handle_t *) malloc(sizeof(mm_sound_handle_t));
231 debug_error("Failed to alloc handle");
232 err = MM_ERROR_OUT_OF_MEMORY;
235 memset(handle, 0, sizeof(mm_sound_handle_t));
237 handle->volume_type = prop_vol_type;
238 handle->gain_type = prop_gain_type;
239 handle->rate = ss->rate;
240 handle->channels = ss->channels;
242 handle->period = samples_per_period * sample_size;
243 *size = handle->period;
244 handle->handle = mm_sound_handle_mgr.handle_count;
245 ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count); // 0 is not used
247 mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
249 if (handle->handle == 0) {
250 debug_msg("out of range. handle(%d)", handle->handle);
255 ("created handle[%d]. mode(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), s(%p), source_type(%d)",
256 handle->handle, handle->mode, handle->volume_type, handle->gain_type, handle->rate, handle->channels, ss->format,
257 handle->s, handle->source_type);
260 pa_proplist_free(proplist);
262 return handle->handle;
266 pa_proplist_free(proplist);
276 int mm_sound_pa_write(const int handle, void *buf, const int size)
278 mm_sound_handle_t *phandle = NULL;
279 int err = MM_ERROR_NONE;
282 return MM_ERROR_INVALID_ARGUMENT;
285 return MM_ERROR_INVALID_ARGUMENT;
287 return MM_ERROR_NONE;
289 CHECK_HANDLE_RANGE(handle);
290 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
292 if (phandle == NULL) {
293 debug_msg("phandle is null");
294 return MM_ERROR_SOUND_INTERNAL;
297 if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
298 debug_error("pa_simple_write() failed with %s", pa_strerror(err));
299 return MM_ERROR_SOUND_INTERNAL;
307 int mm_sound_pa_close(const int handle)
309 mm_sound_handle_t *phandle = NULL;
310 int err = MM_ERROR_NONE;
312 CHECK_HANDLE_RANGE(handle);
313 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
314 if (phandle == NULL) {
315 debug_msg("phandle is null");
316 return MM_ERROR_SOUND_INTERNAL;
319 debug_msg("phandle(%p) s(%p), handle(%d), rate(%d), ch(%d)",
320 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels);
322 switch (phandle->mode) {
323 case HANDLE_MODE_OUTPUT:
324 if (0 > pa_simple_flush(phandle->s, &err)) {
325 err = MM_ERROR_SOUND_INTERNAL;
326 debug_msg("pa_simple_flush() failed with %s", pa_strerror(err));
333 pa_simple_free(phandle->s);
336 debug_msg("leave: handle[%d]", handle);
338 mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
339 if (phandle != NULL) {
349 int mm_sound_pa_drain(const int handle)
351 mm_sound_handle_t *phandle = NULL;
352 int err = MM_ERROR_NONE;
354 CHECK_HANDLE_RANGE(handle);
355 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
357 return MM_ERROR_SOUND_INTERNAL;
359 if (0 > pa_simple_drain(phandle->s, &err)) {
360 debug_error("pa_simple_drain() failed with %s", pa_strerror(err));
361 err = MM_ERROR_SOUND_INTERNAL;