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.
33 #include "include/mm_sound_common.h"
37 #define PULSEAUDIO_READY "/tmp/.pulseaudio_ready"
40 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY "SimplePlay"
41 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP "SimpleStop"
42 #define PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL "SimpleStopAll"
44 #define AUDIO_VOLUME_CONFIG_TYPE(vol) (vol & 0x00FF)
45 #define AUDIO_VOLUME_CONFIG_GAIN(vol) (vol & 0xFF00)
47 #define FILENAME_LEN 1024
48 #define MIN_KEYTONE_INTERVAL_US 30000
49 #define NO_PRIORITY (-1)
51 #define USE_PIPE /* default is pipe now */
55 #define KEYTONE_PATH "/tmp/keytone"
57 #define VOLUME_GAIN_TYPE_LEN 32
60 #define MAX_WRITE_RETRY 50
61 #define WRITE_RETRY_INTERVAL_MS 20
63 #define MM_SOUND_SIMPLE __mm_sound_simple_pipe
65 typedef struct ipc_data {
66 char filename[FILENAME_LEN];
68 char volume_gain_type[VOLUME_GAIN_TYPE_LEN];
69 char method[METHOD_LEN];
70 int priority; /* lower as a higher priority, -1 for no priority (= don't care) */
73 static int __mm_sound_simple_pipe(const char *filename, int volume_config, int priority, const char *method);
79 #define PA_BUS_NAME "org.pulseaudio.Server"
80 #define PA_SOUND_PLAYER_OBJECT_PATH "/org/pulseaudio/SoundPlayer"
81 #define PA_SOUND_PLAYER_INTERFACE "org.pulseaudio.SoundPlayer"
82 #define PA_SOUND_RESPONSE_TIMEOUT 5000
84 #define MM_SOUND_SIMPLE __mm_sound_simple_dbus
86 static int __mm_sound_simple_dbus(const char *filename, int volume_config, int priority, const char *method);
89 static const char* __convert_volume_type_to_role(int volume_type)
91 debug_log("volume_type(%d)", volume_type);
92 switch (volume_type) {
93 case VOLUME_TYPE_MEDIA:
95 case VOLUME_TYPE_SYSTEM:
97 case VOLUME_TYPE_NOTIFICATION:
98 return "notification";
99 case VOLUME_TYPE_ALARM:
101 case VOLUME_TYPE_VOICE:
103 case VOLUME_TYPE_RINGTONE:
104 return "ringtone-call";
106 debug_warning("not supported type(%d), we change it SYSTEM type forcibly", volume_type);
111 static const char* __convert_volume_gain_type_to_string(int volume_gain_type)
113 debug_log("volume_gain_type(0x%x)", volume_gain_type);
114 switch (volume_gain_type) {
115 case VOLUME_GAIN_DEFAULT:
117 case VOLUME_GAIN_DIALER:
119 case VOLUME_GAIN_TOUCH:
123 case VOLUME_GAIN_SHUTTER1:
125 case VOLUME_GAIN_SHUTTER2:
127 case VOLUME_GAIN_CAMCORDING:
129 case VOLUME_GAIN_MIDI:
131 case VOLUME_GAIN_BOOTING:
133 case VOLUME_GAIN_VIDEO:
135 case VOLUME_GAIN_TTS:
142 static uint64_t __elapsed_time_us(struct timespec *start, struct timespec *end)
144 uint64_t start_us = (uint64_t) start->tv_sec * 1000000 +
145 (uint64_t) start->tv_nsec / 1000;
146 uint64_t end_us = (uint64_t) end->tv_sec * 1000000 +
147 (uint64_t) end->tv_nsec / 1000;
149 return end_us - start_us;
152 static bool __is_interval_too_short(const char *filename)
154 static struct timespec ts_last_played = { .tv_sec = 0, .tv_nsec = 0 };
155 static char file_last_played[FILENAME_LEN] = { '\0', };
157 struct timespec ts_now;
158 clock_gettime(CLOCK_MONOTONIC, &ts_now);
160 if (g_strcmp0(file_last_played, filename) == 0 &&
161 __elapsed_time_us(&ts_last_played, &ts_now) < MIN_KEYTONE_INTERVAL_US) {
165 g_strlcpy(file_last_played, filename, sizeof(file_last_played));
166 ts_last_played = ts_now;
172 int mm_sound_play_keysound_priority(const char *filename, int volume_config, int priority)
175 if (__is_interval_too_short(filename)) {
176 debug_warning("Too short time (< %u us) to play same file, try next time again...", MIN_KEYTONE_INTERVAL_US);
177 return MM_ERROR_NONE;
181 return MM_SOUND_SIMPLE(filename, volume_config, priority, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY);
185 int mm_sound_play_keysound(const char *filename, int volume_config)
187 return mm_sound_play_keysound_priority(filename, volume_config, NO_PRIORITY);
191 int mm_sound_stop_keysound(const char *filename)
193 return MM_SOUND_SIMPLE(filename, 0, NO_PRIORITY,
194 filename ? PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP : PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL);
198 static bool _mm_sound_check_pa_ready()
201 static bool is_pa_ready = false;
206 debug_msg("LwipcIsDone start >> ");
207 ret = LwipcIsDone(PULSEAUDIO_READY);
208 debug_msg("LwipcIsDone end << %d", ret);
210 is_pa_ready = (ret > 0) ? true : false;
216 static int __verify_input_file(const char *filename)
219 return MM_ERROR_SOUND_INVALID_FILE;
221 /* Check whether file exists */
222 if (access(filename, F_OK) == -1) {
225 strerror_r(errno, str_error, sizeof(str_error));
226 debug_error("file [%s] doesn't exists : [%s][%d]", filename, str_error, errno);
228 return MM_ERROR_SOUND_FILE_NOT_FOUND;
231 return MM_ERROR_NONE;
235 static int __mm_sound_simple_pipe(const char *filename, int volume_config, int priority, const char *method)
237 int ret = MM_ERROR_NONE;
238 int retry_remaining = MAX_WRITE_RETRY;
239 size_t size_to_write = sizeof(ipc_t);
245 if (!_mm_sound_check_pa_ready()) {
246 debug_error("Pulseaudio is not ready!");
247 return MM_ERROR_SOUND_INVALID_STATE;
252 return MM_ERROR_INVALID_ARGUMENT;
254 /* Prepare data to be send */
255 g_strlcpy(data.method, method, METHOD_LEN);
257 if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL) != 0) {
258 ret = __verify_input_file(filename);
259 if (ret != MM_ERROR_NONE)
262 g_strlcpy(data.filename, filename, FILENAME_LEN);
265 if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY) == 0) {
267 __convert_volume_type_to_role(AUDIO_VOLUME_CONFIG_TYPE(volume_config)),
269 g_strlcpy(data.volume_gain_type,
270 __convert_volume_gain_type_to_string(AUDIO_VOLUME_CONFIG_GAIN(volume_config)),
271 VOLUME_GAIN_TYPE_LEN);
272 data.priority = priority;
275 debug_msg("filepath=[%s], role=[%s], volume_gain_type=[%s], priority=[%d], method=[%s]",
276 data.filename, data.role, data.volume_gain_type, data.priority, data.method);
279 fd = open(KEYTONE_PATH, O_WRONLY | O_NONBLOCK);
283 strerror_r(errno, str_error, sizeof(str_error));
284 debug_error("Fail to open pipe: [%s][%d]", str_error, errno);
286 return MM_ERROR_SOUND_INTERNAL;
291 written = write(fd, &data, size_to_write);
296 strerror_r(errsv, str_error, sizeof(str_error));
297 debug_error("[%d] Fail to write, written = %zd, [%s][%d]",
298 MAX_WRITE_RETRY - retry_remaining, written, str_error, errsv);
300 if (errsv != EAGAIN || --retry_remaining == 0) {
301 ret = MM_ERROR_SOUND_INTERNAL;
305 usleep(WRITE_RETRY_INTERVAL_MS * 1000);
306 } else if (retry_remaining != MAX_WRITE_RETRY) {
307 debug_msg("retry success!!");
309 } while (written != size_to_write);
318 static int __mm_sound_simple_dbus(const char *filename, int volume_config, int priority, const char *method)
320 int ret = MM_ERROR_NONE;
321 const char *role = NULL;
322 const char *vol_gain_type = NULL;
323 GVariant *result = NULL;
324 GDBusConnection *conn = NULL;
329 if (!_mm_sound_check_pa_ready()) {
330 debug_error("Pulseaudio is not ready!");
331 return MM_ERROR_SOUND_INVALID_STATE;
335 return MM_ERROR_INVALID_ARGUMENT;
337 conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
339 debug_error("g_bus_get_sync() error (%s)", err ? err->message : NULL);
341 return MM_ERROR_SOUND_INTERNAL;
344 if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY) == 0) {
345 ret = __verify_input_file(filename);
346 if (ret != MM_ERROR_NONE)
349 role = __convert_volume_type_to_role(AUDIO_VOLUME_CONFIG_TYPE(volume_config));
350 vol_gain_type = __convert_volume_gain_type_to_string(AUDIO_VOLUME_CONFIG_GAIN(volume_config));
352 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
353 PA_SOUND_PLAYER_OBJECT_PATH,
354 PA_SOUND_PLAYER_INTERFACE,
355 PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_PLAY,
356 g_variant_new("(sssi)", filename, role, vol_gain_type, priority),
357 NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
358 if (!result || err) {
359 debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
360 ret = MM_ERROR_SOUND_INTERNAL;
364 g_variant_get(result, "(i)", &idx);
366 debug_error("[%s] failure, filename(%s)/role(%s)/gain(%s)/priority(%d)/stream idx(%d)",
367 method, filename, role, vol_gain_type, priority, idx);
368 ret = MM_ERROR_SOUND_INTERNAL;
371 debug_msg("[%s] success, filename(%s)/role(%s)/gain(%s)/priority(%d),stream idx(%d)",
372 method, filename, role, vol_gain_type, priority, idx);
374 } else if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP) == 0) {
375 ret = __verify_input_file(filename);
376 if (ret != MM_ERROR_NONE)
379 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
380 PA_SOUND_PLAYER_OBJECT_PATH,
381 PA_SOUND_PLAYER_INTERFACE,
382 PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP,
383 g_variant_new("(s)", filename),
384 NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
385 if (!result || err) {
386 debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
387 ret = MM_ERROR_SOUND_INTERNAL;
390 debug_msg("[%s] done, filename(%s)", method, filename);
392 } else if (g_strcmp0(method, PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL) == 0) {
393 result = g_dbus_connection_call_sync(conn, PA_BUS_NAME,
394 PA_SOUND_PLAYER_OBJECT_PATH,
395 PA_SOUND_PLAYER_INTERFACE,
396 PA_SOUND_PLAYER_METHOD_NAME_SIMPLE_STOP_ALL,
398 NULL, G_DBUS_CALL_FLAGS_NONE, PA_SOUND_RESPONSE_TIMEOUT, NULL, &err);
399 if (!result || err) {
400 debug_error("g_dbus_connection_call_sync() for %s error (%s)", method, err ? err->message : NULL);
401 ret = MM_ERROR_SOUND_INTERNAL;
404 debug_msg("[%s] done", method);
407 debug_error("Invalid Method : %s", method);
408 ret = MM_ERROR_INVALID_ARGUMENT;
415 g_variant_unref(result);
417 g_object_unref(conn);
421 #endif /* USE_PIPE */