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; \
90 #define PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT 1536 /* frames */
91 #define PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE 4
92 #define PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT 6
93 #define PA_SIMPLE_PERIODS_PER_BUFFER_VOIP 2
94 #define PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK 8
95 #define PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE 12
96 #define PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO 10
98 #define PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC 20
99 #define PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC 25
100 #define PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC 50
101 #define PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC 75
102 #define PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC 20
104 __attribute__ ((constructor))
105 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))
116 void __mm_sound_pa_deinit(void)
118 g_list_free(mm_sound_handle_mgr.handles);
119 pthread_mutex_destroy(&mm_sound_handle_mgr.lock);
120 mm_sound_handle_mgr.handle_count = 0;
122 if (mm_sound_handle_mgr.state) {
123 debug_msg("mainloop(%x), context(%x)", mm_sound_handle_mgr.mainloop, mm_sound_handle_mgr.context);
124 pa_threaded_mainloop_stop(mm_sound_handle_mgr.mainloop);
125 pa_context_disconnect(mm_sound_handle_mgr.context);
126 pa_context_unref(mm_sound_handle_mgr.context);
127 pa_threaded_mainloop_free(mm_sound_handle_mgr.mainloop);
131 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
133 mm_sound_handle_t *phandle = (mm_sound_handle_t *) a;
134 int *handle = (int *)b;
139 if (phandle->handle == *handle)
146 int mm_sound_pa_open(MMSoundHandleMode mode, int volume_config, pa_sample_spec * ss, pa_channel_map * channel_map,
147 int *size, char *stream_type, int stream_index)
153 int prop_vol_type = 0;
154 int prop_gain_type = VOLUME_GAIN_DEFAULT;
156 int err = MM_ERROR_SOUND_INTERNAL;
157 int period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
158 int samples_per_period = PA_SIMPLE_SAMPLES_PER_PERIOD_DEFAULT;
159 int periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
161 int handle_mode = mode;
163 pa_proplist *proplist = NULL;
165 mm_sound_handle_t *handle = NULL;
167 if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
168 return MM_ERROR_INVALID_ARGUMENT;
170 proplist = pa_proplist_new();
172 if (channel_map == NULL) {
173 pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
177 switch (ss->format) {
179 sample_size = 1 * ss->channels;
181 case PA_SAMPLE_S16LE:
182 sample_size = 2 * ss->channels;
186 debug_error("Invalid sample size (%d)", sample_size);
190 /* Set volume type of stream */
191 if (volume_config > 0) {
192 debug_log("setting gain type");
193 prop_vol_type = 0; /* not used, set it system(0) temporarily */
195 /* Set gain type of stream */
196 prop_gain_type = (volume_config >> 8) & 0x000000FF;
198 pa_proplist_setf(proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", prop_gain_type);
201 if (stream_index != -1) {
202 char stream_index_s[11];
203 debug_msg("Set stream index [%d]", stream_index);
205 snprintf(stream_index_s, sizeof(stream_index_s) - 1, "%d", stream_index);
206 debug_msg("stream_index[%d] converted to string[%s]", stream_index, stream_index_s);
207 pa_proplist_sets(proplist, PA_PROP_MEDIA_PARENT_ID, stream_index_s);
209 /* Set stream type */
210 pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
212 memset(&attr, '\0', sizeof(attr));
214 switch (handle_mode) {
215 case HANDLE_MODE_INPUT:
216 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
217 samples_per_period = (ss->rate * period_time) / 1000;
218 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
223 attr.fragsize = samples_per_period * pa_sample_size(ss);
225 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr,
229 case HANDLE_MODE_INPUT_LOW_LATENCY:
230 period_time = PA_SIMPLE_PERIOD_TIME_FOR_ULOW_LATENCY_MSEC;
231 samples_per_period = (ss->rate * period_time) / 1000;
232 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
237 attr.fragsize = samples_per_period * pa_sample_size(ss);
239 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map,
240 &attr, proplist, &err);
243 case HANDLE_MODE_INPUT_HIGH_LATENCY:
244 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
245 samples_per_period = (ss->rate * period_time) / 1000;
246 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_CAPTURE;
251 attr.fragsize = samples_per_period * pa_sample_size(ss);
253 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map,
254 &attr, proplist, &err);
257 case HANDLE_MODE_OUTPUT:
258 period_time = PA_SIMPLE_PERIOD_TIME_FOR_MID_LATENCY_MSEC;
259 samples_per_period = (ss->rate * period_time) / 1000;
260 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_DEFAULT;
263 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
267 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr,
271 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
272 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
273 samples_per_period = (ss->rate * period_time) / 1000;
274 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_FASTMODE;
275 attr.prebuf = (ss->rate / 100) * pa_sample_size(ss) * ss->channels;
277 attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
280 debug_msg("rate(%d), samplesize(%d), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
282 debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength,
283 attr.maxlength, attr.fragsize);
285 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
286 channel_map, &attr, proplist, &err);
289 case HANDLE_MODE_OUTPUT_CLOCK:
290 period_time = PA_SIMPLE_PERIOD_TIME_FOR_HIGH_LATENCY_MSEC;
291 samples_per_period = (ss->rate * period_time) / 1000;
292 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_PLAYBACK;
295 attr.tlength = (uint32_t) - 1;
299 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss,
300 channel_map, &attr, proplist, &err);
303 case HANDLE_MODE_OUTPUT_VIDEO: /* low latency playback */
304 period_time = PA_SIMPLE_PERIOD_TIME_FOR_LOW_LATENCY_MSEC;
305 samples_per_period = (ss->rate * period_time) / 1000;
306 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VIDEO;
307 attr.prebuf = 4 * (samples_per_period * pa_sample_size(ss));
308 attr.minreq = samples_per_period * pa_sample_size(ss);
309 attr.tlength = periods_per_buffer * samples_per_period * pa_sample_size(ss);
313 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss,
314 channel_map, &attr, proplist, &err);
317 case HANDLE_MODE_OUTPUT_AP_CALL:
318 #if defined(_MMFW_I386_ALL_SIMULATOR)
319 debug_msg("Does not support AP call mode at i386 simulator");
322 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
323 samples_per_period = (ss->rate * period_time) / 1000;
324 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
326 attr.minreq = pa_usec_to_bytes(20 * PA_USEC_PER_MSEC, ss);
327 attr.tlength = pa_usec_to_bytes(100 * PA_USEC_PER_MSEC, ss);
331 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map,
332 &attr, proplist, &err);
335 case HANDLE_MODE_INPUT_AP_CALL:
336 #if defined(_MMFW_I386_ALL_SIMULATOR)
337 debug_msg("Does not support AP call mode at i386 simulator");
340 period_time = PA_SIMPLE_PERIOD_TIME_FOR_VOIP_LATENCY_MSEC;
341 samples_per_period = (ss->rate * period_time) / 1000;
342 periods_per_buffer = PA_SIMPLE_PERIODS_PER_BUFFER_VOIP;
347 attr.fragsize = samples_per_period * pa_sample_size(ss);
349 s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr,
354 err = MM_ERROR_SOUND_INTERNAL;
360 debug_error("Open pulseaudio handle has failed - %s", pa_strerror(err));
361 if (!strncmp(pa_strerror(err), "Access denied by security check", strlen(pa_strerror(err))))
362 err = MM_ERROR_SOUND_PERMISSION_DENIED;
364 err = MM_ERROR_SOUND_INTERNAL;
368 handle = (mm_sound_handle_t *) malloc(sizeof(mm_sound_handle_t));
369 memset(handle, 0, sizeof(mm_sound_handle_t));
371 handle->volume_type = prop_vol_type;
372 handle->gain_type = prop_gain_type;
373 handle->rate = ss->rate;
374 handle->channels = ss->channels;
376 handle->period = samples_per_period * sample_size;
377 *size = handle->period;
378 handle->handle = mm_sound_handle_mgr.handle_count;
379 ATOMIC_INC(&mm_sound_handle_mgr.lock, mm_sound_handle_mgr.handle_count); // 0 is not used
381 if (0 > pa_simple_get_stream_index(s, &handle->stream_idx, &err)) {
382 debug_msg("Can not get stream index %s", pa_strerror(err));
383 err = MM_ERROR_SOUND_INTERNAL;
386 mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
388 if (handle->handle == 0) {
389 debug_msg("out of range. handle(%d)", handle->handle);
394 ("created handle[%d]. mode(%d), volumetype(%d), gain(%d), rate(%d), channels(%d), format(%d), stream_idx(%d), s(%x), source_type(%d)",
395 handle->handle, handle->mode, handle->volume_type, handle->gain_type, handle->rate, handle->channels, ss->format,
396 handle->stream_idx, handle->s, handle->source_type);
399 pa_proplist_free(proplist);
401 return handle->handle;
405 pa_proplist_free(proplist);
415 int mm_sound_pa_write(const int handle, void *buf, const int size)
417 mm_sound_handle_t *phandle = NULL;
418 int err = MM_ERROR_NONE;
421 return MM_ERROR_INVALID_ARGUMENT;
424 return MM_ERROR_INVALID_ARGUMENT;
426 return MM_ERROR_NONE;
428 CHECK_HANDLE_RANGE(handle);
429 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
431 #ifdef __STREAM_DEBUG__
432 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d), buf(%p), size(%d)",
433 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx.buf, size);
436 if (phandle == NULL) {
437 debug_msg("phandle is null");
438 return MM_ERROR_SOUND_INTERNAL;
441 if (0 > pa_simple_write(phandle->s, buf, size, &err)) {
442 debug_error("pa_simple_write() failed with %s", pa_strerror(err));
443 return MM_ERROR_SOUND_INTERNAL;
451 int mm_sound_pa_close(const int handle)
453 mm_sound_handle_t *phandle = NULL;
454 int err = MM_ERROR_NONE;
456 CHECK_HANDLE_RANGE(handle);
457 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
458 if (phandle == NULL) {
459 debug_msg("phandle is null");
460 return MM_ERROR_SOUND_INTERNAL;
463 debug_msg("phandle(%x) s(%x), handle(%d), rate(%d), ch(%d) stream_idx(%d)",
464 phandle, phandle->s, phandle->handle, phandle->rate, phandle->channels, phandle->stream_idx);
466 switch (phandle->mode) {
467 case HANDLE_MODE_OUTPUT:
468 case HANDLE_MODE_OUTPUT_CLOCK:
469 case HANDLE_MODE_OUTPUT_LOW_LATENCY:
470 case HANDLE_MODE_OUTPUT_AP_CALL:
471 case HANDLE_MODE_OUTPUT_VIDEO:
472 if (0 > pa_simple_flush(phandle->s, &err)) {
473 err = MM_ERROR_SOUND_INTERNAL;
474 debug_msg("pa_simple_flush() failed with %s", pa_strerror(err));
481 pa_simple_free(phandle->s);
484 debug_msg("leave: handle[%d] stream_index[%d]", handle, phandle->stream_idx);
486 mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
487 if (phandle != NULL) {
497 int mm_sound_pa_drain(const int handle)
499 mm_sound_handle_t *phandle = NULL;
500 int err = MM_ERROR_NONE;
502 CHECK_HANDLE_RANGE(handle);
503 GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
505 return MM_ERROR_SOUND_INTERNAL;
507 if (0 > pa_simple_drain(phandle->s, &err)) {
508 debug_error("pa_simple_drain() failed with %s", pa_strerror(err));
509 err = MM_ERROR_SOUND_INTERNAL;