blob: 7b4d98405ab371ce46ab618f15bc897bb772d431 [file] [log] [blame]
Luca Saccarola8ce738d2024-11-02 16:22:45 +01001/* vi:set ts=8 sts=4 sw=4 et:
Bram Moolenaar427f5b62019-06-09 13:43:51 +02002 *
Luca Saccarola8ce738d2024-11-02 16:22:45 +01003 * VIM - Vi IMproved by Bram Moolenaar
Bram Moolenaar427f5b62019-06-09 13:43:51 +02004 *
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
Bram Moolenaar9b283522019-06-17 22:19:33 +020016#if defined(FEAT_SOUND) || defined(PROTO)
Bram Moolenaar427f5b62019-06-09 13:43:51 +020017
Luca Saccarola3cf094e2024-11-19 22:55:13 +010018static long sound_id = 0;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020019
Christian Brabandtee17b6f2023-09-09 11:23:50 +020020// soundcb_T is typedef'ed in proto/sound.pro
Bram Moolenaar427f5b62019-06-09 13:43:51 +020021
Luca Saccarola3cf094e2024-11-19 22:55:13 +010022struct soundcb_S
23{
24 callback_T snd_callback;
25# ifdef MSWIN
Luca Saccarola8ce738d2024-11-02 16:22:45 +010026 MCIDEVICEID snd_device_id;
Luca Saccarola3cf094e2024-11-19 22:55:13 +010027 long snd_id;
28# endif
29 soundcb_T *snd_next;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020030};
31
Luca Saccarola3cf094e2024-11-19 22:55:13 +010032static soundcb_T *first_callback = NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020033
Bram Moolenaar28e67e02019-08-15 23:05:49 +020034/*
35 * Return TRUE when a sound callback has been created, it may be invoked when
36 * the sound finishes playing. Also see has_sound_callback_in_queue().
37 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +010038int
Bram Moolenaar28e67e02019-08-15 23:05:49 +020039has_any_sound_callback(void)
40{
41 return first_callback != NULL;
42}
43
Luca Saccarola3cf094e2024-11-19 22:55:13 +010044static soundcb_T *
Bram Moolenaar427f5b62019-06-09 13:43:51 +020045get_sound_callback(typval_T *arg)
46{
Luca Saccarola3cf094e2024-11-19 22:55:13 +010047 callback_T callback;
48 soundcb_T *soundcb;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020049
50 if (arg->v_type == VAR_UNKNOWN)
Hirohito Higashi38972d82025-05-06 18:13:29 +020051 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020052 callback = get_callback(arg);
53 if (callback.cb_name == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +020054 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020055
56 soundcb = ALLOC_ONE(soundcb_T);
57 if (soundcb == NULL)
Bram Moolenaar427f5b62019-06-09 13:43:51 +020058 {
Hirohito Higashi38972d82025-05-06 18:13:29 +020059 free_callback(&callback);
60 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020061 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000062
63 soundcb->snd_next = first_callback;
64 first_callback = soundcb;
65 set_callback(&soundcb->snd_callback, &callback);
66 if (callback.cb_free_name)
Hirohito Higashi38972d82025-05-06 18:13:29 +020067 vim_free(callback.cb_name);
Bram Moolenaar427f5b62019-06-09 13:43:51 +020068 return soundcb;
69}
70
71/*
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010072 * Call "soundcb" with proper parameters.
73 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +010074void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010075call_sound_callback(soundcb_T *soundcb, long snd_id, int result)
76{
Luca Saccarola3cf094e2024-11-19 22:55:13 +010077 typval_T argv[3];
78 typval_T rettv;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010079
80 argv[0].v_type = VAR_NUMBER;
81 argv[0].vval.v_number = snd_id;
82 argv[1].v_type = VAR_NUMBER;
83 argv[1].vval.v_number = result;
84 argv[2].v_type = VAR_UNKNOWN;
85
86 call_callback(&soundcb->snd_callback, -1, &rettv, 2, argv);
87 clear_tv(&rettv);
88}
89
90/*
Bram Moolenaar427f5b62019-06-09 13:43:51 +020091 * Delete "soundcb" from the list of pending callbacks.
92 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +010093void
Bram Moolenaar427f5b62019-06-09 13:43:51 +020094delete_sound_callback(soundcb_T *soundcb)
95{
Luca Saccarola3cf094e2024-11-19 22:55:13 +010096 soundcb_T *p;
97 soundcb_T *prev = NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020098
99 for (p = first_callback; p != NULL; prev = p, p = p->snd_next)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200100 if (p == soundcb)
101 {
102 if (prev == NULL)
103 first_callback = p->snd_next;
104 else
105 prev->snd_next = p->snd_next;
106 free_callback(&p->snd_callback);
107 vim_free(p);
108 break;
109 }
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200110}
111
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100112# if defined(HAVE_CANBERRA) || defined(PROTO)
Bram Moolenaar9b283522019-06-17 22:19:33 +0200113
114/*
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100115 * Sound implementation for Linux/Unix using libcanberra.
Bram Moolenaar9b283522019-06-17 22:19:33 +0200116 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100117# include <canberra.h>
Bram Moolenaar9b283522019-06-17 22:19:33 +0200118
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100119static ca_context *context = NULL;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200120
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200121// Structure to store info about a sound callback to be invoked soon.
122typedef struct soundcb_queue_S soundcb_queue_T;
123
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100124struct soundcb_queue_S
125{
126 soundcb_queue_T *scb_next;
127 uint32_t scb_id; // ID of the sound
128 int scb_result; // CA_ value
129 soundcb_T *scb_callback; // function to call
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200130};
131
132// Queue of callbacks to invoke from the main loop.
133static soundcb_queue_T *callback_queue = NULL;
134
135/*
136 * Add a callback to the queue of callbacks to invoke later from the main loop.
137 * That is because the callback may be called from another thread and invoking
138 * another sound function may cause trouble.
139 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100140static void
141sound_callback(ca_context *c UNUSED,
Hirohito Higashi38972d82025-05-06 18:13:29 +0200142 uint32_t id,
143 int error_code,
144 void *userdata)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200145{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100146 soundcb_T *soundcb = (soundcb_T *)userdata;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200147 soundcb_queue_T *scb;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200148
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200149 scb = ALLOC_ONE(soundcb_queue_T);
150 if (scb == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200151 return;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200152 scb->scb_next = callback_queue;
153 callback_queue = scb;
154 scb->scb_id = id;
155 scb->scb_result = error_code == CA_SUCCESS ? 0
Hirohito Higashi38972d82025-05-06 18:13:29 +0200156 : error_code == CA_ERROR_CANCELED ||
157 error_code == CA_ERROR_DESTROYED
158 ? 1
159 : 2;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200160 scb->scb_callback = soundcb;
161}
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200162
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200163/*
164 * Return TRUE if there is a sound callback to be called.
165 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100166int
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200167has_sound_callback_in_queue(void)
168{
169 return callback_queue != NULL;
170}
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200171
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200172/*
173 * Invoke queued sound callbacks.
174 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100175void
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200176invoke_sound_callback(void)
177{
178 soundcb_queue_T *scb;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200179
180 while (callback_queue != NULL)
181 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200182 scb = callback_queue;
183 callback_queue = scb->scb_next;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200184
Hirohito Higashi38972d82025-05-06 18:13:29 +0200185 call_sound_callback(scb->scb_callback, scb->scb_id, scb->scb_result);
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200186
Hirohito Higashi38972d82025-05-06 18:13:29 +0200187 delete_sound_callback(scb->scb_callback);
188 vim_free(scb);
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200189 }
Bram Moolenaare5050712021-12-09 10:51:05 +0000190 redraw_after_callback(TRUE, FALSE);
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200191}
192
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100193static void
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200194sound_play_common(typval_T *argvars, typval_T *rettv, int playfile)
195{
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200196 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200197 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200198
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200199 if (context == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200200 ca_context_create(&context);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000201 if (context == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200202 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000203
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100204 soundcb_T *soundcb = get_sound_callback(&argvars[1]);
205 int res = CA_ERROR_INVALID;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000206
207 ++sound_id;
208 if (soundcb == NULL)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200209 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200210 res = ca_context_play(context, sound_id,
211 playfile ? CA_PROP_MEDIA_FILENAME
212 : CA_PROP_EVENT_ID,
213 tv_get_string(&argvars[0]),
214 CA_PROP_CANBERRA_CACHE_CONTROL, "volatile", NULL);
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200215 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000216 else
217 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200218 static ca_proplist *proplist = NULL;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000219
Hirohito Higashi38972d82025-05-06 18:13:29 +0200220 ca_proplist_create(&proplist);
221 if (proplist != NULL)
222 {
223 if (playfile)
224 ca_proplist_sets(proplist, CA_PROP_MEDIA_FILENAME,
225 (char *)tv_get_string(&argvars[0]));
226 else
227 ca_proplist_sets(proplist, CA_PROP_EVENT_ID,
228 (char *)tv_get_string(&argvars[0]));
229 ca_proplist_sets(proplist, CA_PROP_CANBERRA_CACHE_CONTROL,
230 "volatile");
231 res = ca_context_play_full(context, sound_id, proplist,
232 sound_callback, soundcb);
233 if (res != CA_SUCCESS)
234 delete_sound_callback(soundcb);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000235
Hirohito Higashi38972d82025-05-06 18:13:29 +0200236 ca_proplist_destroy(proplist);
237 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000238 }
239 rettv->vval.v_number = res == CA_SUCCESS ? sound_id : 0;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200240}
241
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100242void
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200243f_sound_playevent(typval_T *argvars, typval_T *rettv)
244{
245 sound_play_common(argvars, rettv, FALSE);
246}
247
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200248/*
249 * implementation of sound_playfile({path} [, {callback}])
250 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100251void
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200252f_sound_playfile(typval_T *argvars, typval_T *rettv)
253{
254 sound_play_common(argvars, rettv, TRUE);
255}
256
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200257/*
258 * implementation of sound_stop({id})
259 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100260void
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200261f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
262{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200263 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200264 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200265
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200266 if (context != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200267 ca_context_cancel(context, tv_get_number(&argvars[0]));
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200268}
269
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200270/*
271 * implementation of sound_clear()
272 */
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100273void
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200274f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200275{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000276 if (context == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200277 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000278 ca_context_destroy(context);
279 context = NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200280}
281
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100282# if defined(EXITFREE) || defined(PROTO)
283void
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200284sound_free(void)
285{
Bram Moolenaar821d7712019-08-30 16:30:00 +0200286 soundcb_queue_T *scb;
287
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200288 if (context != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200289 ca_context_destroy(context);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200290
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200291 while (first_callback != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200292 delete_sound_callback(first_callback);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200293
294 while (callback_queue != NULL)
295 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200296 scb = callback_queue;
297 callback_queue = scb->scb_next;
298 delete_sound_callback(scb->scb_callback);
299 vim_free(scb);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200300 }
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200301}
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100302# endif
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200303
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100304# elif defined(MSWIN)
Bram Moolenaar9b283522019-06-17 22:19:33 +0200305
306/*
307 * Sound implementation for MS-Windows.
308 */
309
310static HWND g_hWndSound = NULL;
311
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100312static LRESULT CALLBACK
Bram Moolenaar9b283522019-06-17 22:19:33 +0200313sound_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
314{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100315 soundcb_T *p;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200316
317 switch (message)
318 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200319 case MM_MCINOTIFY:
320 for (p = first_callback; p != NULL; p = p->snd_next)
321 if (p->snd_device_id == (MCIDEVICEID)lParam)
322 {
323 char buf[32];
Bram Moolenaar9b283522019-06-17 22:19:33 +0200324
Hirohito Higashi38972d82025-05-06 18:13:29 +0200325 vim_snprintf(buf, sizeof(buf), "close sound%06ld",
326 p->snd_id);
327 mciSendStringA(buf, NULL, 0, 0);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200328
Hirohito Higashi38972d82025-05-06 18:13:29 +0200329 long result = wParam == MCI_NOTIFY_SUCCESSFUL ? 0
330 : wParam == MCI_NOTIFY_ABORTED ? 1
331 : 2;
332 call_sound_callback(p, p->snd_id, result);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200333
Hirohito Higashi38972d82025-05-06 18:13:29 +0200334 delete_sound_callback(p);
335 redraw_after_callback(TRUE, FALSE);
336 }
337 break;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200338 }
339
340 return DefWindowProc(hwnd, message, wParam, lParam);
341}
342
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100343static HWND
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000344sound_window(void)
Bram Moolenaar9b283522019-06-17 22:19:33 +0200345{
346 if (g_hWndSound == NULL)
347 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200348 LPCSTR clazz = "VimSound";
349 WNDCLASS wndclass = { 0, sound_wndproc, 0, 0, g_hinst, NULL, 0,
350 0, NULL, clazz };
351 RegisterClass(&wndclass);
352 g_hWndSound = CreateWindow(clazz, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE,
353 NULL, g_hinst, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200354 }
355
356 return g_hWndSound;
357}
358
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100359void
Bram Moolenaar9b283522019-06-17 22:19:33 +0200360f_sound_playevent(typval_T *argvars, typval_T *rettv)
361{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100362 WCHAR *wp;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200363
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200364 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200365 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200366
Bram Moolenaar9b283522019-06-17 22:19:33 +0200367 wp = enc_to_utf16(tv_get_string(&argvars[0]), NULL);
368 if (wp == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200369 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200370
Dominique Pelle2f9c2092021-06-07 20:28:45 +0200371 if (PlaySoundW(wp, NULL, SND_ASYNC | SND_ALIAS))
Hirohito Higashi38972d82025-05-06 18:13:29 +0200372 rettv->vval.v_number = ++sound_id;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200373 free(wp);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200374}
375
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100376void
Bram Moolenaar9b283522019-06-17 22:19:33 +0200377f_sound_playfile(typval_T *argvars, typval_T *rettv)
378{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100379 long newid = sound_id + 1;
380 size_t len;
381 char_u *p, *filename;
382 WCHAR *wp;
383 soundcb_T *soundcb;
384 char buf[32];
385 MCIERROR err;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200386
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200387 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200388 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200389
GuyBrush15d27002023-11-04 09:48:53 +0100390 filename = tv_get_string(&argvars[0]);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200391
GuyBrush15d27002023-11-04 09:48:53 +0100392 len = STRLEN(filename) + 5 + 18 + 2 + 1;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200393 p = alloc(len);
394 if (p == NULL)
395 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200396 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200397 }
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100398 vim_snprintf((char *)p, len, "open \"%s\" alias sound%06ld", filename,
Hirohito Higashi38972d82025-05-06 18:13:29 +0200399 newid);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200400
401 wp = enc_to_utf16((char_u *)p, NULL);
402 free(p);
403 if (wp == NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200404 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200405
406 err = mciSendStringW(wp, NULL, 0, sound_window());
407 free(wp);
408 if (err != 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200409 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200410
411 vim_snprintf(buf, sizeof(buf), "play sound%06ld notify", newid);
GuyBrush15d27002023-11-04 09:48:53 +0100412 err = mciSendStringA(buf, NULL, 0, sound_window());
Bram Moolenaar9b283522019-06-17 22:19:33 +0200413 if (err != 0)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200414 goto failure;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200415
416 sound_id = newid;
417 rettv->vval.v_number = sound_id;
418
419 soundcb = get_sound_callback(&argvars[1]);
420 if (soundcb != NULL)
421 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200422 vim_snprintf(buf, sizeof(buf), "sound%06ld", newid);
423 soundcb->snd_id = newid;
424 soundcb->snd_device_id = mciGetDeviceID(buf);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200425 }
426 return;
427
428failure:
429 vim_snprintf(buf, sizeof(buf), "close sound%06ld", newid);
GuyBrush15d27002023-11-04 09:48:53 +0100430 mciSendStringA(buf, NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200431}
432
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100433void
Bram Moolenaar9b283522019-06-17 22:19:33 +0200434f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
435{
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100436 long id;
437 char buf[32];
Bram Moolenaar9b283522019-06-17 22:19:33 +0200438
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200439 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200440 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200441
442 id = tv_get_number(&argvars[0]);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200443 vim_snprintf(buf, sizeof(buf), "stop sound%06ld", id);
GuyBrush15d27002023-11-04 09:48:53 +0100444 mciSendStringA(buf, NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200445}
446
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100447void
Bram Moolenaar9b283522019-06-17 22:19:33 +0200448f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
449{
Bram Moolenaar1b33bee2019-09-07 14:50:49 +0200450 PlaySoundW(NULL, NULL, 0);
GuyBrush15d27002023-11-04 09:48:53 +0100451 mciSendStringA("close all", NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200452}
453
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100454# if defined(EXITFREE)
455void
Bram Moolenaar9b283522019-06-17 22:19:33 +0200456sound_free(void)
457{
458 CloseWindow(g_hWndSound);
459
460 while (first_callback != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200461 delete_sound_callback(first_callback);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200462}
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100463# endif
Bram Moolenaar9b283522019-06-17 22:19:33 +0200464
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100465# elif defined(MACOS_X_DARWIN)
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100466
467// Sound implementation for macOS.
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100468static void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100469sound_play_common(typval_T *argvars, typval_T *rettv, bool playfile)
470{
471 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200472 return;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100473
474 char_u *sound_name = tv_get_string(&argvars[0]);
475 soundcb_T *soundcb = get_sound_callback(&argvars[1]);
476
477 ++sound_id;
478
479 bool play_success = sound_mch_play(sound_name, sound_id, soundcb, playfile);
480 if (!play_success && soundcb)
481 {
Hirohito Higashi38972d82025-05-06 18:13:29 +0200482 delete_sound_callback(soundcb);
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100483 }
484 rettv->vval.v_number = play_success ? sound_id : 0;
485}
486
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100487void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100488f_sound_playevent(typval_T *argvars, typval_T *rettv)
489{
490 sound_play_common(argvars, rettv, false);
491}
492
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100493void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100494f_sound_playfile(typval_T *argvars, typval_T *rettv)
495{
496 sound_play_common(argvars, rettv, true);
497}
498
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100499void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100500f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
501{
502 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200503 return;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100504 sound_mch_stop(tv_get_number(&argvars[0]));
505}
506
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100507void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100508f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
509{
510 sound_mch_clear();
511}
512
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100513# if defined(EXITFREE) || defined(PROTO)
514void
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100515sound_free(void)
516{
517 sound_mch_free();
518 while (first_callback != NULL)
Hirohito Higashi38972d82025-05-06 18:13:29 +0200519 delete_sound_callback(first_callback);
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100520}
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100521# endif
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100522
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100523# endif // MACOS_X_DARWIN
Bram Moolenaar9b283522019-06-17 22:19:33 +0200524
Luca Saccarola3cf094e2024-11-19 22:55:13 +0100525#endif // FEAT_SOUND