The Battle for Wesnoth  1.15.0-dev
lua_audio.cpp
Go to the documentation of this file.
1 /*
2 Copyright (C) 2017-2018 by the Battle for Wesnoth Project https://www.wesnoth.org/
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY.
10 
11 See the COPYING file for more details.
12 */
13 
14 #include "lua_audio.hpp"
15 
16 #include "log.hpp"
17 #include "lua/lua.h"
18 #include "lua/lauxlib.h"
19 #include "scripting/lua_common.hpp"
20 #include "scripting/push_check.hpp"
21 #include "sound.hpp"
22 #include "sound_music_track.hpp"
23 #include "preferences/general.hpp"
24 #include <set>
25 
26 static lg::log_domain log_audio("audio");
27 #define DBG_AUDIO LOG_STREAM(debug, log_audio)
28 #define LOG_AUDIO LOG_STREAM(info, log_audio)
29 #define ERR_AUDIO LOG_STREAM(err, log_audio)
30 
31 static const char* Track = "music track";
32 
34  std::shared_ptr<sound::music_track> track;
35 public:
36  explicit lua_music_track(int i) : track(sound::get_track(i)) {}
37  explicit lua_music_track(std::shared_ptr<sound::music_track> new_track) : track(new_track) {}
38  bool valid() const {
39  return track && track->valid();
40  }
42  return *track;
43  }
44  const sound::music_track& operator*() const {
45  return *track;
46  }
47  std::shared_ptr<sound::music_track> operator->() {
48  return track;
49  }
50  std::shared_ptr<const sound::music_track> operator->() const {
51  return track;
52  }
53 };
54 
56  lua_music_track* trk = new(L) lua_music_track(i);
58  return trk;
59 }
60 
61 static lua_music_track* push_track(lua_State* L, std::shared_ptr<sound::music_track> new_track) {
62  lua_music_track* trk = new(L) lua_music_track(new_track);
64  return trk;
65 }
66 
68  return static_cast<lua_music_track*>(luaL_checkudata(L, i, Track));
69 }
70 
71 /**
72  * Destroys a lua_music_track object before it is collected (__gc metamethod).
73  */
75 {
76  lua_music_track* u = get_track(L, 1);
77  u->lua_music_track::~lua_music_track();
78  return 0;
79 }
80 
81 static int impl_music_get(lua_State* L) {
82  if(lua_isnumber(L, 2)) {
83  push_track(L, lua_tointeger(L, 2) - 1);
84  return 1;
85  }
86  const char* m = luaL_checkstring(L, 2);
87 
88  if(strcmp(m, "current") == 0) {
90  return 1;
91  }
92 
93  if(strcmp(m, "previous") == 0) {
95  return 1;
96  }
97 
98  if(strcmp(m, "current_i") == 0) {
99  auto current_index = sound::get_current_track_index();
100  if(current_index) {
101  lua_pushinteger(L, current_index.get() + 1);
102  } else {
103  lua_pushnil(L);
104  }
105  return 1;
106  }
107  if(strcmp(m, "all") == 0) {
108  config playlist;
110  const auto& range = playlist.child_range("music");
111  std::vector<config> tracks(range.begin(), range.end());
112  lua_push(L, tracks);
113  return 1;
114  }
115  // This calculation reverses the one used in [volume] to get back the relative volume level.
116  // (Which is the same calculation that's duplicated in impl_music_set.)
118  return luaW_getmetafield(L, 1, m);
119 }
120 
121 static int impl_music_set(lua_State* L) {
122  if(lua_isnumber(L, 2)) {
123  unsigned int i = lua_tointeger(L, 2) - 1;
124  config cfg;
125  if(lua_isnil(L, 3)) {
126  if(i < sound::get_num_tracks()) {
128  }
129  } else if(luaW_toconfig(L, 3, cfg)) {
130  // Don't allow play_once=yes
131  if(cfg["play_once"]) {
132  return luaL_argerror(L, 3, "For play_once, use wesnoth.music_list.play instead");
133  }
134  if(i < sound::get_num_tracks()) {
136  } else {
137  // Remove the track at that index and add the new one in its place
138  // It's a little inefficient though...
140  sound::play_music_config(cfg, false, i);
141  }
142  } else {
143  lua_music_track& track = *get_track(L, 3);
144  if(i < sound::get_num_tracks()) {
145  sound::set_track(i, track.operator->());
146  } else {
147  track->write(cfg, true);
149  }
150  }
151  return 0;
152  }
153  const char* m = luaL_checkstring(L, 2);
155  modify_int_attrib_check_range("current_i", sound::play_track(value - 1), 1, static_cast<int>(sound::get_num_tracks()));
156  return 0;
157 }
158 
159 static int impl_music_len(lua_State* L) {
161  return 1;
162 }
163 
164 static int intf_music_play(lua_State* L) {
166  return 0;
167 }
168 
170  std::size_t n = sound::get_num_tracks();
171  if(n > 0) {
173  }
174  return 0;
175 }
176 
177 static int intf_music_add(lua_State* L) {
178  int index = -1;
179  if(lua_isinteger(L, 1)) {
180  index = lua_tointeger(L, 1);
181  lua_remove(L, 1);
182  }
183  config cfg = config {
184  "name", luaL_checkstring(L, 1),
185  "append", true,
186  };
187  bool found_ms_before = false, found_ms_after = false, found_imm = false;
188  for(int i = 2; i <= lua_gettop(L); i++) {
189  if(lua_isboolean(L, i)) {
190  if(found_imm) {
191  return luaL_argerror(L, i, "only one boolean argument may be passed");
192  } else {
193  cfg["immediate"] = luaW_toboolean(L, i);
194  }
195  } else if(lua_isnumber(L, i)) {
196  if(found_ms_after) {
197  return luaL_argerror(L, i, "only two integer arguments may be passed");
198  } else if(found_ms_before) {
199  cfg["ms_after"] = lua_tointeger(L, i);
200  found_ms_after = true;
201  } else {
202  cfg["ms_before"] = lua_tointeger(L, i);
203  found_ms_before = true;
204  }
205  } else {
206  return luaL_argerror(L, i, "unrecognized argument");
207  }
208  }
209  sound::play_music_config(cfg, false, index);
210  return 0;
211 }
212 
215  return 0;
216 }
217 
218 static int intf_music_remove(lua_State* L) {
219  // Use a non-standard comparator to ensure iteration in descending order
220  std::set<int, std::greater<int>> to_remove;
221  for(int i = 1; i <= lua_gettop(L); i++) {
222  to_remove.insert(luaL_checkinteger(L, i));
223  }
224  for(int i : to_remove) {
226  }
227  return 0;
228 }
229 
232  return 0;
233 }
234 
235 static int impl_track_get(lua_State* L) {
237  if(track == nullptr || !track->valid()) {
238  return luaL_error(L, "Error: Attempted to access an invalid music track.\n");
239  }
240  const char* m = luaL_checkstring(L, 2);
241  return_bool_attrib("valid", track->valid());
242  if(!track->valid()) {
243  return luaL_error(L, "Tried to access member of track that is no longer valid.");
244  }
245  return_bool_attrib("append", (*track)->append());
246  return_bool_attrib("shuffle", (*track)->shuffle());
247  return_bool_attrib("immediate", (*track)->immediate());
248  return_bool_attrib("once", (*track)->play_once());
249  return_int_attrib("ms_before", (*track)->ms_before());
250  return_int_attrib("ms_after", (*track)->ms_after());
251  return_string_attrib("name", (*track)->id());
252  return_string_attrib("title", (*track)->title());
253 
254  return_cfg_attrib("__cfg",
255  cfg["append"]=(*track)->append();
256  cfg["shuffle"]=(*track)->shuffle();
257  cfg["immediate"]=(*track)->immediate();
258  cfg["once"]=(*track)->play_once();
259  cfg["ms_before"]=(*track)->ms_before();
260  cfg["ms_after"]=(*track)->ms_after();
261  cfg["name"]=(*track)->id();
262  cfg["title"]=(*track)->title());
263 
264  return luaW_getmetafield(L, 1, m);
265 }
266 
267 static int impl_track_set(lua_State* L) {
269  if(track == nullptr || !track->valid()) {
270  return luaL_error(L, "Error: Attempted to access an invalid music track.\n");
271  }
272  const char* m = luaL_checkstring(L, 2);
273  modify_bool_attrib("shuffle", (*track)->set_shuffle(value));
274  modify_bool_attrib("once", (*track)->set_play_once(value));
275  modify_int_attrib("ms_before", (*track)->set_ms_before(value));
276  modify_int_attrib("ms_after", (*track)->set_ms_after(value));
277  modify_string_attrib("title", (*track)->set_title(value));
278  return 0;
279 }
280 
281 static int impl_track_eq(lua_State* L) {
282  lua_music_track* a = get_track(L, 1);
283  lua_music_track* b = get_track(L, 2);
284  if(!a || !b) {
285  // This implies that one argument is not a music track, though I suspect this is dead code...?
286  // Does Lua ever call this if the arguments are not of the same type?
287  lua_pushboolean(L, false);
288  return 1;
289  }
290  if(!a->valid() && !b->valid()) {
291  lua_pushboolean(L, true);
292  return 1;
293  }
294  if(a->valid() && b->valid()) {
295  lua_music_track& lhs = *a;
296  lua_music_track& rhs = *b;
297  lua_pushboolean(L, lhs->id() == rhs->id() && lhs->shuffle() == rhs->shuffle() && lhs->play_once() == rhs->play_once() && lhs->ms_before() == rhs->ms_before() && lhs->ms_after() == rhs->ms_after());
298  return 1;
299  }
300  lua_pushboolean(L, false);
301  return 1;
302 }
303 
304 namespace lua_audio {
305  std::string register_table(lua_State* L) {
306  // The music playlist metatable
307  lua_getglobal(L, "wesnoth");
308  lua_newuserdata(L, 0);
309  lua_createtable(L, 0, 4);
310  static luaL_Reg pl_callbacks[] {
311  { "__index", impl_music_get },
312  { "__newindex", impl_music_set },
313  { "__len", impl_music_len },
314  { "play", intf_music_play },
315  { "add", intf_music_add },
316  { "clear", intf_music_clear },
317  { "remove", intf_music_remove },
318  { "next", intf_music_next },
319  { "force_refresh", intf_music_commit },
320  { nullptr, nullptr },
321  };
322  luaL_setfuncs(L, pl_callbacks, 0);
323  lua_pushstring(L, "music playlist");
324  lua_setfield(L, -2, "__metatable");
325  lua_setmetatable(L, -2);
326  lua_setfield(L, -2, "music_list");
327  lua_pop(L, 1);
328 
329  // The music track metatable
331  static luaL_Reg track_callbacks[] {
332  {"__gc", impl_track_collect},
333  { "__index", impl_track_get },
334  { "__newindex", impl_track_set },
335  { "__eq", impl_track_eq },
336  { nullptr, nullptr },
337  };
338  luaL_setfuncs(L, track_callbacks, 0);
339  lua_pushstring(L, Track);
340  lua_setfield(L, -2, "__metatable");
341 
342  return "Adding music playlist table...";
343  }
344 }
void empty_playlist()
Definition: sound.cpp:574
#define modify_bool_attrib(name, accessor)
Definition: lua_common.hpp:339
LUALIB_API void * luaL_checkudata(lua_State *L, int ud, const char *tname)
Definition: lauxlib.cpp:333
unsigned int get_num_tracks()
Definition: sound.cpp:214
LUA_API void lua_createtable(lua_State *L, int narray, int nrec)
Definition: lapi.cpp:684
sound::music_track & operator*()
Definition: lua_audio.cpp:41
std::shared_ptr< music_track > get_current_track()
Definition: sound.cpp:201
LUA_API void lua_pushboolean(lua_State *L, int b)
Definition: lapi.cpp:557
static int intf_music_commit(lua_State *)
Definition: lua_audio.cpp:230
static lua_music_track * get_track(lua_State *L, int i)
Definition: lua_audio.cpp:67
#define a
static lg::log_domain log_audio("audio")
LUA_API int lua_gettop(lua_State *L)
Definition: lapi.cpp:167
child_itors child_range(config_key_type key)
Definition: config.cpp:366
void remove_track(unsigned int i)
Definition: sound.cpp:239
#define lua_remove(L, idx)
Definition: lua.h:371
std::shared_ptr< sound::music_track > track
Definition: lua_audio.cpp:34
LUA_API int lua_getglobal(lua_State *L, const char *name)
Definition: lapi.cpp:605
#define return_string_attrib(name, accessor)
Definition: lua_common.hpp:217
#define lua_tointeger(L, i)
Definition: lua.h:342
std::shared_ptr< music_track > get_previous_music_track()
Definition: sound.cpp:205
static int impl_track_collect(lua_State *L)
Destroys a lua_music_track object before it is collected (__gc metamethod).
Definition: lua_audio.cpp:74
Audio output for sound and music.
Definition: sound.cpp:40
void play_music_once(const std::string &file)
Definition: sound.cpp:565
LUALIB_API void luaL_setmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:312
void lua_push(lua_State *L, const T &val)
Definition: push_check.hpp:371
void write_music_play_list(config &snapshot)
Definition: sound.cpp:813
#define modify_float_attrib_check_range(name, accessor, allowed_min, allowed_max)
Definition: lua_common.hpp:329
LUA_API void * lua_newuserdata(lua_State *L, size_t size)
Definition: lapi.cpp:1184
static int intf_music_play(lua_State *L)
Definition: lua_audio.cpp:164
#define lua_pop(L, n)
Definition: lua.h:344
static int impl_music_set(lua_State *L)
Definition: lua_audio.cpp:121
#define b
bool valid() const
Definition: lua_audio.cpp:38
static int impl_music_len(lua_State *L)
Definition: lua_audio.cpp:159
static const char * Track
Definition: lua_audio.cpp:31
static int intf_music_add(lua_State *L)
Definition: lua_audio.cpp:177
static int intf_music_next(lua_State *)
Definition: lua_audio.cpp:169
LUALIB_API int luaL_argerror(lua_State *L, int arg, const char *extramsg)
Definition: lauxlib.cpp:164
#define modify_string_attrib(name, accessor)
Definition: lua_common.hpp:292
#define return_int_attrib(name, accessor)
Definition: lua_common.hpp:226
bool luaW_toboolean(lua_State *L, int n)
Definition: lua_common.cpp:870
static lua_music_track * push_track(lua_State *L, int i)
Definition: lua_audio.cpp:55
std::shared_ptr< sound::music_track > operator->()
Definition: lua_audio.cpp:47
static int impl_track_eq(lua_State *L)
Definition: lua_audio.cpp:281
#define modify_int_attrib_check_range(name, accessor, allowed_min, allowed_max)
Definition: lua_common.hpp:310
boost::optional< unsigned int > get_current_track_index()
Definition: sound.cpp:194
LUA_API int lua_setmetatable(lua_State *L, int objindex)
Definition: lapi.cpp:846
LUA_API void lua_pushnil(lua_State *L)
Definition: lapi.cpp:450
void set_music_volume(int vol)
Definition: sound.cpp:1027
#define lua_isboolean(L, n)
Definition: lua.h:356
bool luaW_toconfig(lua_State *L, int index, config &cfg)
Converts an optional table or vconfig to a config object.
Definition: lua_common.cpp:724
#define return_cfg_attrib(name, accessor)
Definition: lua_common.hpp:250
LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname)
Definition: lauxlib.cpp:299
#define lua_isnil(L, n)
Definition: lua.h:355
std::size_t i
Definition: function.cpp:933
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int arg)
Definition: lauxlib.cpp:430
lua_music_track(std::shared_ptr< sound::music_track > new_track)
Definition: lua_audio.cpp:37
Internal representation of music tracks.
static int intf_music_remove(lua_State *L)
Definition: lua_audio.cpp:218
std::string register_table(lua_State *L)
Definition: lua_audio.cpp:305
static int impl_track_get(lua_State *L)
Definition: lua_audio.cpp:235
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
LUA_API int lua_isinteger(lua_State *L, int idx)
Definition: lapi.cpp:270
LUA_API int lua_isnumber(lua_State *L, int idx)
Definition: lapi.cpp:276
int get_music_volume()
Definition: sound.cpp:1018
LUALIB_API int luaL_error(lua_State *L, const char *fmt,...)
Definition: lauxlib.cpp:223
static int impl_music_get(lua_State *L)
Definition: lua_audio.cpp:81
#define return_bool_attrib(name, accessor)
Definition: lua_common.hpp:242
bool luaW_getmetafield(lua_State *L, int idx, const char *key)
Like luaL_getmetafield, but returns false if key is an empty string or begins with two underscores...
Definition: lua_common.cpp:494
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:666
#define return_float_attrib(name, accessor)
Definition: lua_common.hpp:234
Standard logging facilities (interface).
std::shared_ptr< const sound::music_track > operator->() const
Definition: lua_audio.cpp:50
void commit_music_changes()
Definition: sound.cpp:784
void set_track(unsigned int i, const std::shared_ptr< music_track > &to)
Definition: sound.cpp:232
static int intf_music_clear(lua_State *)
Definition: lua_audio.cpp:213
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
Definition: lauxlib.cpp:934
const sound::music_track & operator*() const
Definition: lua_audio.cpp:44
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
void play_track(unsigned int i)
Definition: sound.cpp:591
static int impl_track_set(lua_State *L)
Definition: lua_audio.cpp:267
static map_location::DIRECTION n
LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
Definition: lapi.cpp:466
LUA_API const char * lua_pushstring(lua_State *L, const char *s)
Definition: lapi.cpp:491
LUA_API void lua_setfield(lua_State *L, int idx, const char *k)
Definition: lapi.cpp:777
lua_music_track(int i)
Definition: lua_audio.cpp:36
int music_volume()
Definition: general.cpp:547
#define modify_int_attrib(name, accessor)
Definition: lua_common.hpp:301
#define luaL_checkstring(L, n)
Definition: lauxlib.h:124