blob: 04a16513f5898068a6777b6305529db709194cf7 [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 Saccarola8ce738d2024-11-02 16:22:45 +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
22struct soundcb_S {
Luca Saccarola8ce738d2024-11-02 16:22:45 +010023 callback_T snd_callback;
Bram Moolenaar9b283522019-06-17 22:19:33 +020024#ifdef MSWIN
Luca Saccarola8ce738d2024-11-02 16:22:45 +010025 MCIDEVICEID snd_device_id;
26 long snd_id;
Bram Moolenaar9b283522019-06-17 22:19:33 +020027#endif
Luca Saccarola8ce738d2024-11-02 16:22:45 +010028 soundcb_T *snd_next;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020029};
30
31static soundcb_T *first_callback = NULL;
32
Bram Moolenaar28e67e02019-08-15 23:05:49 +020033/*
34 * Return TRUE when a sound callback has been created, it may be invoked when
35 * the sound finishes playing. Also see has_sound_callback_in_queue().
36 */
37 int
38has_any_sound_callback(void)
39{
40 return first_callback != NULL;
41}
42
Bram Moolenaar427f5b62019-06-09 13:43:51 +020043 static soundcb_T *
44get_sound_callback(typval_T *arg)
45{
Luca Saccarola8ce738d2024-11-02 16:22:45 +010046 callback_T callback;
47 soundcb_T *soundcb;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020048
49 if (arg->v_type == VAR_UNKNOWN)
Luca Saccarola8ce738d2024-11-02 16:22:45 +010050 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020051 callback = get_callback(arg);
52 if (callback.cb_name == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +010053 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020054
55 soundcb = ALLOC_ONE(soundcb_T);
56 if (soundcb == NULL)
Bram Moolenaar427f5b62019-06-09 13:43:51 +020057 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +010058 free_callback(&callback);
59 return NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020060 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +000061
62 soundcb->snd_next = first_callback;
63 first_callback = soundcb;
64 set_callback(&soundcb->snd_callback, &callback);
65 if (callback.cb_free_name)
Luca Saccarola8ce738d2024-11-02 16:22:45 +010066 vim_free(callback.cb_name);
Bram Moolenaar427f5b62019-06-09 13:43:51 +020067 return soundcb;
68}
69
70/*
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010071 * Call "soundcb" with proper parameters.
72 */
73 void
74call_sound_callback(soundcb_T *soundcb, long snd_id, int result)
75{
Luca Saccarola8ce738d2024-11-02 16:22:45 +010076 typval_T argv[3];
77 typval_T rettv;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010078
79 argv[0].v_type = VAR_NUMBER;
80 argv[0].vval.v_number = snd_id;
81 argv[1].v_type = VAR_NUMBER;
82 argv[1].vval.v_number = result;
83 argv[2].v_type = VAR_UNKNOWN;
84
85 call_callback(&soundcb->snd_callback, -1, &rettv, 2, argv);
86 clear_tv(&rettv);
87}
88
89/*
Bram Moolenaar427f5b62019-06-09 13:43:51 +020090 * Delete "soundcb" from the list of pending callbacks.
91 */
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +010092 void
Bram Moolenaar427f5b62019-06-09 13:43:51 +020093delete_sound_callback(soundcb_T *soundcb)
94{
Luca Saccarola8ce738d2024-11-02 16:22:45 +010095 soundcb_T *p;
96 soundcb_T *prev = NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +020097
98 for (p = first_callback; p != NULL; prev = p, p = p->snd_next)
Luca Saccarola8ce738d2024-11-02 16:22:45 +010099 if (p == soundcb)
100 {
101 if (prev == NULL)
102 first_callback = p->snd_next;
103 else
104 prev->snd_next = p->snd_next;
105 free_callback(&p->snd_callback);
106 vim_free(p);
107 break;
108 }
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200109}
110
Bram Moolenaar9b283522019-06-17 22:19:33 +0200111#if defined(HAVE_CANBERRA) || defined(PROTO)
112
113/*
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100114 * Sound implementation for Linux/Unix using libcanberra.
Bram Moolenaar9b283522019-06-17 22:19:33 +0200115 */
116# include <canberra.h>
117
118static ca_context *context = NULL;
119
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200120// Structure to store info about a sound callback to be invoked soon.
121typedef struct soundcb_queue_S soundcb_queue_T;
122
123struct soundcb_queue_S {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100124 soundcb_queue_T *scb_next;
125 uint32_t scb_id; // ID of the sound
126 int scb_result; // CA_ value
127 soundcb_T *scb_callback; // function to call
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200128};
129
130// Queue of callbacks to invoke from the main loop.
131static soundcb_queue_T *callback_queue = NULL;
132
133/*
134 * Add a callback to the queue of callbacks to invoke later from the main loop.
135 * That is because the callback may be called from another thread and invoking
136 * another sound function may cause trouble.
137 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200138 static void
139sound_callback(
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100140 ca_context *c UNUSED,
141 uint32_t id,
142 int error_code,
143 void *userdata)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200144{
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100145 soundcb_T *soundcb = (soundcb_T *)userdata;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200146 soundcb_queue_T *scb;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200147
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200148 scb = ALLOC_ONE(soundcb_queue_T);
149 if (scb == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100150 return;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200151 scb->scb_next = callback_queue;
152 callback_queue = scb;
153 scb->scb_id = id;
154 scb->scb_result = error_code == CA_SUCCESS ? 0
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100155 : error_code == CA_ERROR_CANCELED
156 || error_code == CA_ERROR_DESTROYED
157 ? 1 : 2;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200158 scb->scb_callback = soundcb;
159}
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200160
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200161/*
162 * Return TRUE if there is a sound callback to be called.
163 */
164 int
165has_sound_callback_in_queue(void)
166{
167 return callback_queue != NULL;
168}
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200169
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200170/*
171 * Invoke queued sound callbacks.
172 */
173 void
174invoke_sound_callback(void)
175{
176 soundcb_queue_T *scb;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200177
178 while (callback_queue != NULL)
179 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100180 scb = callback_queue;
181 callback_queue = scb->scb_next;
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200182
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100183 call_sound_callback(scb->scb_callback, scb->scb_id, scb->scb_result);
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200184
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100185 delete_sound_callback(scb->scb_callback);
186 vim_free(scb);
Bram Moolenaar28e67e02019-08-15 23:05:49 +0200187 }
Bram Moolenaare5050712021-12-09 10:51:05 +0000188 redraw_after_callback(TRUE, FALSE);
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200189}
190
191 static void
192sound_play_common(typval_T *argvars, typval_T *rettv, int playfile)
193{
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200194 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100195 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200196
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200197 if (context == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100198 ca_context_create(&context);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000199 if (context == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100200 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000201
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100202 soundcb_T *soundcb = get_sound_callback(&argvars[1]);
203 int res = CA_ERROR_INVALID;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000204
205 ++sound_id;
206 if (soundcb == NULL)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200207 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100208 res = ca_context_play(context, sound_id,
209 playfile ? CA_PROP_MEDIA_FILENAME : CA_PROP_EVENT_ID,
210 tv_get_string(&argvars[0]),
211 CA_PROP_CANBERRA_CACHE_CONTROL, "volatile",
212 NULL);
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200213 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000214 else
215 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100216 static ca_proplist *proplist = NULL;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000217
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100218 ca_proplist_create(&proplist);
219 if (proplist != NULL)
220 {
221 if (playfile)
222 ca_proplist_sets(proplist, CA_PROP_MEDIA_FILENAME,
223 (char *)tv_get_string(&argvars[0]));
224 else
225 ca_proplist_sets(proplist, CA_PROP_EVENT_ID,
226 (char *)tv_get_string(&argvars[0]));
227 ca_proplist_sets(proplist, CA_PROP_CANBERRA_CACHE_CONTROL,
228 "volatile");
229 res = ca_context_play_full(context, sound_id, proplist,
230 sound_callback, soundcb);
231 if (res != CA_SUCCESS)
232 delete_sound_callback(soundcb);
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000233
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100234 ca_proplist_destroy(proplist);
235 }
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000236 }
237 rettv->vval.v_number = res == CA_SUCCESS ? sound_id : 0;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200238}
239
240 void
241f_sound_playevent(typval_T *argvars, typval_T *rettv)
242{
243 sound_play_common(argvars, rettv, FALSE);
244}
245
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200246/*
247 * implementation of sound_playfile({path} [, {callback}])
248 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200249 void
250f_sound_playfile(typval_T *argvars, typval_T *rettv)
251{
252 sound_play_common(argvars, rettv, TRUE);
253}
254
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200255/*
256 * implementation of sound_stop({id})
257 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200258 void
259f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
260{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200261 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100262 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200263
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200264 if (context != NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100265 ca_context_cancel(context, tv_get_number(&argvars[0]));
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200266}
267
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200268/*
269 * implementation of sound_clear()
270 */
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200271 void
Bram Moolenaar3ff5f0f2019-06-10 13:11:22 +0200272f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200273{
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000274 if (context == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100275 return;
Yegappan Lakshmanan6ec66662023-01-23 20:46:21 +0000276 ca_context_destroy(context);
277 context = NULL;
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200278}
279
Bram Moolenaar9b283522019-06-17 22:19:33 +0200280# if defined(EXITFREE) || defined(PROTO)
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200281 void
282sound_free(void)
283{
Bram Moolenaar821d7712019-08-30 16:30:00 +0200284 soundcb_queue_T *scb;
285
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200286 if (context != NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100287 ca_context_destroy(context);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200288
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200289 while (first_callback != NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100290 delete_sound_callback(first_callback);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200291
292 while (callback_queue != NULL)
293 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100294 scb = callback_queue;
295 callback_queue = scb->scb_next;
296 delete_sound_callback(scb->scb_callback);
297 vim_free(scb);
Bram Moolenaar821d7712019-08-30 16:30:00 +0200298 }
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200299}
Bram Moolenaar9b283522019-06-17 22:19:33 +0200300# endif
Bram Moolenaar427f5b62019-06-09 13:43:51 +0200301
Bram Moolenaar9b283522019-06-17 22:19:33 +0200302#elif defined(MSWIN)
303
304/*
305 * Sound implementation for MS-Windows.
306 */
307
308static HWND g_hWndSound = NULL;
309
310 static LRESULT CALLBACK
311sound_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
312{
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100313 soundcb_T *p;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200314
315 switch (message)
316 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100317 case MM_MCINOTIFY:
318 for (p = first_callback; p != NULL; p = p->snd_next)
319 if (p->snd_device_id == (MCIDEVICEID) lParam)
320 {
321 char buf[32];
Bram Moolenaar9b283522019-06-17 22:19:33 +0200322
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100323 vim_snprintf(buf, sizeof(buf), "close sound%06ld",
324 p->snd_id);
325 mciSendStringA(buf, NULL, 0, 0);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200326
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100327 long result = wParam == MCI_NOTIFY_SUCCESSFUL ? 0
328 : wParam == MCI_NOTIFY_ABORTED ? 1 : 2;
329 call_sound_callback(p, p->snd_id, result);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200330
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100331 delete_sound_callback(p);
332 redraw_after_callback(TRUE, FALSE);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200333
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100334 }
335 break;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200336 }
337
338 return DefWindowProc(hwnd, message, wParam, lParam);
339}
340
341 static HWND
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000342sound_window(void)
Bram Moolenaar9b283522019-06-17 22:19:33 +0200343{
344 if (g_hWndSound == NULL)
345 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100346 LPCSTR clazz = "VimSound";
347 WNDCLASS wndclass = {
348 0, sound_wndproc, 0, 0, g_hinst, NULL, 0, 0, NULL, clazz };
349 RegisterClass(&wndclass);
350 g_hWndSound = CreateWindow(clazz, NULL, 0, 0, 0, 0, 0,
351 HWND_MESSAGE, NULL, g_hinst, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200352 }
353
354 return g_hWndSound;
355}
356
357 void
358f_sound_playevent(typval_T *argvars, typval_T *rettv)
359{
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100360 WCHAR *wp;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200361
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200362 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100363 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200364
Bram Moolenaar9b283522019-06-17 22:19:33 +0200365 wp = enc_to_utf16(tv_get_string(&argvars[0]), NULL);
366 if (wp == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100367 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200368
Dominique Pelle2f9c2092021-06-07 20:28:45 +0200369 if (PlaySoundW(wp, NULL, SND_ASYNC | SND_ALIAS))
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100370 rettv->vval.v_number = ++sound_id;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200371 free(wp);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200372}
373
374 void
375f_sound_playfile(typval_T *argvars, typval_T *rettv)
376{
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100377 long newid = sound_id + 1;
378 size_t len;
379 char_u *p, *filename;
380 WCHAR *wp;
381 soundcb_T *soundcb;
382 char buf[32];
383 MCIERROR err;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200384
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200385 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100386 return;
Yegappan Lakshmanan5bca9062021-07-24 21:33:26 +0200387
GuyBrush15d27002023-11-04 09:48:53 +0100388 filename = tv_get_string(&argvars[0]);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200389
GuyBrush15d27002023-11-04 09:48:53 +0100390 len = STRLEN(filename) + 5 + 18 + 2 + 1;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200391 p = alloc(len);
392 if (p == NULL)
393 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100394 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200395 }
GuyBrush15d27002023-11-04 09:48:53 +0100396 vim_snprintf((char *)p, len, "open \"%s\" alias sound%06ld", filename, newid);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200397
398 wp = enc_to_utf16((char_u *)p, NULL);
399 free(p);
400 if (wp == NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100401 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200402
403 err = mciSendStringW(wp, NULL, 0, sound_window());
404 free(wp);
405 if (err != 0)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100406 return;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200407
408 vim_snprintf(buf, sizeof(buf), "play sound%06ld notify", newid);
GuyBrush15d27002023-11-04 09:48:53 +0100409 err = mciSendStringA(buf, NULL, 0, sound_window());
Bram Moolenaar9b283522019-06-17 22:19:33 +0200410 if (err != 0)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100411 goto failure;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200412
413 sound_id = newid;
414 rettv->vval.v_number = sound_id;
415
416 soundcb = get_sound_callback(&argvars[1]);
417 if (soundcb != NULL)
418 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100419 vim_snprintf(buf, sizeof(buf), "sound%06ld", newid);
420 soundcb->snd_id = newid;
421 soundcb->snd_device_id = mciGetDeviceID(buf);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200422 }
423 return;
424
425failure:
426 vim_snprintf(buf, sizeof(buf), "close sound%06ld", newid);
GuyBrush15d27002023-11-04 09:48:53 +0100427 mciSendStringA(buf, NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200428}
429
430 void
431f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
432{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200433 long id;
Bram Moolenaar9b283522019-06-17 22:19:33 +0200434 char buf[32];
435
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200436 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100437 return;
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +0200438
439 id = tv_get_number(&argvars[0]);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200440 vim_snprintf(buf, sizeof(buf), "stop sound%06ld", id);
GuyBrush15d27002023-11-04 09:48:53 +0100441 mciSendStringA(buf, NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200442}
443
444 void
445f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
446{
Bram Moolenaar1b33bee2019-09-07 14:50:49 +0200447 PlaySoundW(NULL, NULL, 0);
GuyBrush15d27002023-11-04 09:48:53 +0100448 mciSendStringA("close all", NULL, 0, NULL);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200449}
450
451# if defined(EXITFREE)
452 void
453sound_free(void)
454{
455 CloseWindow(g_hWndSound);
456
457 while (first_callback != NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100458 delete_sound_callback(first_callback);
Bram Moolenaar9b283522019-06-17 22:19:33 +0200459}
460# endif
461
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100462#elif defined(MACOS_X_DARWIN)
463
464// Sound implementation for macOS.
465 static void
466sound_play_common(typval_T *argvars, typval_T *rettv, bool playfile)
467{
468 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100469 return;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100470
471 char_u *sound_name = tv_get_string(&argvars[0]);
472 soundcb_T *soundcb = get_sound_callback(&argvars[1]);
473
474 ++sound_id;
475
476 bool play_success = sound_mch_play(sound_name, sound_id, soundcb, playfile);
477 if (!play_success && soundcb)
478 {
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100479 delete_sound_callback(soundcb);
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100480 }
481 rettv->vval.v_number = play_success ? sound_id : 0;
482}
483
484 void
485f_sound_playevent(typval_T *argvars, typval_T *rettv)
486{
487 sound_play_common(argvars, rettv, false);
488}
489
490 void
491f_sound_playfile(typval_T *argvars, typval_T *rettv)
492{
493 sound_play_common(argvars, rettv, true);
494}
495
496 void
497f_sound_stop(typval_T *argvars, typval_T *rettv UNUSED)
498{
499 if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100500 return;
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100501 sound_mch_stop(tv_get_number(&argvars[0]));
502}
503
504 void
505f_sound_clear(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
506{
507 sound_mch_clear();
508}
509
510#if defined(EXITFREE) || defined(PROTO)
511 void
512sound_free(void)
513{
514 sound_mch_free();
515 while (first_callback != NULL)
Luca Saccarola8ce738d2024-11-02 16:22:45 +0100516 delete_sound_callback(first_callback);
Yee Cheng Chin4314e4f2022-10-08 13:50:05 +0100517}
518#endif
519
520#endif // MACOS_X_DARWIN
Bram Moolenaar9b283522019-06-17 22:19:33 +0200521
522#endif // FEAT_SOUND