Generate /tmp/.sound_server_ready as well as /tmp/.focus_server_ready for compatibility
[platform/core/multimedia/libmm-sound.git] / mm_sound_pa_client.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Seungbae Shin <seungbae.shin@samsung.com>
7  *
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <mm_error.h>
27 #include <mm_debug.h>
28
29 #include <mm_sound_pa_client.h>
30
31 #include <glib.h>
32
33 #define MM_SOUND_CHANNEL_MIN                1
34 #define MM_SOUND_CHANNEL_MAX                6
35
36 typedef struct _mm_sound_handle_t {
37     uint32_t handle;
38
39     int mode;
40     int policy;
41     int volume_type;
42     int gain_type;
43     int rate;
44     int channels;
45     pa_simple* s;
46
47     int period; /* open api retrun value.*/
48
49     unsigned int stream_idx;
50     int source_type;
51 } mm_sound_handle_t;
52
53 #define MM_SOUND_HANDLE_MAX                 32
54 static struct {
55     uint32_t handle_count; /* use amotic operations */
56     GList* handles;
57     pthread_mutex_t lock;
58
59     int state;
60     pa_threaded_mainloop *mainloop;
61     pa_context *context;
62 } mm_sound_handle_mgr;
63
64 #define CHECK_HANDLE_RANGE(x) \
65     do { \
66         if(x == 0) { \
67             debug_msg("invalid handle(%d)", x); \
68             return MM_ERROR_INVALID_ARGUMENT; \
69         } \
70     } while(0);
71
72 #define CHECK_VOLUME_TYPE_RANGE(x) \
73     do { \
74         if(x < VOLUME_TYPE_SYSTEM || x >= VOLUME_TYPE_MAX) { \
75             debug_msg("invalid volume type(%d)", x); \
76             return MM_ERROR_INVALID_ARGUMENT; \
77         } \
78     } while(0);
79
80 #define ATOMIC_INC(l, x) \
81     do { \
82         pthread_mutex_lock(l); \
83         x = x + 1; \
84         if(x == 0) \
85             x = x + 1; \
86         pthread_mutex_unlock(l); \
87     } while(0);
88
89 // phandle(ret), GList, userdata, coimpare func
90 #define GET_HANDLE_DATA(p, l, u, func) \
91     do { \
92         GList* list = 0; \
93         list = g_list_find_custom(l, u, func); \
94         if(list != 0) \
95             p = (mm_sound_handle_t*)list->data; \
96         else \
97             p = NULL; \
98     } while(0);
99
100
101 // should be call after pa_ext function.
102 #define WAIT_PULSEAUDIO_OPERATION(x, y) \
103     do { \
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"); \
108         } \
109     } while(0);
110
111 #define PA_SIMPLE_FADE_INTERVAL_USEC                                            20000
112
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
120
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
127
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 )
131
132 __attribute__ ((constructor)) void __mm_sound_pa_init(void)
133 {
134     memset(&mm_sound_handle_mgr, 0, sizeof(mm_sound_handle_mgr));
135     mm_sound_handle_mgr.state = FALSE;
136
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);
140 }
141
142 __attribute__ ((destructor)) void __mm_sound_pa_deinit(void)
143 {
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;
147
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);
154     }
155 }
156
157 gint __mm_sound_handle_comparefunc(gconstpointer a, gconstpointer b)
158 {
159     mm_sound_handle_t* phandle = (mm_sound_handle_t*)a;
160     int* handle = (int*)b;
161
162     if(phandle == NULL)
163         return -1;
164
165     if(phandle->handle == *handle)
166         return 0;
167     else
168         return -1;
169 }
170
171 EXPORT_API
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)
173 {
174     pa_simple *s = NULL;
175     pa_channel_map maps;
176     pa_buffer_attr attr;
177
178     int prop_vol_type = 0;
179     int prop_gain_type = VOLUME_GAIN_DEFAULT;
180
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;
185
186     int handle_mode = mode;
187     int handle_inout = HANDLE_DIRECTION_NONE;
188     int sample_size = 0;
189     pa_proplist *proplist = NULL;
190
191     mm_sound_handle_t* handle = NULL;
192     MMSoundHandleRoutePolicy policy = HANDLE_ROUTE_POLICY_DEFAULT;
193
194     if(route_info) {
195         policy = route_info->policy;
196     }
197
198     if (ss->channels < MM_SOUND_CHANNEL_MIN || ss->channels > MM_SOUND_CHANNEL_MAX)
199         return MM_ERROR_INVALID_ARGUMENT;
200
201     proplist = pa_proplist_new();
202
203     if(channel_map == NULL) {
204         pa_channel_map_init_auto(&maps, ss->channels, PA_CHANNEL_MAP_ALSA);
205         channel_map = &maps;
206     }
207
208     switch(ss->format) {
209         case PA_SAMPLE_U8:
210             sample_size = 1 * ss->channels;
211             break;
212         case PA_SAMPLE_S16LE:
213             sample_size = 2 * ss->channels;
214             break;
215         default :
216             sample_size = 0;
217             debug_error("Invalid sample size (%d)", sample_size);
218             break;
219     }
220
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 */
225
226         /* Set gain type of stream */
227         prop_gain_type = (volume_config >> 8) & 0x000000FF;
228
229         pa_proplist_setf(proplist, PA_PROP_MEDIA_TIZEN_GAIN_TYPE, "%d", prop_gain_type);
230     }
231
232     if (stream_index != -1) {
233         char stream_index_s[11];
234         debug_msg("Set stream index [%d]", stream_index);
235
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);
239     }
240     /* Set stream type */
241     pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, stream_type);
242
243     memset(&attr, '\0', sizeof(attr));
244
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;
250         attr.prebuf = 0;
251         attr.minreq = -1;
252         attr.tlength = -1;
253         attr.maxlength = -1;
254         attr.fragsize = samples_per_period * pa_sample_size(ss);
255
256         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "CAPTURE", ss, channel_map, &attr, proplist, &err);
257         break;
258
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;
263         attr.prebuf = 0;
264         attr.minreq = -1;
265         attr.tlength = -1;
266         attr.maxlength = -1;
267         attr.fragsize = samples_per_period * pa_sample_size(ss);
268
269         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "LOW LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
270         break;
271
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;
276         attr.prebuf = 0;
277         attr.minreq = -1;
278         attr.tlength = -1;
279         attr.maxlength = -1;
280         attr.fragsize = samples_per_period * pa_sample_size(ss);
281
282         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "HIGH LATENCY CAPTURE", ss, channel_map, &attr, proplist, &err);
283         break;
284
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;
289         attr.prebuf = -1;
290         attr.minreq = -1;
291         attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
292         attr.maxlength = -1;
293         attr.fragsize = 0;
294
295         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "PLAYBACK", ss, channel_map, &attr, proplist, &err);
296         break;
297
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;
303         attr.minreq = -1;
304         attr.tlength = (ss->rate / 10) * pa_sample_size(ss) * ss->channels;
305         attr.maxlength = -1;
306         attr.fragsize = 0;
307         debug_msg("rate(%d), samplesize(%d), ch(%d) format(%d)", ss->rate, pa_sample_size(ss), ss->channels, ss->format);
308
309         debug_msg("prebuf(%d), minreq(%d), tlength(%d), maxlength(%d), fragsize(%d)", attr.prebuf, attr.minreq, attr.tlength, attr.maxlength, attr.fragsize);
310
311         s = pa_simple_new_proplist(NULL,"MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
312         break;
313
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;
318         attr.prebuf = -1;
319         attr.minreq = -1;
320         attr.tlength = (uint32_t)-1;
321         attr.maxlength = -1;
322         attr.fragsize = 0;
323
324         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "HIGH LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
325         break;
326
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);
334         attr.maxlength = -1;
335         attr.fragsize = 0;
336
337         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "LOW LATENCY PLAYBACK", ss, channel_map, &attr, proplist, &err);
338         break;
339
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");
343         s = NULL;
344 #else
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;
348         attr.prebuf = -1;
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);
351         attr.maxlength = -1;
352         attr.fragsize = 0;
353
354         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_PLAYBACK, NULL, "VoIP PLAYBACK", ss, channel_map, &attr, proplist, &err);
355 #endif
356         break;
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");
360         s = NULL;
361 #else
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;
365         attr.prebuf = 0;
366         attr.minreq = -1;
367         attr.tlength = -1;
368         attr.maxlength = -1;
369         attr.fragsize = samples_per_period * pa_sample_size(ss);
370
371         s = pa_simple_new_proplist(NULL, "MM_SOUND_PA_CLIENT", PA_STREAM_RECORD, NULL, "VoIP CAPTURE", ss, channel_map, &attr, proplist, &err);
372 #endif
373         break;
374     default:
375         err = MM_ERROR_SOUND_INTERNAL;
376         goto fail;
377         break;
378     }
379
380     if (!s) {
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;
384         } else {
385             err = MM_ERROR_SOUND_INTERNAL;
386         }
387         goto fail;
388     }
389
390     handle = (mm_sound_handle_t*)malloc(sizeof(mm_sound_handle_t));
391     memset(handle, 0, sizeof(mm_sound_handle_t));
392     handle->mode = mode;
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;
398     handle->s = s;
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
403
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;
407         goto fail;
408     }
409     mm_sound_handle_mgr.handles = g_list_append(mm_sound_handle_mgr.handles, handle);
410
411     if(handle->handle == 0) {
412         debug_msg("out of range. handle(%d)\n", handle->handle);
413         goto fail;
414     }
415
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);
419
420     if (proplist)
421         pa_proplist_free(proplist);
422
423     return handle->handle;
424
425 fail:
426     if (proplist)
427         pa_proplist_free(proplist);
428
429     if(handle)
430         free(handle);
431
432     return err;
433
434 }
435
436 EXPORT_API
437 int mm_sound_pa_read(const int handle, void* buf, const int size)
438 {
439     mm_sound_handle_t* phandle = NULL;
440     int err = MM_ERROR_NONE;
441
442 #ifdef __STREAM_DEBUG__
443     debug_msg("handle(%d), buf(%p), size(%d)", handle, buf, size);
444 #endif
445     if (buf == NULL)
446         return MM_ERROR_INVALID_ARGUMENT;
447
448     if (size < 0)
449         return MM_ERROR_INVALID_ARGUMENT;
450     else if (size == 0)
451         return size;
452
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;
458     }
459
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;
463     }
464
465     return size;
466 }
467
468 EXPORT_API
469 int mm_sound_pa_write(const int handle, void* buf, const int size)
470 {
471     mm_sound_handle_t* phandle = NULL;
472     int err = MM_ERROR_NONE;
473
474     if (buf == NULL)
475         return MM_ERROR_INVALID_ARGUMENT;
476
477     if (size < 0)
478         return MM_ERROR_INVALID_ARGUMENT;
479     else if (size == 0)
480         return MM_ERROR_NONE;
481
482     CHECK_HANDLE_RANGE(handle);
483     GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
484
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);
488 #endif
489
490     if(phandle == NULL) {
491         debug_msg("phandle is null");
492         return MM_ERROR_SOUND_INTERNAL;
493     }
494
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;
498     }
499
500     return size;
501
502 }
503
504 EXPORT_API
505 int mm_sound_pa_close(const int handle)
506 {
507     mm_sound_handle_t* phandle = NULL;
508     int err = MM_ERROR_NONE;
509
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;
515     }
516
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);
519
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));
529                 }
530                 break;
531         default:
532                 break;
533         }
534
535         pa_simple_free(phandle->s);
536     phandle->s = NULL;
537
538     debug_msg("leave: handle[%d] stream_index[%d]\n", handle, phandle->stream_idx);
539
540     mm_sound_handle_mgr.handles = g_list_remove(mm_sound_handle_mgr.handles, phandle);
541     if(phandle != NULL) {
542         free(phandle);
543         phandle = NULL;
544     }
545
546     return err;
547
548 }
549
550 EXPORT_API
551 int mm_sound_pa_drain(const int handle)
552 {
553     mm_sound_handle_t* phandle = NULL;
554     int err = MM_ERROR_NONE;
555
556         CHECK_HANDLE_RANGE(handle);
557     GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
558     if(phandle == NULL)
559         return MM_ERROR_SOUND_INTERNAL;
560
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;
564         }
565
566         return err;
567 }
568
569 EXPORT_API
570 int mm_sound_pa_flush(const int handle)
571 {
572     mm_sound_handle_t* phandle = NULL;
573     int err = MM_ERROR_NONE;
574
575     CHECK_HANDLE_RANGE(handle);
576     GET_HANDLE_DATA(phandle, mm_sound_handle_mgr.handles, &handle, __mm_sound_handle_comparefunc);
577     if(phandle == NULL)
578         return MM_ERROR_SOUND_INTERNAL;
579
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;
583     }
584
585     return err;
586 }
587
588 typedef struct _get_volume_max_userdata_t
589 {
590     pa_threaded_mainloop* mainloop;
591     int value;
592 } get_volume_max_userdata_t;