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.*/
48 unsigned int stream_idx;
53 uint32_t handle_count; /* use amotic operations */
58 pa_threaded_mainloop *mainloop;
60 } mm_sound_handle_mgr;
62 #define CHECK_HANDLE_RANGE(x) \
65 debug_msg("invalid handle(%d)", x); \
66 return MM_ERROR_INVALID_ARGUMENT; \
70 #define ATOMIC_INC(l, x) \
72 pthread_mutex_lock(l); \
76 pthread_mutex_unlock(l); \
79 // phandle(ret), GList, userdata, coimpare func
80 #define GET_HANDLE_DATA(p, l, u, func) \
83 list = g_list_find_custom(l, u, func); \
85 p = (mm_sound_handle_t*)list->data; \
91 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT 1536 /* frames */
92 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE 4
93 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT 6
94 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP 2
95 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK 8
96 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE 12
97 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO 10
99 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC 20
100 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC 25
101 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC 50
102 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC 75
103 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC 20
105 __attribute__ ((constructor)) void __mm_sound_pa_init(void)
107 memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
108 mm_sound_handle_mgr.state = FALSE;
110 mm_sound_handle_mgr.handles = g_list_alloc();
111 mm_sound_handle_mgr.handle_count = 1;
112 pthread_mutex_init(&mm_sound_handle_mgr.lock, NULL);
115 __attribute__ ((destructor)) 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(%x), context(%x)", 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, int* size, char *stream_type, int stream_index)
151 int prop_vol_type = 0;
152 int prop_gain_type = VOLUME_GAIN_DEFAULT;
154 int err = MM_ERROR_SOUND_INTERNAL;
155 int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
156 int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
157 int periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_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);
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 /* Set volume type of stream */
189 if(volume_config > 0) {
190 debug_log("setting gain type");
191 prop_vol_type = 0; /* not used, set it system(0) temporarily */
193 /* Set gain type of stream */
194 prop_gain_type = (volume_config >> 8) & 0x000000FF;
196 pa_proplist_setf(proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", prop_gain_type);
199 if (stream_index != -1) {
200 char stream_index_s[11];
201 debug_msg("Set stream index [%d]", stream_index);
203 snprintf(stream_index_s, sizeof(stream_index_s)-1, "%d", stream_index);
204 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
205 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
207 /* Set stream type */
208 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
210 memset(&attr, '\0', sizeof(attr));
212 switch (handle_mode) {
213 case HANDLE_MODE_INPUT:
214 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
215 samples_per_period = (ss->rate * period_time) / 1000;
216 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
221 attr.fragsize = samples_per_period * pa_sample_size(ss);
223 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr, proplist, &err);
226 case HANDLE_MODE_INPUT_LOW_LATENCY:
227 period_time = PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC;
228 samples_per_period = (ss->rate * period_time) / 1000;
229 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
234 attr.fragsize = samples_per_period * pa_sample_size(ss);
236 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
239 case HANDLE_MODE_INPUT_HIGH_LATENCY:
240 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
241 samples_per_period = (ss->rate * period_time) / 1000;
242 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE;
247 attr.fragsize = samples_per_period * pa_sample_size(ss);
249 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
252 case HANDLE_MODE_OUTPUT:
253 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
254 samples_per_period = (ss->rate * period_time) / 1000;
255 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
258 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
262 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr, proplist, &err);
265 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
266 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
267 samples_per_period = (ss->rate * period_time) / 1000;
268 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
269 attr.prebuf = (ss->rate / 100) * pa_sample_size(ss) * ss->channels;
271 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
274 debug_msg("rate(%d), samplesize(%d), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
276 debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength, attr.maxlength, attr.fragsize);
278 s = pa_simple_new_proplist(NULL,"MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
281 case HANDLE_MODE_OUTPUT_CLOCK:
282 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
283 samples_per_period = (ss->rate * period_time) / 1000;
284 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK;
287 attr.tlength = (uint32_t)-1;
291 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
294 case HANDLE_MODE_OUTPUT_VIDEO: /* low latency playback */
295 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
296 samples_per_period = (ss->rate * period_time) / 1000;
297 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO;
298 attr.prebuf = 4*(samples_per_period * pa_sample_size(ss));
299 attr.minreq = samples_per_period * pa_sample_size(ss);
300 attr.tlength = periods_per_buffer * samples_per_period * pa_sample_size(ss);
304 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
307 case HANDLE_MODE_OUTPUT_AP_CALL:
308 #if defined(_MMFW_I386_ALL_SIMULATOR)
309 debug_msg("Does not support AP call mode at i386 simulator");
312 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
313 samples_per_period = (ss->rate * period_time) / 1000;
314 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
316 attr.minreq = pa_usec_to_bytes(20*PA_USEC_PER_MSEC, ss);
317 attr.tlength = pa_usec_to_bytes(100*PA_USEC_PER_MSEC, ss);
321 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map, &attr, proplist, &err);
324 case HANDLE_MODE_INPUT_AP_CALL:
325 #if defined(_MMFW_I386_ALL_SIMULATOR)
326 debug_msg("Does not support AP call mode at i386 simulator");
329 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
330 samples_per_period = (ss->rate * period_time) / 1000;
331 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
336 attr.fragsize = samples_per_period * pa_sample_size(ss);
338 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr, proplist, &err);
342 err = MM_ERROR_SOUND_INTERNAL;
348 debug_error("Open pulseaudio handle has failed - %s", pa_strerror(err));
349 if (!strncmp(pa_strerror(err), "Access denied by security check",strlen(pa_strerror(err)))) {
350 err = MM_ERROR_SOUND_PERMISSION_DENIED;
352 err = MM_ERROR_SOUND_INTERNAL;
357 handle = (mm_sound_handle_t*)malloc(sizeof(mm_sound_handle_t));
358 memset(handle, 0, sizeof(mm_sound_handle_t));
360 handle->volume_type = prop_vol_type;
361 handle->gain_type = prop_gain_type;
362 handle->rate = ss->rate;
363 handle->channels = ss->channels;
365 handle->period = samples_per_period * sample_size;
366 *size = handle->period;
367 handle->handle = mm_sound_handle_mgr.handle_count;
368 ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count); // 0 is not used
370 if (0 > pa_simple_get_stream_index(s, &handle->stream_idx, &err)) {
371 debug_msg("Can not get stream index %s", pa_strerror(err));
372 err = MM_ERROR_SOUND_INTERNAL;
375 mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
377 if(handle->handle == 0) {
378 debug_msg("out of range. handle(%d)", handle->handle);
382 debug_msg("created handle[%d]. mode(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), stream_idx(%d), s(%x), source_type(%d)",
383 handle->handle, handle->mode, handle->volume_type, handle->gain_type,
384 handle->rate, handle->channels, ss->format, handle->stream_idx, handle->s, handle->source_type);
387 pa_proplist_free(proplist);
389 return handle->handle;
393 pa_proplist_free(proplist);
403 int mm_sound_pa_write(const int handle, void* buf, const int size)
405 mm_sound_handle_t* phandle = NULL;
406 int err = MM_ERROR_NONE;
409 return MM_ERROR_INVALID_ARGUMENT;
412 return MM_ERROR_INVALID_ARGUMENT;
414 return MM_ERROR_NONE;
416 CHECK_HANDLE_RANGE(handle);
417 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
419 #ifdef __STREAM_DEBUG__
420 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d), buf(%p), size(%d)",
421 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx. buf, size);
424 if(phandle == NULL) {
425 debug_msg("phandle is null");
426 return MM_ERROR_SOUND_INTERNAL;
429 if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
430 debug_error("pa_simple_write() failed with %s", pa_strerror(err));
431 return MM_ERROR_SOUND_INTERNAL;
439 int mm_sound_pa_close(const int handle)
441 mm_sound_handle_t* phandle = NULL;
442 int err = MM_ERROR_NONE;
444 CHECK_HANDLE_RANGE(handle);
445 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
446 if(phandle == NULL) {
447 debug_msg("phandle is null");
448 return MM_ERROR_SOUND_INTERNAL;
451 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d)",
452 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx);
454 switch (phandle->mode) {
455 case HANDLE_MODE_OUTPUT:
456 case HANDLE_MODE_OUTPUT_CLOCK:
457 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
458 case HANDLE_MODE_OUTPUT_AP_CALL:
459 case HANDLE_MODE_OUTPUT_VIDEO:
460 if (0 > pa_simple_flush(phandle->s, &err)) {
461 err = MM_ERROR_SOUND_INTERNAL;
462 debug_msg("pa_simple_flush() failed with %s", pa_strerror(err));
469 pa_simple_free(phandle->s);
472 debug_msg("leave: handle[%d] stream_index[%d]", handle, phandle->stream_idx);
474 mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
475 if(phandle != NULL) {
485 int mm_sound_pa_drain(const int handle)
487 mm_sound_handle_t* phandle = NULL;
488 int err = MM_ERROR_NONE;
490 CHECK_HANDLE_RANGE(handle);
491 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
493 return MM_ERROR_SOUND_INTERNAL;
495 if (0 > pa_simple_drain(phandle->s, &err)) {
496 debug_error("pa_simple_drain() failed with %s", pa_strerror(err));
497 err = MM_ERROR_SOUND_INTERNAL;