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 {
47 int period; /* open api retrun value.*/
49 unsigned int stream_idx;
53 #define MM_SOUND_HANDLE_MAX 32
55 uint32_t handle_count; /* use amotic operations */
60 pa_threaded_mainloop *mainloop;
62 } mm_sound_handle_mgr;
64 #define CHECK_HANDLE_RANGE(x) \
67 debug_msg("invalid handle(%d)", x); \
68 return MM_ERROR_INVALID_ARGUMENT; \
72 #define CHECK_VOLUME_TYPE_RANGE(x) \
74 if(x < VOLUME_TYPE_SYSTEM || x >= VOLUME_TYPE_MAX) { \
75 debug_msg("invalid volume type(%d)", x); \
76 return MM_ERROR_INVALID_ARGUMENT; \
80 #define ATOMIC_INC(l, x) \
82 pthread_mutex_lock(l); \
86 pthread_mutex_unlock(l); \
89 // phandle(ret), GList, userdata, coimpare func
90 #define GET_HANDLE_DATA(p, l, u, func) \
93 list = g_list_find_custom(l, u, func); \
95 p = (mm_sound_handle_t*)list->data; \
101 // should be call after pa_ext function.
102 #define WAIT_PULSEAUDIO_OPERATION(x, y) \
104 while (pa_operation_get_state(y) == PA_OPERATION_RUNNING) { \
105 debug_msg("waiting.................."); \
106 pa_threaded_mainloop_wait(x.mainloop); \
107 debug_msg("waiting DONE"); \
111 #define PA_SIMPLE_FADE_INTERVAL_USEC 20000
113 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT 1536 /* frames */
114 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE 4
115 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT 6
116 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP 2
117 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK 8
118 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE 12
119 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO 10
121 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC 20
122 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC 25
123 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC 50
124 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC 75
125 #define PA_SIMPLE_PERIOD_TIME_FOR_VERY_HIGH_LATENCY_MSEC 150
126 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC 20
128 #define IS_INPUT_HANDLE(x) \
129 if( x == HANDLE_MODE_INPUT || x == HANDLE_MODE_INPUT_HIGH_LATENCY || \
130 x == HANDLE_MODE_INPUT_LOW_LATENCY || x == HANDLE_MODE_INPUT_AP_CALL )
132 __attribute__ ((constructor)) void __mm_sound_pa_init(void)
134 memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
135 mm_sound_handle_mgr.state = FALSE;
137 mm_sound_handle_mgr.handles = g_list_alloc();
138 mm_sound_handle_mgr.handle_count = 1;
139 pthread_mutex_init(&mm_sound_handle_mgr.lock, NULL);
142 __attribute__ ((destructor)) void __mm_sound_pa_deinit(void)
144 g_list_free(mm_sound_handle_mgr.handles);
145 pthread_mutex_destroy(&mm_sound_handle_mgr.lock);
146 mm_sound_handle_mgr.handle_count = 0;
148 if(mm_sound_handle_mgr.state) {
149 debug_msg("mainloop(%x), context(%x)", mm_sound_handle_mgr.mainloop, mm_sound_handle_mgr.context);
150 pa_threaded_mainloop_stop(mm_sound_handle_mgr.mainloop);
151 pa_context_disconnect(mm_sound_handle_mgr.context);
152 pa_context_unref(mm_sound_handle_mgr.context);
153 pa_threaded_mainloop_free(mm_sound_handle_mgr.mainloop);
157 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
159 mm_sound_handle_t* phandle = (mm_sound_handle_t*)a;
160 int* handle = (int*)b;
165 if(phandle->handle == *handle)
172 int mm_sound_pa_open(MMSoundHandleMode mode, mm_sound_handle_route_info *route_info, MMSoundHandlePriority priority, int volume_config, pa_sample_spec* ss, pa_channel_map* channel_map, int* size, char *stream_type, int stream_index)
178 int prop_vol_type = 0;
179 int prop_gain_type = VOLUME_GAIN_DEFAULT;
181 int err = MM_ERROR_SOUND_INTERNAL;
182 int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
183 int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
184 int periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
186 int handle_mode = mode;
187 int handle_inout = HANDLE_DIRECTION_NONE;
189 pa_proplist *proplist = NULL;
191 mm_sound_handle_t* handle = NULL;
192 MMSoundHandleRoutePolicy policy = HANDLE_ROUTE_POLICY_DEFAULT;
195 policy = route_info->policy;
198 if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
199 return MM_ERROR_INVALID_ARGUMENT;
201 proplist = pa_proplist_new();
203 if(channel_map == NULL) {
204 pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
210 sample_size = 1 * ss->channels;
212 case PA_SAMPLE_S16LE:
213 sample_size = 2 * ss->channels;
217 debug_error("Invalid sample size (%d)", sample_size);
221 /* Set volume type of stream */
222 if(volume_config > 0) {
223 debug_log("setting gain type");
224 prop_vol_type = 0; /* not used, set it system(0) temporarily */
226 /* Set gain type of stream */
227 prop_gain_type = (volume_config >> 8) & 0x000000FF;
229 pa_proplist_setf(proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", prop_gain_type);
232 if (stream_index != -1) {
233 char stream_index_s[11];
234 debug_msg("Set stream index [%d]", stream_index);
236 snprintf(stream_index_s, sizeof(stream_index_s)-1, "%d", stream_index);
237 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
238 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
240 /* Set stream type */
241 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
243 memset(&attr, '\0', sizeof(attr));
245 switch (handle_mode) {
246 case HANDLE_MODE_INPUT:
247 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
248 samples_per_period = (ss->rate * period_time) / 1000;
249 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
254 attr.fragsize = samples_per_period * pa_sample_size(ss);
256 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr, proplist, &err);
259 case HANDLE_MODE_INPUT_LOW_LATENCY:
260 period_time = PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC;
261 samples_per_period = (ss->rate * period_time) / 1000;
262 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
267 attr.fragsize = samples_per_period * pa_sample_size(ss);
269 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
272 case HANDLE_MODE_INPUT_HIGH_LATENCY:
273 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
274 samples_per_period = (ss->rate * period_time) / 1000;
275 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE;
280 attr.fragsize = samples_per_period * pa_sample_size(ss);
282 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
285 case HANDLE_MODE_OUTPUT:
286 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
287 samples_per_period = (ss->rate * period_time) / 1000;
288 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
291 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
295 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr, proplist, &err);
298 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
299 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
300 samples_per_period = (ss->rate * period_time) / 1000;
301 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
302 attr.prebuf = (ss->rate / 100) * pa_sample_size(ss) * ss->channels;
304 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
307 debug_msg("rate(%d), samplesize(%d), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
309 debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength, attr.maxlength, attr.fragsize);
311 s = pa_simple_new_proplist(NULL,"MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
314 case HANDLE_MODE_OUTPUT_CLOCK:
315 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
316 samples_per_period = (ss->rate * period_time) / 1000;
317 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK;
320 attr.tlength = (uint32_t)-1;
324 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
327 case HANDLE_MODE_OUTPUT_VIDEO: /* low latency playback */
328 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
329 samples_per_period = (ss->rate * period_time) / 1000;
330 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO;
331 attr.prebuf = 4*(samples_per_period * pa_sample_size(ss));
332 attr.minreq = samples_per_period * pa_sample_size(ss);
333 attr.tlength = periods_per_buffer * samples_per_period * pa_sample_size(ss);
337 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
340 case HANDLE_MODE_OUTPUT_AP_CALL:
341 #if defined(_MMFW_I386_ALL_SIMULATOR)
342 debug_msg("Does not support AP call mode at i386 simulator\n");
345 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
346 samples_per_period = (ss->rate * period_time) / 1000;
347 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
349 attr.minreq = pa_usec_to_bytes(20*PA_USEC_PER_MSEC, ss);
350 attr.tlength = pa_usec_to_bytes(100*PA_USEC_PER_MSEC, ss);
354 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map, &attr, proplist, &err);
357 case HANDLE_MODE_INPUT_AP_CALL:
358 #if defined(_MMFW_I386_ALL_SIMULATOR)
359 debug_msg("Does not support AP call mode at i386 simulator\n");
362 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
363 samples_per_period = (ss->rate * period_time) / 1000;
364 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
369 attr.fragsize = samples_per_period * pa_sample_size(ss);
371 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr, proplist, &err);
375 err = MM_ERROR_SOUND_INTERNAL;
381 debug_error("Open pulseaudio handle has failed - %s\n", pa_strerror(err));
382 if (!strncmp(pa_strerror(err), "Access denied by security check",strlen(pa_strerror(err)))) {
383 err = MM_ERROR_SOUND_PERMISSION_DENIED;
385 err = MM_ERROR_SOUND_INTERNAL;
390 handle = (mm_sound_handle_t*)malloc(sizeof(mm_sound_handle_t));
391 memset(handle, 0, sizeof(mm_sound_handle_t));
393 handle->policy = policy;
394 handle->volume_type = prop_vol_type;
395 handle->gain_type = prop_gain_type;
396 handle->rate = ss->rate;
397 handle->channels = ss->channels;
399 handle->period = samples_per_period * sample_size;
400 *size = handle->period;
401 handle->handle = mm_sound_handle_mgr.handle_count;
402 ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count); // 0 is not used
404 if (0 > pa_simple_get_stream_index(s, &handle->stream_idx, &err)) {
405 debug_msg("Can not get stream index %s\n", pa_strerror(err));
406 err = MM_ERROR_SOUND_INTERNAL;
409 mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
411 if(handle->handle == 0) {
412 debug_msg("out of range. handle(%d)\n", handle->handle);
416 debug_msg("created handle[%d]. mode(%d), policy(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), stream_idx(%d), s(%x), source_type(%d) handle_inout(%d)",
417 handle->handle, handle->mode, handle->policy, handle->volume_type, handle->gain_type,
418 handle->rate, handle->channels, ss->format, handle->stream_idx, handle->s, handle->source_type, handle_inout);
421 pa_proplist_free(proplist);
423 return handle->handle;
427 pa_proplist_free(proplist);
437 int mm_sound_pa_read(const int handle, void* buf, const int size)
439 mm_sound_handle_t* phandle = NULL;
440 int err = MM_ERROR_NONE;
442 #ifdef __STREAM_DEBUG__
443 debug_msg("handle(%d), buf(%p), size(%d)", handle, buf, size);
446 return MM_ERROR_INVALID_ARGUMENT;
449 return MM_ERROR_INVALID_ARGUMENT;
453 CHECK_HANDLE_RANGE(handle);
454 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
455 if(phandle == NULL) {
456 debug_msg("phandle is null");
457 return MM_ERROR_SOUND_INTERNAL;
460 if (0 > pa_simple_read(phandle->s, buf, size, &err)) {
461 debug_error("pa_simple_read() failed with %s", pa_strerror(err));
462 return MM_ERROR_SOUND_INTERNAL;
469 int mm_sound_pa_write(const int handle, void* buf, const int size)
471 mm_sound_handle_t* phandle = NULL;
472 int err = MM_ERROR_NONE;
475 return MM_ERROR_INVALID_ARGUMENT;
478 return MM_ERROR_INVALID_ARGUMENT;
480 return MM_ERROR_NONE;
482 CHECK_HANDLE_RANGE(handle);
483 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
485 #ifdef __STREAM_DEBUG__
486 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d), buf(%p), size(%d)",
487 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx. buf, size);
490 if(phandle == NULL) {
491 debug_msg("phandle is null");
492 return MM_ERROR_SOUND_INTERNAL;
495 if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
496 debug_error("pa_simple_write() failed with %s\n", pa_strerror(err));
497 return MM_ERROR_SOUND_INTERNAL;
505 int mm_sound_pa_close(const int handle)
507 mm_sound_handle_t* phandle = NULL;
508 int err = MM_ERROR_NONE;
510 CHECK_HANDLE_RANGE(handle);
511 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
512 if(phandle == NULL) {
513 debug_msg("phandle is null");
514 return MM_ERROR_SOUND_INTERNAL;
517 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d)",
518 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx);
520 switch (phandle->mode) {
521 case HANDLE_MODE_OUTPUT:
522 case HANDLE_MODE_OUTPUT_CLOCK:
523 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
524 case HANDLE_MODE_OUTPUT_AP_CALL:
525 case HANDLE_MODE_OUTPUT_VIDEO:
526 if (0 > pa_simple_flush(phandle->s, &err)) {
527 err = MM_ERROR_SOUND_INTERNAL;
528 debug_msg("pa_simple_flush() failed with %s\n", pa_strerror(err));
535 pa_simple_free(phandle->s);
538 debug_msg("leave: handle[%d] stream_index[%d]\n", handle, phandle->stream_idx);
540 mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
541 if(phandle != NULL) {
551 int mm_sound_pa_drain(const int handle)
553 mm_sound_handle_t* phandle = NULL;
554 int err = MM_ERROR_NONE;
556 CHECK_HANDLE_RANGE(handle);
557 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
559 return MM_ERROR_SOUND_INTERNAL;
561 if (0 > pa_simple_drain(phandle->s, &err)) {
562 debug_error("pa_simple_drain() failed with %s\n", pa_strerror(err));
563 err = MM_ERROR_SOUND_INTERNAL;
570 int mm_sound_pa_flush(const int handle)
572 mm_sound_handle_t* phandle = NULL;
573 int err = MM_ERROR_NONE;
575 CHECK_HANDLE_RANGE(handle);
576 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
578 return MM_ERROR_SOUND_INTERNAL;
580 if (0 > pa_simple_flush(phandle->s, &err)) {
581 debug_error("pa_simple_flush() failed with %s\n", pa_strerror(err));
582 err = MM_ERROR_SOUND_INTERNAL;
588 typedef struct _get_volume_max_userdata_t
590 pa_threaded_mainloop* mainloop;
592 } get_volume_max_userdata_t;