1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2004-2021 musikcube team
4 //
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright notice,
11 // this list of conditions and the following disclaimer.
12 //
13 // * Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // * Neither the name of the author nor the names of other contributors may
18 // be used to endorse or promote products derived from this software
19 // without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 //
33 //////////////////////////////////////////////////////////////////////////////
34
35 #include <stdafx.h>
36
37 #include "PlaybackOverlays.h"
38
39 #include <musikcore/audio/Outputs.h>
40 #include <musikcore/support/Preferences.h>
41 #include <musikcore/support/PreferenceKeys.h>
42
43 #include <cursespp/App.h>
44 #include <cursespp/SimpleScrollAdapter.h>
45 #include <cursespp/ListOverlay.h>
46 #include <cursespp/DialogOverlay.h>
47
48 #include <vector>
49
50 using namespace musik::cube;
51 using namespace musik::core;
52 using namespace musik::core::audio;
53 using namespace musik::core::sdk;
54 using namespace cursespp;
55
56 static std::vector<std::shared_ptr<IOutput> > plugins;
57 static std::set<std::string> invalidCrossfadeOutputs = { "WaveOut" };
58
59 template <typename T>
60 struct ReleaseDeleter {
operator ()ReleaseDeleter61 void operator()(T* t) {
62 if (t) t->Release();
63 }
64 };
65
showNoOutputPluginsMessage()66 static void showNoOutputPluginsMessage() {
67 std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
68
69 (*dialog)
70 .SetTitle(_TSTR("default_overlay_title"))
71 .SetMessage(_TSTR("playback_overlay_no_output_plugins_mesage"))
72 .AddButton(
73 "KEY_ENTER",
74 "ENTER",
75 _TSTR("button_ok"));
76
77 App::Overlays().Push(dialog);
78 }
79
showOutputCannotCrossfadeMessage(const std::string & outputName)80 static void showOutputCannotCrossfadeMessage(const std::string& outputName) {
81 std::shared_ptr<DialogOverlay> dialog(new DialogOverlay());
82
83 std::string message = u8fmt(
84 _TSTR("playback_overlay_invalid_transport"),
85 outputName.c_str());
86
87 (*dialog)
88 .SetTitle(_TSTR("default_overlay_title"))
89 .SetMessage(message)
90 .AddButton(
91 "KEY_ENTER",
92 "ENTER",
93 _TSTR("button_ok"));
94
95 App::Overlays().Push(dialog);
96 }
97
PlaybackOverlays()98 PlaybackOverlays::PlaybackOverlays() {
99 }
100
ShowOutputDriverOverlay(MasterTransport::Type transportType,std::function<void ()> callback)101 void PlaybackOverlays::ShowOutputDriverOverlay(
102 MasterTransport::Type transportType,
103 std::function<void()> callback)
104 {
105 plugins = outputs::GetAllOutputs();
106
107 if (!plugins.size()) {
108 showNoOutputPluginsMessage();
109 return;
110 }
111
112 using Adapter = cursespp::SimpleScrollAdapter;
113 using ListOverlay = cursespp::ListOverlay;
114
115 std::string currentOutput = outputs::SelectedOutput()->Name();
116 size_t selectedIndex = 0;
117
118 std::shared_ptr<Adapter> adapter(new Adapter());
119 for (size_t i = 0; i < plugins.size(); i++) {
120 const std::string name = plugins[i]->Name();
121 adapter->AddEntry(name);
122
123 if (name == currentOutput) {
124 selectedIndex = i;
125 }
126 }
127
128 adapter->SetSelectable(true);
129
130 std::shared_ptr<ListOverlay> dialog(new ListOverlay());
131
132 dialog->SetAdapter(adapter)
133 .SetTitle(_TSTR("playback_overlay_output_plugins_title"))
134 .SetSelectedIndex(selectedIndex)
135 .SetItemSelectedCallback(
136 [callback, transportType](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
137
138 if (transportType == MasterTransport::Type::Crossfade) {
139 std::string output = outputs::GetAllOutputs().at(index)->Name();
140 if (invalidCrossfadeOutputs.find(output) != invalidCrossfadeOutputs.end()) {
141 showOutputCannotCrossfadeMessage(output);
142 return;
143 }
144 }
145
146 outputs::SelectOutput(plugins[index]);
147
148 if (callback) {
149 callback();
150 }
151 });
152
153 cursespp::App::Overlays().Push(dialog);
154 }
155
ShowOutputDeviceOverlay(std::function<void ()> callback)156 void PlaybackOverlays::ShowOutputDeviceOverlay(std::function<void()> callback) {
157 auto output = outputs::SelectedOutput();
158 if (!output) {
159 showNoOutputPluginsMessage();
160 return;
161 }
162
163 std::string currentDeviceName = _TSTR("settings_output_device_default");
164
165 std::shared_ptr<IDeviceList> deviceList = std::shared_ptr<IDeviceList>(
166 output->GetDeviceList(), ReleaseDeleter<IDeviceList>());
167
168 std::shared_ptr<IDevice> device = std::shared_ptr<IDevice>(
169 output->GetDefaultDevice(), ReleaseDeleter<IDevice>());
170
171 if (device) {
172 currentDeviceName = device->Name();
173 }
174
175 using Adapter = cursespp::SimpleScrollAdapter;
176 using ListOverlay = cursespp::ListOverlay;
177
178 size_t width = _DIMEN("output_device_overlay_width", 35);
179 size_t selectedIndex = 0;
180
181 std::shared_ptr<Adapter> adapter(new Adapter());
182 adapter->AddEntry(_TSTR("settings_output_device_default"));
183
184 if (deviceList) {
185 for (size_t i = 0; i < deviceList->Count(); i++) {
186 const std::string name = deviceList->At(i)->Name();
187 adapter->AddEntry(name);
188
189 width = std::max(width, u8cols(name));
190
191 if (name == currentDeviceName) {
192 selectedIndex = i + 1;
193 }
194 }
195 }
196
197 adapter->SetSelectable(true);
198
199 std::shared_ptr<ListOverlay> dialog(new ListOverlay());
200
201 dialog->SetAdapter(adapter)
202 .SetTitle(_TSTR("playback_overlay_output_device_title"))
203 .SetSelectedIndex(selectedIndex)
204 .SetWidth(narrow_cast<int>(width))
205 .SetItemSelectedCallback(
206 [output, deviceList, callback](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
207 if (index == 0) {
208 output->SetDefaultDevice("");
209 }
210 else {
211 output->SetDefaultDevice(deviceList->At(index - 1)->Id());
212 }
213
214 if (callback) {
215 callback();
216 }
217 });
218
219 cursespp::App::Overlays().Push(dialog);
220 }
221
ShowTransportOverlay(MasterTransport::Type transportType,std::function<void (MasterTransport::Type)> callback)222 void PlaybackOverlays::ShowTransportOverlay(
223 MasterTransport::Type transportType,
224 std::function<void(MasterTransport::Type)> callback)
225 {
226 using Adapter = cursespp::SimpleScrollAdapter;
227 using ListOverlay = cursespp::ListOverlay;
228
229 std::shared_ptr<Adapter> adapter(new Adapter());
230 adapter->AddEntry(_TSTR("settings_transport_type_gapless"));
231 adapter->AddEntry(_TSTR("settings_transport_type_crossfade"));
232 adapter->SetSelectable(true);
233
234 size_t selectedIndex = (transportType == MasterTransport::Type::Gapless) ? 0 : 1;
235
236 std::shared_ptr<ListOverlay> dialog(new ListOverlay());
237
238 dialog->SetAdapter(adapter)
239 .SetTitle(_TSTR("playback_overlay_transport_title"))
240 .SetSelectedIndex(selectedIndex)
241 .SetItemSelectedCallback(
242 [callback](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
243 auto result = (index == 0)
244 ? MasterTransport::Type::Gapless
245 : MasterTransport::Type::Crossfade;
246
247 std::string output = outputs::SelectedOutput()->Name();
248
249 if (result == MasterTransport::Type::Crossfade &&
250 invalidCrossfadeOutputs.find(output) != invalidCrossfadeOutputs.end())
251 {
252 showOutputCannotCrossfadeMessage(output);
253 }
254 else if (callback) {
255 callback(result);
256 }
257 });
258
259 cursespp::App::Overlays().Push(dialog);
260 }
261
ShowReplayGainOverlay(std::function<void ()> callback)262 void PlaybackOverlays::ShowReplayGainOverlay(std::function<void()> callback) {
263 using Adapter = cursespp::SimpleScrollAdapter;
264 using ListOverlay = cursespp::ListOverlay;
265
266 std::shared_ptr<Adapter> adapter(new Adapter());
267 adapter->AddEntry(_TSTR("settings_replay_gain_mode_disabled"));
268 adapter->AddEntry(_TSTR("settings_replay_gain_mode_track"));
269 adapter->AddEntry(_TSTR("settings_replay_gain_mode_album"));
270 adapter->SetSelectable(true);
271
272 auto prefs = Preferences::ForComponent(core::prefs::components::Playback);
273
274 auto selectedIndex = prefs->GetInt(
275 core::prefs::keys::ReplayGainMode.c_str(),
276 (int) ReplayGainMode::Disabled);
277
278 std::shared_ptr<ListOverlay> dialog(new ListOverlay());
279
280 dialog->SetAdapter(adapter)
281 .SetTitle(_TSTR("settings_replay_gain_title"))
282 .SetSelectedIndex((size_t) selectedIndex)
283 .SetItemSelectedCallback(
284 [callback, selectedIndex, prefs](ListOverlay* overlay, IScrollAdapterPtr adapter, size_t index) {
285 prefs->SetInt(core::prefs::keys::ReplayGainMode.c_str(), (int) index);
286 prefs->Save();
287 if (selectedIndex != index && callback) {
288 callback();
289 }
290 });
291
292 cursespp::App::Overlays().Push(dialog);
293 }
294