1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "wrap_Audio.h"
23
24#include "openal/Audio.h"
25#include "null/Audio.h"
26
27#include "common/runtime.h"
28
29// C++
30#include <iostream>
31#include <cmath>
32
33namespace love
34{
35namespace audio
36{
37
38#define instance() (Module::getInstance<Audio>(Module::M_AUDIO))
39
40int w_getActiveSourceCount(lua_State *L)
41{
42 lua_pushinteger(L, instance()->getActiveSourceCount());
43 return 1;
44}
45
46int w_newSource(lua_State *L)
47{
48 Source::Type stype = Source::TYPE_STREAM;
49
50 if (!luax_istype(L, 1, love::sound::SoundData::type) && !luax_istype(L, 1, love::sound::Decoder::type))
51 {
52 const char *stypestr = luaL_checkstring(L, 2);
53 if (stypestr && !Source::getConstant(stypestr, stype))
54 return luax_enumerror(L, "source type", Source::getConstants(stype), stypestr);
55
56 if (stype == Source::TYPE_QUEUE)
57 return luaL_error(L, "Cannot create queueable sources using newSource. Use newQueueableSource instead.");
58 }
59
60 if (lua_isstring(L, 1) || luax_istype(L, 1, love::filesystem::File::type) || luax_istype(L, 1, love::filesystem::FileData::type))
61 luax_convobj(L, 1, "sound", "newDecoder");
62
63 if (stype == Source::TYPE_STATIC && luax_istype(L, 1, love::sound::Decoder::type))
64 luax_convobj(L, 1, "sound", "newSoundData");
65
66 Source *t = nullptr;
67
68 luax_catchexcept(L, [&]() {
69 if (luax_istype(L, 1, love::sound::SoundData::type))
70 t = instance()->newSource(luax_totype<love::sound::SoundData>(L, 1));
71 else if (luax_istype(L, 1, love::sound::Decoder::type))
72 t = instance()->newSource(luax_totype<love::sound::Decoder>(L, 1));
73 });
74
75 if (t != nullptr)
76 {
77 luax_pushtype(L, t);
78 t->release();
79 return 1;
80 }
81 else
82 return luax_typerror(L, 1, "Decoder or SoundData");
83}
84
85int w_newQueueableSource(lua_State *L)
86{
87 Source *t = nullptr;
88
89 luax_catchexcept(L, [&]() {
90 t = instance()->newSource((int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_optinteger(L, 4, 0));
91 });
92
93 if (t != nullptr)
94 {
95 luax_pushtype(L, t);
96 t->release();
97 return 1;
98 }
99 else
100 return 0; //all argument type errors are checked in above constructor
101}
102
103static std::vector<Source*> readSourceList(lua_State *L, int n)
104{
105 if (n < 0)
106 n += lua_gettop(L) + 1;
107
108 int items = (int) luax_objlen(L, n);
109 std::vector<Source*> sources(items);
110
111 for (int i = 0; i < items; i++)
112 {
113 lua_rawgeti(L, n, i+1);
114 sources[i] = luax_checksource(L, -1);
115 lua_pop(L, 1);
116 }
117
118 return sources;
119}
120
121static std::vector<Source*> readSourceVararg(lua_State *L, int i)
122{
123 const int top = lua_gettop(L);
124
125 if (i < 0)
126 i += top + 1;
127
128 int items = top - i + 1;
129 std::vector<Source*> sources(items);
130
131 for (int pos = 0; i <= top; i++, pos++)
132 sources[pos] = luax_checksource(L, i);
133
134 return sources;
135}
136
137int w_play(lua_State *L)
138{
139 if (lua_istable(L, 1))
140 luax_pushboolean(L, instance()->play(readSourceList(L, 1)));
141 else if (lua_gettop(L) > 1)
142 luax_pushboolean(L, instance()->play(readSourceVararg(L, 1)));
143 else
144 {
145 Source *s = luax_checksource(L, 1);
146 luax_pushboolean(L, instance()->play(s));
147 }
148
149 return 1;
150}
151
152int w_stop(lua_State *L)
153{
154 if (lua_isnone(L, 1))
155 instance()->stop();
156 else if (lua_istable(L, 1))
157 instance()->stop(readSourceList(L, 1));
158 else if (lua_gettop(L) > 1)
159 instance()->stop(readSourceVararg(L, 1));
160 else
161 {
162 Source *s = luax_checksource(L, 1);
163 s->stop();
164 }
165 return 0;
166}
167
168int w_pause(lua_State *L)
169{
170 if (lua_isnone(L, 1))
171 {
172 auto sources = instance()->pause();
173
174 lua_createtable(L, (int) sources.size(), 0);
175 for (int i = 0; i < (int) sources.size(); i++)
176 {
177 luax_pushtype(L, sources[i]);
178 lua_rawseti(L, -2, i+1);
179 }
180 return 1;
181 }
182 else if (lua_istable(L, 1))
183 instance()->pause(readSourceList(L, 1));
184 else if (lua_gettop(L) > 1)
185 instance()->pause(readSourceVararg(L, 1));
186 else
187 {
188 Source *s = luax_checksource(L, 1);
189 s->pause();
190 }
191
192 return 0;
193}
194
195int w_setVolume(lua_State *L)
196{
197 float v = (float)luaL_checknumber(L, 1);
198 instance()->setVolume(v);
199 return 0;
200}
201
202int w_getVolume(lua_State *L)
203{
204 lua_pushnumber(L, instance()->getVolume());
205 return 1;
206}
207
208int w_setPosition(lua_State *L)
209{
210 float v[3];
211 v[0] = (float)luaL_checknumber(L, 1);
212 v[1] = (float)luaL_checknumber(L, 2);
213 v[2] = (float)luaL_optnumber(L, 3, 0);
214 instance()->setPosition(v);
215 return 0;
216}
217
218int w_getPosition(lua_State *L)
219{
220 float v[3];
221 instance()->getPosition(v);
222 lua_pushnumber(L, v[0]);
223 lua_pushnumber(L, v[1]);
224 lua_pushnumber(L, v[2]);
225 return 3;
226}
227
228int w_setOrientation(lua_State *L)
229{
230 float v[6];
231 v[0] = (float)luaL_checknumber(L, 1);
232 v[1] = (float)luaL_checknumber(L, 2);
233 v[2] = (float)luaL_checknumber(L, 3);
234 v[3] = (float)luaL_checknumber(L, 4);
235 v[4] = (float)luaL_checknumber(L, 5);
236 v[5] = (float)luaL_checknumber(L, 6);
237 instance()->setOrientation(v);
238 return 0;
239}
240
241int w_getOrientation(lua_State *L)
242{
243 float v[6];
244 instance()->getOrientation(v);
245 lua_pushnumber(L, v[0]);
246 lua_pushnumber(L, v[1]);
247 lua_pushnumber(L, v[2]);
248 lua_pushnumber(L, v[3]);
249 lua_pushnumber(L, v[4]);
250 lua_pushnumber(L, v[5]);
251 return 6;
252}
253
254int w_setVelocity(lua_State *L)
255{
256 float v[3];
257 v[0] = (float)luaL_checknumber(L, 1);
258 v[1] = (float)luaL_checknumber(L, 2);
259 v[2] = (float)luaL_optnumber(L, 3, 0);
260 instance()->setVelocity(v);
261 return 0;
262}
263
264int w_getVelocity(lua_State *L)
265{
266 float v[3];
267 instance()->getVelocity(v);
268 lua_pushnumber(L, v[0]);
269 lua_pushnumber(L, v[1]);
270 lua_pushnumber(L, v[2]);
271 return 3;
272}
273
274int w_setDopplerScale(lua_State *L)
275{
276 instance()->setDopplerScale(luax_checkfloat(L, 1));
277 return 0;
278}
279
280int w_getDopplerScale(lua_State *L)
281{
282 lua_pushnumber(L, instance()->getDopplerScale());
283 return 1;
284}
285/*
286int w_setMeter(lua_State *L)
287{
288 instance()->setMeter(luax_checkfloat(L, 1));
289 return 0;
290}
291
292int w_getMeter(lua_State *L)
293{
294 lua_pushnumber(L, instance()->getMeter());
295 return 1;
296}
297*/
298int w_setDistanceModel(lua_State *L)
299{
300 const char *modelStr = luaL_checkstring(L, 1);
301 Audio::DistanceModel distanceModel;
302 if (!Audio::getConstant(modelStr, distanceModel))
303 return luax_enumerror(L, "distance model", Audio::getConstants(distanceModel), modelStr);
304 instance()->setDistanceModel(distanceModel);
305 return 0;
306}
307
308int w_getDistanceModel(lua_State *L)
309{
310 Audio::DistanceModel distanceModel = instance()->getDistanceModel();
311 const char *modelStr;
312 if (!Audio::getConstant(distanceModel, modelStr))
313 return 0;
314 lua_pushstring(L, modelStr);
315 return 1;
316}
317
318int w_getRecordingDevices(lua_State *L)
319{
320 const std::vector<RecordingDevice*> &devices = instance()->getRecordingDevices();
321
322 lua_createtable(L, devices.size(), 0);
323
324 for (unsigned int i = 0; i < devices.size(); i++)
325 {
326 luax_pushtype(L, devices[i]);
327 lua_rawseti(L, -2, i + 1);
328 }
329
330 return 1;
331}
332
333int w_setEffect(lua_State *L)
334{
335 const char *namestr = luaL_checkstring(L, 1);
336
337 if (lua_isnoneornil(L, 2) || (lua_gettop(L) == 2 && lua_isboolean(L, 2) && !lua_toboolean(L, 2)))
338 {
339 lua_pushboolean(L, instance()->unsetEffect(namestr));
340 return 1;
341 }
342
343 luaL_checktype(L, 2, LUA_TTABLE);
344
345 const char *paramstr = nullptr;
346
347 //find type (mandatory)
348 Effect::getConstant(Effect::EFFECT_TYPE, paramstr, Effect::TYPE_BASIC);
349 lua_pushstring(L, paramstr);
350 lua_rawget(L, 2);
351 if (lua_type(L, -1) == LUA_TNIL)
352 return luaL_error(L, "Effect type not specificed.");
353
354 Effect::Type type = Effect::TYPE_MAX_ENUM;
355 const char *typestr = luaL_checkstring(L, -1);
356 if (!Effect::getConstant(typestr, type))
357 return luax_enumerror(L, "effect type", Effect::getConstants(type), typestr);
358
359 lua_pop(L, 1);
360 std::map<Effect::Parameter, float> params;
361 params[Effect::EFFECT_TYPE] = static_cast<int>(type);
362
363 // Iterate over the whole table, reading valid parameters and erroring on invalid ones
364 lua_pushnil(L);
365 while (lua_next(L, 2))
366 {
367 const char *keystr = luaL_checkstring(L, -2);
368 Effect::Parameter param;
369
370 if(Effect::getConstant(keystr, param, type) || Effect::getConstant(keystr, param, Effect::TYPE_BASIC))
371 {
372#define luax_effecterror(l,t) luaL_error(l,"Bad parameter type for %s %s: " t " expected, got %s", typestr, keystr, lua_typename(L, -1))
373 switch(Effect::getParameterType(param))
374 {
375 case Effect::PARAM_FLOAT:
376 if (!lua_isnumber(L, -1))
377 return luax_effecterror(L, "number");
378 params[param] = lua_tonumber(L, -1);
379 break;
380 case Effect::PARAM_BOOL:
381 if (!lua_isboolean(L, -1))
382 return luax_effecterror(L, "boolean");
383 params[param] = lua_toboolean(L, -1) ? 1.0 : 0.0;
384 break;
385 case Effect::PARAM_WAVEFORM:
386 {
387 if (!lua_isstring(L, -1))
388 return luax_effecterror(L, "string");
389 paramstr = lua_tostring(L, -1);
390 Effect::Waveform waveform;
391 if (!Effect::getConstant(paramstr, waveform))
392 return luax_enumerror(L, "waveform type", paramstr);
393 params[param] = static_cast<int>(waveform);
394 break;
395 }
396 /*
397 case Effect::PARAM_DIRECTION:
398 {
399 if (!lua_isstring(L, -1))
400 return luax_effecterror(L, "string");
401 paramstr = lua_tostring(L, -1);
402 Effect::Direction direction;
403 if (!Effect::getConstant(paramstr, direction))
404 return luaL_error(L, "Invalid direction type: %s", paramstr);
405 params[param] = static_cast<int>(direction);
406 break;
407 }
408 case Effect::PARAM_PHONEME:
409 {
410 if (!lua_isstring(L, -1))
411 return luax_effecterror(L, "string");
412 paramstr = lua_tostring(L, -1);
413 Effect::Phoneme phoneme;
414 if (!Effect::getConstant(basicstr, phoneme))
415 return luaL_error(L, "Invalid phoneme type: %s", paramstr);
416 params[param] = static_cast<int>(phoneme);
417 break;
418 }
419 */
420 case Effect::PARAM_TYPE:
421 case Effect::PARAM_MAX_ENUM:
422 break;
423 }
424#undef luax_effecterror
425 }
426 else
427 luaL_error(L, "Invalid '%s' Effect parameter: %s", typestr, keystr);
428
429 //remove the value (-1) from stack, keep the key (-2) to feed into lua_next
430 lua_pop(L, 1);
431 }
432
433 luax_catchexcept(L, [&]() { lua_pushboolean(L, instance()->setEffect(namestr, params)); });
434 return 1;
435}
436
437int w_getEffect(lua_State *L)
438{
439 const char *namestr = luaL_checkstring(L, 1);
440
441 std::map<Effect::Parameter, float> params;
442
443 if (!instance()->getEffect(namestr, params))
444 return 0;
445
446 const char *keystr, *valstr;
447 Effect::Type type = static_cast<Effect::Type>((int)params[Effect::EFFECT_TYPE]);
448
449 if (lua_istable(L, 2))
450 lua_pushvalue(L, 2);
451 else
452 lua_createtable(L, 0, params.size());
453
454 for (auto p : params)
455 {
456 if (!Effect::getConstant(p.first, keystr, type))
457 Effect::getConstant(p.first, keystr, Effect::TYPE_BASIC);
458
459 lua_pushstring(L, keystr);
460 switch (Effect::getParameterType(p.first))
461 {
462 case Effect::PARAM_FLOAT:
463 lua_pushnumber(L, p.second);
464 break;
465 case Effect::PARAM_BOOL:
466 lua_pushboolean(L, p.second > 0.5 ? true : false);
467 break;
468 case Effect::PARAM_WAVEFORM:
469 Effect::getConstant(static_cast<Effect::Waveform>((int)p.second), valstr);
470 lua_pushstring(L, valstr);
471 break;
472/*
473 case Effect::PARAM_DIRECTION:
474 Effect::getConstant(static_cast<Effect::Direction>((int)p.second), valstr);
475 lua_pushstring(L, valstr);
476 break;
477 case Effect::PARAM_PHONEME:
478 Effect::getConstant(static_cast<Effect::Phoneme>((int)p.second), valstr);
479 lua_pushstring(L, valstr);
480 break;
481*/
482 case Effect::PARAM_TYPE:
483 Effect::getConstant(static_cast<Effect::Type>((int)p.second), valstr);
484 lua_pushstring(L, valstr);
485 break;
486 case Effect::PARAM_MAX_ENUM:
487 break;
488 }
489 lua_rawset(L, -3);
490 }
491 return 1;
492}
493
494int w_getActiveEffects(lua_State *L)
495{
496 std::vector<std::string> list;
497 instance()->getActiveEffects(list);
498
499 lua_createtable(L, 0, (int) list.size());
500 for (int i = 0; i < (int) list.size(); i++)
501 {
502 lua_pushnumber(L, i + 1);
503 lua_pushstring(L, list[i].c_str());
504 lua_rawset(L, -3);
505 }
506 return 1;
507}
508
509int w_getMaxSceneEffects(lua_State *L)
510{
511 lua_pushnumber(L, instance()->getMaxSceneEffects());
512 return 1;
513}
514
515int w_getMaxSourceEffects(lua_State *L)
516{
517 lua_pushnumber(L, instance()->getMaxSourceEffects());
518 return 1;
519}
520
521int w_isEffectsSupported(lua_State *L)
522{
523 lua_pushboolean(L, instance()->isEFXsupported());
524 return 1;
525}
526
527int w_setMixWithSystem(lua_State *L)
528{
529 luax_pushboolean(L, Audio::setMixWithSystem(luax_checkboolean(L, 1)));
530 return 1;
531}
532
533int w_getSourceCount(lua_State *L)
534{
535 luax_markdeprecated(L, "love.audio.getSourceCount", API_FUNCTION, DEPRECATED_RENAMED, "love.audio.getActiveSourceCount");
536 return w_getActiveSourceCount(L);
537}
538
539// List of functions to wrap.
540static const luaL_Reg functions[] =
541{
542 { "getActiveSourceCount", w_getActiveSourceCount },
543 { "newSource", w_newSource },
544 { "newQueueableSource", w_newQueueableSource },
545 { "play", w_play },
546 { "stop", w_stop },
547 { "pause", w_pause },
548 { "setVolume", w_setVolume },
549 { "getVolume", w_getVolume },
550 { "setPosition", w_setPosition },
551 { "getPosition", w_getPosition },
552 { "setOrientation", w_setOrientation },
553 { "getOrientation", w_getOrientation },
554 { "setVelocity", w_setVelocity },
555 { "getVelocity", w_getVelocity },
556 { "setDopplerScale", w_setDopplerScale },
557 { "getDopplerScale", w_getDopplerScale },
558 //{ "setMeter", w_setMeter },
559 //{ "getMeter", w_setMeter },
560 { "setDistanceModel", w_setDistanceModel },
561 { "getDistanceModel", w_getDistanceModel },
562 { "getRecordingDevices", w_getRecordingDevices },
563 { "setEffect", w_setEffect },
564 { "getEffect", w_getEffect },
565 { "getActiveEffects", w_getActiveEffects },
566 { "getMaxSceneEffects", w_getMaxSceneEffects },
567 { "getMaxSourceEffects", w_getMaxSourceEffects },
568 { "isEffectsSupported", w_isEffectsSupported },
569 { "setMixWithSystem", w_setMixWithSystem },
570
571 // Deprecated
572 { "getSourceCount", w_getSourceCount },
573
574 { 0, 0 }
575};
576
577static const lua_CFunction types[] =
578{
579 luaopen_source,
580 luaopen_recordingdevice,
581 0
582};
583
584extern "C" int luaopen_love_audio(lua_State *L)
585{
586 Audio *instance = instance();
587
588 if (instance == nullptr)
589 {
590 // Try OpenAL first.
591 try
592 {
593 instance = new love::audio::openal::Audio();
594 }
595 catch(love::Exception &e)
596 {
597 std::cout << e.what() << std::endl;
598 }
599 }
600 else
601 instance->retain();
602
603 if (instance == nullptr)
604 {
605 // Fall back to nullaudio.
606 try
607 {
608 instance = new love::audio::null::Audio();
609 }
610 catch(love::Exception &e)
611 {
612 std::cout << e.what() << std::endl;
613 }
614 }
615
616 if (instance == nullptr)
617 return luaL_error(L, "Could not open any audio module.");
618
619 WrappedModule w;
620 w.module = instance;
621 w.name = "audio";
622 w.type = &Module::type;
623 w.functions = functions;
624 w.types = types;
625
626 int n = luax_register_module(L, w);
627
628 return n;
629}
630
631} // audio
632} // love
633