blob: e28ccff90a2272a503e63fa60f778f182850e7f9 [file] [log] [blame]
Bram Moolenaar427f5b62019-06-09 13:43:51 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * sound.c: functions related making noise
12 */
13
14#include "vim.h"
15
16#if (defined(FEAT_SOUND) && defined(HAVE_CANBERRA)) || defined(PROTO)
17
18#include <canberra.h>
19
20static long sound_id = 0;
21static ca_context *context = NULL;
22
23typedef struct soundcb_S soundcb_T;
24
25struct soundcb_S {
26 callback_T snd_callback;
27 soundcb_T *snd_next;
28};
29
30static soundcb_T *first_callback = NULL;
31
32 static soundcb_T *
33get_sound_callback(typval_T *arg)
34{
35 callback_T callback;
36 soundcb_T *soundcb;
37
38 if (arg->v_type == VAR_UNKNOWN)
39 return NULL;
40 callback = get_callback(arg);
41 if (callback.cb_name == NULL)
42 return NULL;
43
44 soundcb = ALLOC_ONE(soundcb_T);
45 if (soundcb == NULL)
46 free_callback(&callback);
47 else
48 {
49 soundcb->snd_next = first_callback;
50 first_callback = soundcb;
51 set_callback(&soundcb->snd_callback, &callback);
52 }
53 return soundcb;
54}
55
56/*
57 * Delete "soundcb" from the list of pending callbacks.
58 */
59 static void
60delete_sound_callback(soundcb_T *soundcb)
61{
62 soundcb_T *p;
63 soundcb_T *prev = NULL;
64
65 for (p = first_callback; p != NULL; prev = p, p = p->snd_next)
66 if (p == soundcb)
67 {
68 if (prev == NULL)
69 first_callback = p->snd_next;
70 else
71 prev->snd_next = p->snd_next;
72 free_callback(&p->snd_callback);
73 vim_free(p);
74 break;
75 }
76}
77
78 static void
79sound_callback(
80 ca_context *c UNUSED,
81 uint32_t id,
82 int error_code,
83 void *userdata)
84{
85 soundcb_T *soundcb = (soundcb_T *)userdata;
86 typval_T argv[3];
87 typval_T rettv;
88 int dummy;
89
90 argv[0].v_type = VAR_NUMBER;
91 argv[0].vval.v_number = id;
92 argv[1].v_type = VAR_NUMBER;
93 argv[1].vval.v_number = error_code == CA_SUCCESS ? 0
94 : error_code == CA_ERROR_CANCELED
95 || error_code == CA_ERROR_DESTROYED
96 ? 1 : 2;
97 argv[2].v_type = VAR_UNKNOWN;
98
99 call_callback(&soundcb->snd_callback, -1,
100 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, NULL);
101 clear_tv(&rettv);
102
103 delete_sound_callback(soundcb);
104 redraw_after_callback(TRUE);
105}
106
107 static void
108sound_play_common(typval_T *argvars, typval_T *rettv, int playfile)
109{
110 if (context == NULL)
111 ca_context_create(&context);
112 if (context != NULL)
113 {
114 soundcb_T *soundcb = get_sound_callback(&argvars[1]);
115 int res = CA_ERROR_INVALID;
116
117 ++sound_id;
118 if (soundcb == NULL)
119 {
120 res = ca_context_play(context, sound_id,
121 playfile ? CA_PROP_MEDIA_FILENAME : CA_PROP_EVENT_ID,
122 tv_get_string(&argvars[0]),
123 CA_PROP_CANBERRA_CACHE_CONTROL, "volatile",
124 NULL);
125 }
126 else
127 {
128 static ca_proplist *proplist = NULL;
129
130 ca_proplist_create(&proplist);
131 if (proplist != NULL)
132 {
133 if (playfile)
134 ca_proplist_sets(proplist, CA_PROP_MEDIA_FILENAME,
135 (char *)tv_get_string(&argvars[0]));
136 else
137 ca_proplist_sets(proplist, CA_PROP_EVENT_ID,
138 (char *)tv_get_string(&argvars[0]));
139 ca_proplist_sets(proplist, CA_PROP_CANBERRA_CACHE_CONTROL,
140 "volatile");
141 res = ca_context_play_full(context, sound_id, proplist,
142 sound_callback, soundcb);
143 if (res != CA_SUCCESS)
144 delete_sound_callback(soundcb);
145
146 ca_proplist_destroy(proplist);
147 }
148 }
149 rettv->vval.v_number = res == CA_SUCCESS ? sound_id : 0;
150 }
151}
152
153 void
154f_sound_playevent(typval_T *argvars, typval_T *rettv)
155{
156 sound_play_common(argvars, rettv, FALSE);
157}
158
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200159/*
160 * implementation of sound_playfile({path} [, {callback}])
161 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200162 void
163f_sound_playfile(typval_T *argvars, typval_T *rettv)
164{
165 sound_play_common(argvars, rettv, TRUE);
166}
167
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200168/*
169 * implementation of sound_stop({id})
170 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200171 void
172f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
173{
174 if (context != NULL)
175 ca_context_cancel(context, tv_get_number(&argvars[0]));
176}
177
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200178/*
179 * implementation of sound_clear()
180 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200181 void
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200182f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200183{
184 if (context != NULL)
185 {
186 ca_context_destroy(context);
187 context = NULL;
188 }
189}
190
191#if defined(EXITFREE) || defined(PROTO)
192 void
193sound_free(void)
194{
195 if (context != NULL)
196 ca_context_destroy(context);
197 while (first_callback != NULL)
198 delete_sound_callback(first_callback);
199}
200#endif
201
202#endif // FEAT_SOUND && HAVE_CANBERRA