Skip to content

Commit 7412c09

Browse files
authored
Add a UI for setting audio input and output devices (#492)
1 parent a1ce144 commit 7412c09

File tree

5 files changed

+127
-12
lines changed

5 files changed

+127
-12
lines changed

gui/renderengine.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ void DrawFixtureBeam(const DrawData &data, const theatre::Fixture &fixture) {
194194
const double sin_1 = std::sin(direction_1);
195195
const double cos_2 = std::cos(direction_2);
196196
const double sin_2 = std::sin(direction_2);
197-
data.cairo->arc_negative(x, y, beam_start_radius, direction_2,
198-
direction_1);
197+
data.cairo->arc(x, y, beam_start_radius, direction_2, direction_1);
199198
data.cairo->line_to(x + cos_1 * beam_end_radius,
200199
y + sin_1 * beam_end_radius);
201200
// small optimization: don't draw an extra arc when the
@@ -334,8 +333,8 @@ void RenderEngine::DrawSelectedFixtures(
334333
const Cairo::RefPtr<Cairo::Context> &cairo,
335334
const std::vector<system::ObservingPtr<theatre::Fixture>>
336335
&selected_fixtures) const {
337-
cairo->set_source_rgb(0.2, 0.2, 1.0);
338336
cairo->set_line_width(4.0 / scale_);
337+
cairo->set_source_rgb(0.2, 0.2, 1.0);
339338
for (const system::ObservingPtr<theatre::Fixture> &fixture_ptr :
340339
selected_fixtures) {
341340
const theatre::Fixture *f = fixture_ptr.Get();
@@ -348,11 +347,17 @@ void RenderEngine::DrawSelectedFixtures(
348347
cairo->arc(x, y, radius, 0.0, 2.0 * M_PI);
349348
const double cos_dir = std::cos(direction);
350349
const double sin_dir = std::sin(direction);
351-
cairo->move_to(x + radius * kRotationHandleEnd * cos_dir,
352-
y + radius * kRotationHandleEnd * sin_dir);
350+
const double end_x = x + radius * kRotationHandleEnd * cos_dir;
351+
const double end_y = y + radius * kRotationHandleEnd * sin_dir;
352+
cairo->move_to(end_x, end_y);
353353
cairo->line_to(x + radius * kRotationHandleStart * cos_dir,
354354
y + radius * kRotationHandleStart * sin_dir);
355355
cairo->stroke();
356+
cairo->arc(end_x, end_y, 5.0 / scale_, 0.0, 2.0 * M_PI);
357+
cairo->set_source_rgb(0.2, 0.5, 1.0);
358+
cairo->fill_preserve();
359+
cairo->set_source_rgb(0.2, 0.2, 1.0);
360+
cairo->stroke();
356361
}
357362
}
358363
}

gui/windows/settingswindow.cpp

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#include "settingswindow.h"
22

3+
#include <cassert>
34
#include <variant>
45

6+
#include <alsa/asoundlib.h>
7+
58
#include "gui/instance.h"
69

10+
#include "system/settings.h"
11+
712
#include "theatre/management.h"
813

914
namespace glight::gui::windows {
@@ -15,6 +20,20 @@ using theatre::devices::OutputMapping;
1520
using theatre::devices::UniverseMap;
1621

1722
SettingsWindow::SettingsWindow() {
23+
MakeDmxPage();
24+
25+
// notebook_.append_page(midi_page_, "MIDI");
26+
27+
MakeAudioPage();
28+
29+
add(notebook_);
30+
notebook_.show_all();
31+
32+
FillUniverses();
33+
UpdateAfterSelection();
34+
}
35+
36+
void SettingsWindow::MakeDmxPage() {
1837
universe_list_store_ = Gtk::ListStore::create(universe_columns_);
1938
universe_list_view_.set_model(universe_list_store_);
2039
universe_list_view_.append_column("Universe", universe_columns_.universe_);
@@ -66,14 +85,62 @@ SettingsWindow::SettingsWindow() {
6685
dmx_output_rb_.signal_clicked().connect(save_universe);
6786
dmx_page_.attach(dmx_output_rb_, 0, 6, 2, 1);
6887
notebook_.append_page(dmx_page_, "DMX");
88+
}
6989

70-
notebook_.append_page(midi_page_, "MIDI");
71-
72-
add(notebook_);
73-
notebook_.show_all();
74-
75-
FillUniverses();
76-
UpdateAfterSelection();
90+
void SettingsWindow::MakeAudioPage() {
91+
void** hints = nullptr;
92+
if (snd_device_name_hint(-1, "pcm", &hints) >= 0) {
93+
size_t hi = 0;
94+
output_devices_combo_.remove_all();
95+
const std::string selected_input = Instance::Settings().audio_input;
96+
const std::string selected_output = Instance::Settings().audio_output;
97+
while (hints[hi] != nullptr) {
98+
char* device_name = snd_device_name_get_hint(hints[hi], "NAME");
99+
char* device_desc = snd_device_name_get_hint(hints[hi], "DESC");
100+
char* input_or_output = snd_device_name_get_hint(hints[hi], "IOID");
101+
const bool is_input = input_or_output == nullptr ||
102+
std::strcmp(input_or_output, "Input") == 0;
103+
const bool is_output = input_or_output == nullptr ||
104+
std::strcmp(input_or_output, "Output") == 0;
105+
assert(is_input || is_output);
106+
std::string device_desc_str(device_desc);
107+
const std::size_t new_line = device_desc_str.find('\n');
108+
if (new_line != std::string::npos) {
109+
device_desc_str.resize(new_line);
110+
}
111+
const std::string description_with_name =
112+
device_desc_str + " (" + device_name + ")";
113+
if (is_input) {
114+
input_devices_combo_.append(description_with_name);
115+
input_devices_.emplace_back(device_name);
116+
}
117+
if (is_output) {
118+
output_devices_combo_.append(description_with_name);
119+
output_devices_.emplace_back(device_name);
120+
}
121+
if (selected_input == device_name) input_devices_combo_.set_active(hi);
122+
if (selected_output == device_name) output_devices_combo_.set_active(hi);
123+
free(input_or_output);
124+
free(device_desc);
125+
free(device_name);
126+
++hi;
127+
}
128+
snd_device_name_free_hint(hints);
129+
}
130+
input_devices_combo_.signal_changed().connect([&]() { SetInputAudio(); });
131+
output_devices_combo_.signal_changed().connect([&]() { SetOutputAudio(); });
132+
audio_page_label_.set_line_wrap(true);
133+
audio_page_label_.set_max_width_chars(40);
134+
audio_page_label_.set_margin_start(8);
135+
audio_page_label_.set_margin_end(8);
136+
audio_page_label_.set_margin_top(8);
137+
audio_page_label_.set_margin_bottom(8);
138+
audio_page_.attach(audio_page_label_, 0, 0, 2, 1);
139+
audio_page_.attach(input_devices_label_, 0, 1);
140+
audio_page_.attach(input_devices_combo_, 1, 1);
141+
audio_page_.attach(output_devices_label_, 0, 2);
142+
audio_page_.attach(output_devices_combo_, 1, 2);
143+
notebook_.append_page(audio_page_, "Audio");
77144
}
78145

79146
void SettingsWindow::FillUniverses() {
@@ -263,4 +330,17 @@ void SettingsWindow::ReloadOla() {
263330
UpdateAfterSelection();
264331
}
265332

333+
void SettingsWindow::SetInputAudio() {
334+
const std::string& selected_device =
335+
input_devices_[input_devices_combo_.get_active_row_number()];
336+
Instance::Settings().audio_input = selected_device;
337+
Instance::Management().StartBeatFinder();
338+
}
339+
340+
void SettingsWindow::SetOutputAudio() {
341+
const std::string& selected_device =
342+
output_devices_[output_devices_combo_.get_active_row_number()];
343+
Instance::Settings().audio_output = selected_device;
344+
}
345+
266346
} // namespace glight::gui::windows

gui/windows/settingswindow.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class SettingsWindow : public ChildWindow {
3030
void SaveSelectedOlaUniverse();
3131
void ReloadOla();
3232

33+
void SetInputAudio();
34+
void SetOutputAudio();
35+
36+
void MakeDmxPage();
37+
void MakeAudioPage();
38+
3339
Gtk::Notebook notebook_;
3440
Gtk::Grid dmx_page_;
3541
struct UniverseListColumns : public Gtk::TreeModelColumnRecord {
@@ -62,6 +68,18 @@ class SettingsWindow : public ChildWindow {
6268

6369
Gtk::VBox midi_page_;
6470

71+
std::vector<std::string> input_devices_;
72+
std::vector<std::string> output_devices_;
73+
Gtk::Grid audio_page_;
74+
Gtk::Label audio_page_label_{
75+
"Select the Alsa device names to be used. The input device is used for "
76+
"beat detection and volume effects. The output device is used for "
77+
"playing the audio of scenes."};
78+
Gtk::Label input_devices_label_{"Input device: "};
79+
Gtk::ComboBoxText input_devices_combo_;
80+
Gtk::Label output_devices_label_{"Output device: "};
81+
Gtk::ComboBoxText output_devices_combo_;
82+
6583
RecursionLock recursion_lock_;
6684
};
6785

theatre/management.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ Management::~Management() {
4747
}
4848

4949
void Management::StartBeatFinder() {
50+
std::lock_guard<std::mutex> lock(_mutex);
51+
// In case the beat finder is already running, it is better to stop it first
52+
// so the audio device is not used twice.
53+
_beatFinder.reset();
5054
_beatFinder = std::make_unique<BeatFinder>(settings_.audio_input);
5155
_beatFinder->Start();
5256
}

theatre/management.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ class Management {
4040

4141
void Run();
4242

43+
/**
44+
* Start or restart the beat finder. This is not done automatically because
45+
* for testing and certain other scenarios it might not be desirable to access
46+
* the audio device.
47+
*
48+
* If the beat finder is running, calling this function again can be used to
49+
* use a new device after the device has been changed in the settings.
50+
*/
4351
void StartBeatFinder();
4452
BeatFinder &GetBeatFinder() { return *_beatFinder; }
4553

0 commit comments

Comments
 (0)