#include <new>
#include <utility>
+#include "audio_mixer.h"
#include "defs.h"
#ifdef HAVE_CEF
#include "cef_capture.h"
#include "image_input.h"
#include "input_state.h"
#include "lua_utils.h"
+#include "mainwindow.h"
#include "pbo_frame_allocator.h"
#include "scene.h"
return 0;
}
+// NOTE: There's a race condition in all of the audio functions; if the mapping
+// is changed by the user underway, you might not be manipulating the bus you
+// expect. (You should not get crashes, though.) There's not all that much we
+// can do about it, short of locking the entire mixer while anything from the
+// theme runs.
+
+int Nageru_get_num_audio_buses(lua_State *L)
+{
+ if (global_audio_mixer == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+ lua_pushinteger(L, global_audio_mixer->num_buses());
+ return 1;
+}
+
+int Nageru_get_audio_bus_name(lua_State *L)
+{
+ if (global_audio_mixer == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+ int bus_index = luaL_checknumber(L, 1);
+ InputMapping input_mapping = global_audio_mixer->get_input_mapping();
+ if (bus_index < 0 || size_t(bus_index) >= input_mapping.buses.size()) {
+ // Doesn't fix the race, but fixes other out-of-bounds.
+ print_warning(L, "Theme called get_audio_bus_name() on nonexistent bus %d; returning nil.\n", bus_index);
+ lua_pushnil(L);
+ } else {
+ lua_pushstring(L, input_mapping.buses[bus_index].name.c_str());
+ }
+ return 1;
+}
+
+int Nageru_get_audio_bus_fader_level_db(lua_State *L)
+{
+ if (global_audio_mixer == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+
+ int bus_index = luaL_checknumber(L, 1);
+ if (bus_index < 0 || size_t(bus_index) >= global_audio_mixer->num_buses()) {
+ // Doesn't fix the race, but fixes other out-of-bounds.
+ print_warning(L, "Theme called get_audio_bus_fader_level_db() on nonexistent bus %d; returning 0.0.\n", bus_index);
+ lua_pushnumber(L, 0.0);
+ } else {
+ lua_pushnumber(L, global_audio_mixer->get_fader_volume(bus_index));
+ }
+ return 1;
+}
+
+int Nageru_set_audio_bus_fader_level_db(lua_State *L)
+{
+ if (global_audio_mixer == nullptr || global_mainwindow == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+
+ int bus_index = luaL_checknumber(L, 1);
+ if (bus_index < 0 || size_t(bus_index) >= global_audio_mixer->num_buses()) {
+ // Doesn't fix the race, but fixes other out-of-bounds.
+ print_warning(L, "Theme called set_audio_bus_fader_level_db() on nonexistent bus %d; ignoring.\n", bus_index);
+ return 0;
+ }
+ double level_db = luaL_checknumber(L, 2);
+
+ // Go through the UI, so that it gets updated.
+ global_mainwindow->set_fader_absolute(bus_index, level_db);
+ return 0;
+}
+
+int Nageru_get_audio_bus_mute(lua_State *L)
+{
+ if (global_audio_mixer == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+
+ int bus_index = luaL_checknumber(L, 1);
+ if (bus_index < 0 || size_t(bus_index) >= global_audio_mixer->num_buses()) {
+ // Doesn't fix the race, but fixes other out-of-bounds.
+ print_warning(L, "Theme called get_audio_bus_mute() on nonexistent bus %d; returning false.\n", bus_index);
+ lua_pushboolean(L, false);
+ } else {
+ lua_pushboolean(L, global_audio_mixer->get_mute(bus_index));
+ }
+ return 1;
+}
+
+int Nageru_set_audio_bus_mute(lua_State *L)
+{
+ if (global_audio_mixer == nullptr || global_mainwindow == nullptr) {
+ // The audio mixer isn't set up until we know how many FFmpeg inputs we have.
+ luaL_error(L, "Audio functions can not be called before the theme is done initializing.");
+ }
+
+ int bus_index = luaL_checknumber(L, 1);
+ if (bus_index < 0 || size_t(bus_index) >= global_audio_mixer->num_buses()) {
+ // Doesn't fix the race, but fixes other out-of-bounds.
+ print_warning(L, "Theme called set_audio_bus_mute() on nonexistent bus %d; ignoring.\n", bus_index);
+ return 0;
+ }
+ bool mute = checkbool(L, 2);
+
+ // Go through the UI, so that it gets updated.
+ if (mute != global_audio_mixer->get_mute(bus_index)) {
+ global_mainwindow->toggle_mute(bus_index);
+ }
+ return 0;
+}
+
Theme::Theme(const string &filename, const vector<string> &search_dirs, ResourcePool *resource_pool, unsigned num_cards)
: resource_pool(resource_pool), num_cards(num_cards), signal_to_card_mapping(global_flags.default_stream_mapping)
{
}
const luaL_Reg Nageru_funcs[] = {
+ // Channel information.
{ "set_channel_name", Nageru_set_channel_name },
{ "set_num_channels", Nageru_set_num_channels },
{ "set_channel_signal", Nageru_set_channel_signal },
{ "set_supports_wb", Nageru_set_supports_wb },
- { NULL, NULL }
+
+ // Audio.
+ { "get_num_audio_buses", Nageru_get_num_audio_buses },
+ { "get_audio_bus_name", Nageru_get_audio_bus_name },
+ { "get_audio_bus_fader_level_db", Nageru_get_audio_bus_fader_level_db },
+ { "set_audio_bus_fader_level_db", Nageru_set_audio_bus_fader_level_db },
+ { "get_audio_bus_mute", Nageru_get_audio_bus_mute },
+ { "set_audio_bus_mute", Nageru_set_audio_bus_mute },
+
+ { nullptr, nullptr }
};
lua_pushlightuserdata(L, this);
luaL_setfuncs(L, Nageru_funcs, 1); // for (name,f in funcs) { mt[name] = f, with upvalue {theme} }