wrap_Source.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. /**
  2. * Copyright (c) 2006-2018 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. #include <limits>
  21. #include "sound/SoundData.h"
  22. #include "wrap_Source.h"
  23. #include <cmath>
  24. #include <iostream>
  25. namespace love
  26. {
  27. namespace audio
  28. {
  29. Source *luax_checksource(lua_State *L, int idx)
  30. {
  31. return luax_checktype<Source>(L, idx);
  32. }
  33. int w_Source_clone(lua_State *L)
  34. {
  35. Source *t = luax_checksource(L, 1);
  36. Source *clone = nullptr;
  37. luax_catchexcept(L, [&](){ clone = t->clone(); });
  38. luax_pushtype(L, clone);
  39. clone->release();
  40. return 1;
  41. }
  42. int w_Source_play(lua_State *L)
  43. {
  44. Source *t = luax_checksource(L, 1);
  45. luax_pushboolean(L, t->play());
  46. return 1;
  47. }
  48. int w_Source_stop(lua_State *L)
  49. {
  50. Source *t = luax_checksource(L, 1);
  51. t->stop();
  52. return 0;
  53. }
  54. int w_Source_pause(lua_State *L)
  55. {
  56. Source *t = luax_checksource(L, 1);
  57. t->pause();
  58. return 0;
  59. }
  60. int w_Source_setPitch(lua_State *L)
  61. {
  62. Source *t = luax_checksource(L, 1);
  63. float p = (float)luaL_checknumber(L, 2);
  64. if (p > std::numeric_limits<lua_Number>::max() ||
  65. p < std::numeric_limits<lua_Number>::min() ||
  66. p != p)
  67. return luaL_error(L, "Pitch has to be finite and not NaN.");
  68. t->setPitch(p);
  69. return 0;
  70. }
  71. int w_Source_getPitch(lua_State *L)
  72. {
  73. Source *t = luax_checksource(L, 1);
  74. lua_pushnumber(L, t->getPitch());
  75. return 1;
  76. }
  77. int w_Source_setVolume(lua_State *L)
  78. {
  79. Source *t = luax_checksource(L, 1);
  80. float p = (float)luaL_checknumber(L, 2);
  81. t->setVolume(p);
  82. return 0;
  83. }
  84. int w_Source_getVolume(lua_State *L)
  85. {
  86. Source *t = luax_checksource(L, 1);
  87. lua_pushnumber(L, t->getVolume());
  88. return 1;
  89. }
  90. int w_Source_seek(lua_State *L)
  91. {
  92. Source *t = luax_checksource(L, 1);
  93. float offset = (float)luaL_checknumber(L, 2);
  94. if (offset < 0)
  95. return luaL_argerror(L, 2, "can't seek to a negative position");
  96. Source::Unit u = Source::UNIT_SECONDS;
  97. const char *unit = lua_isnoneornil(L, 3) ? 0 : lua_tostring(L, 3);
  98. if (unit && !t->getConstant(unit, u))
  99. return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
  100. t->seek(offset, u);
  101. return 0;
  102. }
  103. int w_Source_tell(lua_State *L)
  104. {
  105. Source *t = luax_checksource(L, 1);
  106. Source::Unit u = Source::UNIT_SECONDS;
  107. const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
  108. if (unit && !t->getConstant(unit, u))
  109. return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
  110. lua_pushnumber(L, t->tell(u));
  111. return 1;
  112. }
  113. int w_Source_getDuration(lua_State *L)
  114. {
  115. Source *t = luax_checksource(L, 1);
  116. Source::Unit u = Source::UNIT_SECONDS;
  117. const char *unit = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2);
  118. if (unit && !t->getConstant(unit, u))
  119. return luax_enumerror(L, "time unit", Source::getConstants(u), unit);
  120. lua_pushnumber(L, t->getDuration(u));
  121. return 1;
  122. }
  123. int w_Source_setPosition(lua_State *L)
  124. {
  125. Source *t = luax_checksource(L, 1);
  126. float v[3];
  127. v[0] = (float)luaL_checknumber(L, 2);
  128. v[1] = (float)luaL_checknumber(L, 3);
  129. v[2] = (float)luaL_optnumber(L, 4, 0);
  130. luax_catchexcept(L, [&](){ t->setPosition(v); });
  131. return 0;
  132. }
  133. int w_Source_getPosition(lua_State *L)
  134. {
  135. Source *t = luax_checksource(L, 1);
  136. float v[3];
  137. luax_catchexcept(L, [&](){ t->getPosition(v); });
  138. lua_pushnumber(L, v[0]);
  139. lua_pushnumber(L, v[1]);
  140. lua_pushnumber(L, v[2]);
  141. return 3;
  142. }
  143. int w_Source_setVelocity(lua_State *L)
  144. {
  145. Source *t = luax_checksource(L, 1);
  146. float v[3];
  147. v[0] = (float)luaL_checknumber(L, 2);
  148. v[1] = (float)luaL_checknumber(L, 3);
  149. v[2] = (float)luaL_optnumber(L, 4, 0);
  150. luax_catchexcept(L, [&](){ t->setVelocity(v); });
  151. return 0;
  152. }
  153. int w_Source_getVelocity(lua_State *L)
  154. {
  155. Source *t = luax_checksource(L, 1);
  156. float v[3];
  157. luax_catchexcept(L, [&](){ t->getVelocity(v); });
  158. lua_pushnumber(L, v[0]);
  159. lua_pushnumber(L, v[1]);
  160. lua_pushnumber(L, v[2]);
  161. return 3;
  162. }
  163. int w_Source_setDirection(lua_State *L)
  164. {
  165. Source *t = luax_checksource(L, 1);
  166. float v[3];
  167. v[0] = (float)luaL_checknumber(L, 2);
  168. v[1] = (float)luaL_checknumber(L, 3);
  169. v[2] = (float)luaL_optnumber(L, 4, 0);
  170. luax_catchexcept(L, [&](){ t->setDirection(v); });
  171. return 0;
  172. }
  173. int w_Source_getDirection(lua_State *L)
  174. {
  175. Source *t = luax_checksource(L, 1);
  176. float v[3];
  177. luax_catchexcept(L, [&](){ t->getDirection(v); });
  178. lua_pushnumber(L, v[0]);
  179. lua_pushnumber(L, v[1]);
  180. lua_pushnumber(L, v[2]);
  181. return 3;
  182. }
  183. int w_Source_setCone(lua_State *L)
  184. {
  185. Source *t = luax_checksource(L, 1);
  186. float innerAngle = (float) luaL_checknumber(L, 2);
  187. float outerAngle = (float) luaL_checknumber(L, 3);
  188. float outerVolume = (float) luaL_optnumber(L, 4, 0.0);
  189. float outerHighGain = (float) luaL_optnumber(L, 5, 1.0);
  190. luax_catchexcept(L, [&](){ t->setCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
  191. return 0;
  192. }
  193. int w_Source_getCone(lua_State *L)
  194. {
  195. Source *t = luax_checksource(L, 1);
  196. float innerAngle, outerAngle, outerVolume, outerHighGain;
  197. luax_catchexcept(L, [&](){ t->getCone(innerAngle, outerAngle, outerVolume, outerHighGain); });
  198. lua_pushnumber(L, innerAngle);
  199. lua_pushnumber(L, outerAngle);
  200. lua_pushnumber(L, outerVolume);
  201. lua_pushnumber(L, outerHighGain);
  202. return 4;
  203. }
  204. int w_Source_setRelative(lua_State *L)
  205. {
  206. Source *t = luax_checksource(L, 1);
  207. luax_catchexcept(L, [&](){ t->setRelative(luax_checkboolean(L, 2)); });
  208. return 0;
  209. }
  210. int w_Source_isRelative(lua_State *L)
  211. {
  212. Source *t = luax_checksource(L, 1);
  213. luax_catchexcept(L, [&](){ luax_pushboolean(L, t->isRelative()); });
  214. return 1;
  215. }
  216. int w_Source_setLooping(lua_State *L)
  217. {
  218. Source *t = luax_checksource(L, 1);
  219. luax_catchexcept(L, [&](){ t->setLooping(luax_checkboolean(L, 2)); });
  220. return 0;
  221. }
  222. int w_Source_isLooping(lua_State *L)
  223. {
  224. Source *t = luax_checksource(L, 1);
  225. luax_pushboolean(L, t->isLooping());
  226. return 1;
  227. }
  228. int w_Source_isPlaying(lua_State *L)
  229. {
  230. Source *t = luax_checksource(L, 1);
  231. luax_pushboolean(L, t->isPlaying());
  232. return 1;
  233. }
  234. int w_Source_setVolumeLimits(lua_State *L)
  235. {
  236. Source *t = luax_checksource(L, 1);
  237. float vmin = (float)luaL_checknumber(L, 2);
  238. float vmax = (float)luaL_checknumber(L, 3);
  239. if (vmin < .0f || vmin > 1.f || vmax < .0f || vmax > 1.f)
  240. return luaL_error(L, "Invalid volume limits: [%f:%f]. Must be in [0:1]", vmin, vmax);
  241. t->setMinVolume(vmin);
  242. t->setMaxVolume(vmax);
  243. return 0;
  244. }
  245. int w_Source_getVolumeLimits(lua_State *L)
  246. {
  247. Source *t = luax_checksource(L, 1);
  248. lua_pushnumber(L, t->getMinVolume());
  249. lua_pushnumber(L, t->getMaxVolume());
  250. return 2;
  251. }
  252. int w_Source_setAttenuationDistances(lua_State *L)
  253. {
  254. Source *t = luax_checksource(L, 1);
  255. float dref = (float)luaL_checknumber(L, 2);
  256. float dmax = (float)luaL_checknumber(L, 3);
  257. if (dref < .0f || dmax < .0f)
  258. return luaL_error(L, "Invalid distances: %f, %f. Must be > 0", dref, dmax);
  259. luax_catchexcept(L, [&]() {
  260. t->setReferenceDistance(dref);
  261. t->setMaxDistance(dmax);
  262. });
  263. return 0;
  264. }
  265. int w_Source_getAttenuationDistances(lua_State *L)
  266. {
  267. Source *t = luax_checksource(L, 1);
  268. luax_catchexcept(L, [&]() {
  269. lua_pushnumber(L, t->getReferenceDistance());
  270. lua_pushnumber(L, t->getMaxDistance());
  271. });
  272. return 2;
  273. }
  274. int w_Source_setRolloff(lua_State *L)
  275. {
  276. Source *t = luax_checksource(L, 1);
  277. float rolloff = (float)luaL_checknumber(L, 2);
  278. if (rolloff < .0f)
  279. return luaL_error(L, "Invalid rolloff: %f. Must be > 0.", rolloff);
  280. luax_catchexcept(L, [&](){ t->setRolloffFactor(rolloff); });
  281. return 0;
  282. }
  283. int w_Source_getRolloff(lua_State *L)
  284. {
  285. Source *t = luax_checksource(L, 1);
  286. luax_catchexcept(L, [&](){ lua_pushnumber(L, t->getRolloffFactor()); });
  287. return 1;
  288. }
  289. int w_Source_setAirAbsorption(lua_State *L)
  290. {
  291. Source *t = luax_checksource(L, 1);
  292. float factor = (float)luaL_checknumber(L, 2);
  293. if (factor < 0.0f)
  294. return luaL_error(L, "Invalid air absorption factor: %f. Must be > 0.", factor);
  295. luax_catchexcept(L, [&](){ t->setAirAbsorptionFactor(factor); });
  296. return 0;
  297. }
  298. int w_Source_getChannelCount(lua_State *L)
  299. {
  300. Source *t = luax_checksource(L, 1);
  301. lua_pushinteger(L, t->getChannelCount());
  302. return 1;
  303. }
  304. int setFilterReadFilter(lua_State *L, int idx, std::map<Filter::Parameter, float> &params)
  305. {
  306. if (lua_gettop(L) < idx || lua_isnoneornil(L, idx))
  307. return 0;
  308. luaL_checktype(L, idx, LUA_TTABLE);
  309. const char *paramstr = nullptr;
  310. Filter::getConstant(Filter::FILTER_TYPE, paramstr, Filter::TYPE_BASIC);
  311. lua_pushstring(L, paramstr);
  312. lua_rawget(L, idx);
  313. if (lua_type(L, -1) == LUA_TNIL)
  314. return luaL_error(L, "Filter type not specificed.");
  315. Filter::Type type = Filter::TYPE_MAX_ENUM;
  316. const char *typestr = luaL_checkstring(L, -1);
  317. if (!Filter::getConstant(typestr, type))
  318. return luax_enumerror(L, "filter type", Filter::getConstants(type), typestr);
  319. lua_pop(L, 1);
  320. params[Filter::FILTER_TYPE] = static_cast<int>(type);
  321. lua_pushnil(L);
  322. while (lua_next(L, idx))
  323. {
  324. const char *keystr = luaL_checkstring(L, -2);
  325. Filter::Parameter param;
  326. if(Filter::getConstant(keystr, param, type) || Filter::getConstant(keystr, param, Filter::TYPE_BASIC))
  327. {
  328. #define luax_effecterror(l,t) luaL_error(l,"Bad parameter type for %s %s: " t " expected, got %s", typestr, keystr, lua_typename(L, -1))
  329. switch(Filter::getParameterType(param))
  330. {
  331. case Filter::PARAM_FLOAT:
  332. if (!lua_isnumber(L, -1))
  333. return luax_effecterror(L, "number");
  334. params[param] = lua_tonumber(L, -1);
  335. break;
  336. case Filter::PARAM_TYPE:
  337. case Filter::PARAM_MAX_ENUM:
  338. break;
  339. }
  340. #undef luax_effecterror
  341. }
  342. else
  343. luaL_error(L, "Invalid '%s' Effect parameter: %s", typestr, keystr);
  344. //remove the value (-1) from stack, keep the key (-2) to feed into lua_next
  345. lua_pop(L, 1);
  346. }
  347. return 1;
  348. }
  349. void getFilterWriteFilter(lua_State *L, int idx, std::map<Filter::Parameter, float> &params)
  350. {
  351. const char *keystr, *valstr;
  352. Filter::Type type = static_cast<Filter::Type>((int)params[Filter::FILTER_TYPE]);
  353. if (lua_istable(L, idx))
  354. lua_pushvalue(L, idx);
  355. else
  356. lua_createtable(L, 0, params.size());
  357. for (auto p : params)
  358. {
  359. if (!Filter::getConstant(p.first, keystr, type))
  360. Filter::getConstant(p.first, keystr, Filter::TYPE_BASIC);
  361. lua_pushstring(L, keystr);
  362. switch (Filter::getParameterType(p.first))
  363. {
  364. case Filter::PARAM_FLOAT:
  365. lua_pushnumber(L, p.second);
  366. break;
  367. case Filter::PARAM_TYPE:
  368. Filter::getConstant(static_cast<Filter::Type>((int)p.second), valstr);
  369. lua_pushstring(L, valstr);
  370. break;
  371. case Filter::PARAM_MAX_ENUM:
  372. break;
  373. }
  374. lua_rawset(L, -3);
  375. }
  376. }
  377. int w_Source_setFilter(lua_State *L)
  378. {
  379. Source *t = luax_checksource(L, 1);
  380. std::map<Filter::Parameter, float> params;
  381. if (setFilterReadFilter(L, 2, params) == 1)
  382. luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter(params)); });
  383. else
  384. luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setFilter()); });
  385. return 1;
  386. }
  387. int w_Source_getFilter(lua_State *L)
  388. {
  389. Source *t = luax_checksource(L, 1);
  390. std::map<Filter::Parameter, float> params;
  391. if (!t->getFilter(params))
  392. return 0;
  393. getFilterWriteFilter(L, 2, params);
  394. return 1;
  395. }
  396. int w_Source_setEffect(lua_State *L)
  397. {
  398. Source *t = luax_checksource(L, 1);
  399. const char *namestr = luaL_checkstring(L, 2);
  400. // :setEffect(effect, false) = clear effect
  401. if (lua_gettop(L) == 3 && lua_isboolean(L, 3) && !lua_toboolean(L, 3))
  402. {
  403. luax_catchexcept(L, [&]() { lua_pushboolean(L, t->unsetEffect(namestr)); });
  404. return 1;
  405. }
  406. std::map<Filter::Parameter, float> params;
  407. if (setFilterReadFilter(L, 3, params) == 1)
  408. luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setEffect(namestr, params)); });
  409. else
  410. luax_catchexcept(L, [&]() { lua_pushboolean(L, t->setEffect(namestr)); });
  411. return 1;
  412. }
  413. int w_Source_getEffect(lua_State *L)
  414. {
  415. Source *t = luax_checksource(L, 1);
  416. const char *namestr = luaL_checkstring(L, 2);
  417. std::map<Filter::Parameter, float> params;
  418. if (!t->getEffect(namestr, params))
  419. return 0;
  420. if (params.size() == 0)
  421. return 0;
  422. getFilterWriteFilter(L, 3, params);
  423. return 1;
  424. }
  425. int w_Source_getActiveEffects(lua_State *L)
  426. {
  427. Source *t = luax_checksource(L, 1);
  428. std::vector<std::string> list;
  429. t->getActiveEffects(list);
  430. lua_createtable(L, 0, (int) list.size());
  431. for (int i = 0; i < (int) list.size(); i++)
  432. {
  433. lua_pushnumber(L, i + 1);
  434. lua_pushstring(L, list[i].c_str());
  435. lua_rawset(L, -3);
  436. }
  437. return 1;
  438. }
  439. int w_Source_getFreeBufferCount(lua_State *L)
  440. {
  441. Source *t = luax_checksource(L, 1);
  442. lua_pushinteger(L, t->getFreeBufferCount());
  443. return 1;
  444. }
  445. int w_Source_queue(lua_State *L)
  446. {
  447. Source *t = luax_checksource(L, 1);
  448. bool success;
  449. if (luax_istype(L, 2, love::sound::SoundData::type))
  450. {
  451. auto s = luax_totype<love::sound::SoundData>(L, 2);
  452. int offset = 0;
  453. size_t length = s->getSize();
  454. if (lua_gettop(L) == 4)
  455. {
  456. offset = luaL_checknumber(L, 3);
  457. length = luaL_checknumber(L, 4);
  458. }
  459. else if (lua_gettop(L) == 3)
  460. length = luaL_checknumber(L, 3);
  461. if (offset < 0 || length > s->getSize() - offset)
  462. return luaL_error(L, "Data region out of bounds.");
  463. luax_catchexcept(L, [&]() {
  464. success = t->queue((unsigned char *)s->getData() + offset, length,
  465. s->getSampleRate(), s->getBitDepth(), s->getChannelCount());
  466. });
  467. }
  468. else if (lua_islightuserdata(L, 2))
  469. {
  470. int offset = luaL_checknumber(L, 3);
  471. int length = luaL_checknumber(L, 4);
  472. int sampleRate = luaL_checknumber(L, 5);
  473. int bitDepth = luaL_checknumber(L, 6);
  474. int channels = luaL_checknumber(L, 7);
  475. if (length < 0 || offset < 0)
  476. return luaL_error(L, "Data region out of bounds.");
  477. luax_catchexcept(L, [&]() {
  478. success = t->queue((void*)((uintptr_t)lua_touserdata(L, 2) + (uintptr_t)offset), length, sampleRate, bitDepth, channels);
  479. });
  480. }
  481. else
  482. return luax_typerror(L, 1, "Sound Data or lightuserdata");
  483. luax_pushboolean(L, success);
  484. return 1;
  485. }
  486. int w_Source_getType(lua_State *L)
  487. {
  488. Source *t = luax_checksource(L, 1);
  489. Source::Type type = t->getType();
  490. const char *str = nullptr;
  491. if (!Source::getConstant(type, str))
  492. return luaL_error(L, "Unknown Source type.");
  493. lua_pushstring(L, str);
  494. return 1;
  495. }
  496. // Deprecated
  497. int w_Source_getChannels(lua_State *L)
  498. {
  499. luax_markdeprecated(L, "Source:getChannels", API_METHOD, DEPRECATED_RENAMED, "Source:getChannelCount");
  500. return w_Source_getChannelCount(L);
  501. }
  502. static const luaL_Reg w_Source_functions[] =
  503. {
  504. { "clone", w_Source_clone },
  505. { "play", w_Source_play },
  506. { "stop", w_Source_stop },
  507. { "pause", w_Source_pause },
  508. { "setPitch", w_Source_setPitch },
  509. { "getPitch", w_Source_getPitch },
  510. { "setVolume", w_Source_setVolume },
  511. { "getVolume", w_Source_getVolume },
  512. { "seek", w_Source_seek },
  513. { "tell", w_Source_tell },
  514. { "getDuration", w_Source_getDuration },
  515. { "setPosition", w_Source_setPosition },
  516. { "getPosition", w_Source_getPosition },
  517. { "setVelocity", w_Source_setVelocity },
  518. { "getVelocity", w_Source_getVelocity },
  519. { "setDirection", w_Source_setDirection },
  520. { "getDirection", w_Source_getDirection },
  521. { "setCone", w_Source_setCone },
  522. { "getCone", w_Source_getCone },
  523. { "setRelative", w_Source_setRelative },
  524. { "isRelative", w_Source_isRelative },
  525. { "setLooping", w_Source_setLooping },
  526. { "isLooping", w_Source_isLooping },
  527. { "isPlaying", w_Source_isPlaying },
  528. { "setVolumeLimits", w_Source_setVolumeLimits },
  529. { "getVolumeLimits", w_Source_getVolumeLimits },
  530. { "setAttenuationDistances", w_Source_setAttenuationDistances },
  531. { "getAttenuationDistances", w_Source_getAttenuationDistances },
  532. { "setRolloff", w_Source_setRolloff },
  533. { "getRolloff", w_Source_getRolloff },
  534. { "getChannelCount", w_Source_getChannelCount },
  535. { "setFilter", w_Source_setFilter },
  536. { "getFilter", w_Source_getFilter },
  537. { "setEffect", w_Source_setEffect },
  538. { "getEffect", w_Source_getEffect },
  539. { "getActiveEffects", w_Source_getActiveEffects },
  540. { "getFreeBufferCount", w_Source_getFreeBufferCount },
  541. { "queue", w_Source_queue },
  542. { "getType", w_Source_getType },
  543. // Deprecated
  544. { "getChannels", w_Source_getChannels },
  545. { 0, 0 }
  546. };
  547. extern "C" int luaopen_source(lua_State *L)
  548. {
  549. return luax_register_type(L, &love::audio::Source::type, w_Source_functions, nullptr);
  550. }
  551. } // audio
  552. } // love