1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <sstream>
19
20#include "bspf.hxx"
21
22#include "Console.hxx"
23#include "Cart.hxx"
24#include "CartDPC.hxx"
25#include "Control.hxx"
26#include "Dialog.hxx"
27#include "Font.hxx"
28#include "Menu.hxx"
29#include "OSystem.hxx"
30#include "PopUpWidget.hxx"
31#include "Settings.hxx"
32#include "Sound.hxx"
33#include "Widget.hxx"
34#include "AudioSettings.hxx"
35
36#include "AudioDialog.hxx"
37
38// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
39AudioDialog::AudioDialog(OSystem& osystem, DialogContainer& parent,
40 const GUI::Font& font)
41 : Dialog(osystem, parent, font, "Audio settings")
42{
43 const int VBORDER = 10;
44 const int HBORDER = 10;
45 const int INDENT = 20;
46 const int VGAP = 4;
47 const int lineHeight = font.getLineHeight(),
48 fontWidth = font.getMaxCharWidth();
49 int xpos, ypos;
50 int lwidth = font.getStringWidth("Volume "),
51 pwidth;
52
53 WidgetArray wid;
54 VariantList items;
55
56 // Set real dimensions
57 _w = 48 * fontWidth + HBORDER * 2;
58 _h = 12 * (lineHeight + VGAP) + VBORDER + _th;
59
60 xpos = HBORDER; ypos = VBORDER + _th;
61
62 // Enable sound
63 mySoundEnableCheckbox = new CheckboxWidget(this, font, xpos, ypos,
64 "Enable sound", kSoundEnableChanged);
65 wid.push_back(mySoundEnableCheckbox);
66 ypos += lineHeight + VGAP;
67 xpos += INDENT;
68
69 // Volume
70 myVolumeSlider = new SliderWidget(this, font, xpos, ypos,
71 "Volume", lwidth, 0, 4 * fontWidth, "%");
72 myVolumeSlider->setMinValue(1); myVolumeSlider->setMaxValue(100);
73 myVolumeSlider->setTickmarkIntervals(4);
74 wid.push_back(myVolumeSlider);
75 ypos += lineHeight + VGAP;
76
77 // Mode
78 items.clear();
79 VarList::push_back(items, "Low quality, medium lag", static_cast<int>(AudioSettings::Preset::lowQualityMediumLag));
80 VarList::push_back(items, "High quality, medium lag", static_cast<int>(AudioSettings::Preset::highQualityMediumLag));
81 VarList::push_back(items, "High quality, low lag", static_cast<int>(AudioSettings::Preset::highQualityLowLag));
82 VarList::push_back(items, "Ultra quality, minimal lag", static_cast<int>(AudioSettings::Preset::ultraQualityMinimalLag));
83 VarList::push_back(items, "Custom", static_cast<int>(AudioSettings::Preset::custom));
84 myModePopup = new PopUpWidget(this, font, xpos, ypos,
85 font.getStringWidth("Ultry quality, minimal lag"), lineHeight,
86 items, "Mode", lwidth, kModeChanged);
87 wid.push_back(myModePopup);
88 ypos += lineHeight + VGAP;
89 xpos += INDENT;
90
91 // Fragment size
92 pwidth = font.getStringWidth("512 samples") + 7;
93 lwidth = font.getStringWidth("Resampling quality ");
94 items.clear();
95 VarList::push_back(items, "128 samples", 128);
96 VarList::push_back(items, "256 samples", 256);
97 VarList::push_back(items, "512 samples", 512);
98 VarList::push_back(items, "1k samples", 1024);
99 VarList::push_back(items, "2k samples", 2048);
100 VarList::push_back(items, "4K samples", 4096);
101 myFragsizePopup = new PopUpWidget(this, font, xpos, ypos,
102 pwidth, lineHeight,
103 items, "Fragment size", lwidth);
104 wid.push_back(myFragsizePopup);
105 ypos += lineHeight + VGAP;
106
107 // Output frequency
108 items.clear();
109 VarList::push_back(items, "44100 Hz", 44100);
110 VarList::push_back(items, "48000 Hz", 48000);
111 VarList::push_back(items, "96000 Hz", 96000);
112 myFreqPopup = new PopUpWidget(this, font, xpos, ypos,
113 pwidth, lineHeight,
114 items, "Sample rate", lwidth);
115 wid.push_back(myFreqPopup);
116 ypos += lineHeight + VGAP;
117
118 // Resampling quality
119 items.clear();
120 VarList::push_back(items, "Low", static_cast<int>(AudioSettings::ResamplingQuality::nearestNeightbour));
121 VarList::push_back(items, "High", static_cast<int>(AudioSettings::ResamplingQuality::lanczos_2));
122 VarList::push_back(items, "Ultra", static_cast<int>(AudioSettings::ResamplingQuality::lanczos_3));
123 myResamplingPopup = new PopUpWidget(this, font, xpos, ypos,
124 pwidth, lineHeight,
125 items, "Resampling quality ", lwidth);
126 wid.push_back(myResamplingPopup);
127 ypos += lineHeight + VGAP;
128
129 // Param 1
130 int swidth = pwidth+23;
131 myHeadroomSlider = new SliderWidget(this, font, xpos, ypos, swidth, lineHeight,
132 "Headroom ", 0, kHeadroomChanged, 10 * fontWidth);
133 myHeadroomSlider->setMinValue(0); myHeadroomSlider->setMaxValue(AudioSettings::MAX_HEADROOM);
134 myHeadroomSlider->setTickmarkIntervals(5);
135 wid.push_back(myHeadroomSlider);
136 ypos += lineHeight + VGAP;
137
138 // Param 2
139 myBufferSizeSlider = new SliderWidget(this, font, xpos, ypos, swidth, lineHeight,
140 "Buffer size ", 0, kBufferSizeChanged, 10 * fontWidth);
141 myBufferSizeSlider->setMinValue(0); myBufferSizeSlider->setMaxValue(AudioSettings::MAX_BUFFER_SIZE);
142 myBufferSizeSlider->setTickmarkIntervals(5);
143 wid.push_back(myBufferSizeSlider);
144 ypos += lineHeight + VGAP;
145
146 // Stereo sound
147 xpos -= INDENT;
148 myStereoSoundCheckbox = new CheckboxWidget(this, font, xpos, ypos,
149 "Stereo for all ROMs");
150 wid.push_back(myStereoSoundCheckbox);
151 ypos += lineHeight + VGAP;
152
153 myDpcPitch = new SliderWidget(this, font, xpos, ypos, swidth - 16, lineHeight,
154 "Pitfall II music pitch ", 0, 0, 5 * fontWidth);
155 myDpcPitch->setMinValue(10000); myDpcPitch->setMaxValue(30000);
156 myDpcPitch->setStepValue(100);
157 myDpcPitch->setTickmarkIntervals(2);
158 wid.push_back(myDpcPitch);
159
160 // Add Defaults, OK and Cancel buttons
161 addDefaultsOKCancelBGroup(wid, font);
162
163 addToFocusList(wid);
164}
165
166// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167void AudioDialog::loadConfig()
168{
169 AudioSettings& audioSettings = instance().audioSettings();
170
171 // Enable sound
172 mySoundEnableCheckbox->setState(audioSettings.enabled());
173
174 // Volume
175 myVolumeSlider->setValue(audioSettings.volume());
176
177 // Stereo
178 myStereoSoundCheckbox->setState(audioSettings.stereo());
179
180 // DPC Pitch
181 myDpcPitch->setValue(audioSettings.dpcPitch());
182
183 // Preset / mode
184 myModePopup->setSelected(static_cast<int>(audioSettings.preset()));
185
186 updateSettingsWithPreset(instance().audioSettings());
187
188 updateEnabledState();
189}
190
191// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
192void AudioDialog::updateSettingsWithPreset(AudioSettings& audioSettings)
193{
194 // Fragsize
195 myFragsizePopup->setSelected(audioSettings.fragmentSize());
196
197 // Output frequency
198 myFreqPopup->setSelected(audioSettings.sampleRate());
199
200 // Headroom
201 myHeadroomSlider->setValue(audioSettings.headroom());
202
203 // Buffer size
204 myBufferSizeSlider->setValue(audioSettings.bufferSize());
205
206 // Resampling quality
207 myResamplingPopup->setSelected(static_cast<int>(audioSettings.resamplingQuality()));
208}
209
210// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
211void AudioDialog::saveConfig()
212{
213 AudioSettings& audioSettings = instance().audioSettings();
214
215 // Enabled
216 audioSettings.setEnabled(mySoundEnableCheckbox->getState());
217 instance().sound().setEnabled(mySoundEnableCheckbox->getState());
218
219 // Volume
220 audioSettings.setVolume(myVolumeSlider->getValue());
221 instance().sound().setVolume(myVolumeSlider->getValue());
222
223 // Stereo
224 audioSettings.setStereo(myStereoSoundCheckbox->getState());
225
226 // DPC Pitch
227 audioSettings.setDpcPitch(myDpcPitch->getValue());
228 // update if current cart is Pitfall II
229 if (instance().hasConsole() && instance().console().cartridge().name() == "CartridgeDPC")
230 {
231 CartridgeDPC& cart = static_cast<CartridgeDPC&>(instance().console().cartridge());
232 cart.setDpcPitch(myDpcPitch->getValue());
233 }
234
235 AudioSettings::Preset preset = static_cast<AudioSettings::Preset>(myModePopup->getSelectedTag().toInt());
236 audioSettings.setPreset(preset);
237
238 if (preset == AudioSettings::Preset::custom) {
239 // Fragsize
240 audioSettings.setFragmentSize(myFragsizePopup->getSelectedTag().toInt());
241 audioSettings.setSampleRate(myFreqPopup->getSelectedTag().toInt());
242 audioSettings.setHeadroom(myHeadroomSlider->getValue());
243 audioSettings.setBufferSize(myBufferSizeSlider->getValue());
244 audioSettings.setResamplingQuality(static_cast<AudioSettings::ResamplingQuality>(myResamplingPopup->getSelectedTag().toInt()));
245 }
246
247 // Only force a re-initialization when necessary, since it can
248 // be a time-consuming operation
249 if(instance().hasConsole())
250 instance().console().initializeAudio();
251}
252
253// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
254void AudioDialog::setDefaults()
255{
256 mySoundEnableCheckbox->setState(AudioSettings::DEFAULT_ENABLED);
257 myVolumeSlider->setValue(AudioSettings::DEFAULT_VOLUME);
258 myStereoSoundCheckbox->setState(AudioSettings::DEFAULT_STEREO);
259 myDpcPitch->setValue(AudioSettings::DEFAULT_DPC_PITCH);
260 myModePopup->setSelected(static_cast<int>(AudioSettings::DEFAULT_PRESET));
261
262 if (AudioSettings::DEFAULT_PRESET == AudioSettings::Preset::custom) {
263 myResamplingPopup->setSelected(static_cast<int>(AudioSettings::DEFAULT_RESAMPLING_QUALITY));
264 myFragsizePopup->setSelected(AudioSettings::DEFAULT_FRAGMENT_SIZE);
265 myFreqPopup->setSelected(AudioSettings::DEFAULT_SAMPLE_RATE);
266 myHeadroomSlider->setValue(AudioSettings::DEFAULT_HEADROOM);
267 myBufferSizeSlider->setValue(AudioSettings::DEFAULT_BUFFER_SIZE);
268 }
269 else updatePreset();
270
271 updateEnabledState();
272}
273
274// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
275void AudioDialog::updateEnabledState()
276{
277 bool active = mySoundEnableCheckbox->getState();
278 AudioSettings::Preset preset = static_cast<AudioSettings::Preset>(myModePopup->getSelectedTag().toInt());
279 bool userMode = preset == AudioSettings::Preset::custom;
280
281 myVolumeSlider->setEnabled(active);
282 myStereoSoundCheckbox->setEnabled(active);
283 myModePopup->setEnabled(active);
284 // enable only for Pitfall II cart
285 myDpcPitch->setEnabled(active && instance().hasConsole() && instance().console().cartridge().name() == "CartridgeDPC");
286
287 myFragsizePopup->setEnabled(active && userMode);
288 myFreqPopup->setEnabled(active && userMode);
289 myResamplingPopup->setEnabled(active && userMode);
290 myHeadroomSlider->setEnabled(active && userMode);
291 myBufferSizeSlider->setEnabled(active && userMode);
292}
293
294// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
295void AudioDialog::updatePreset()
296{
297 AudioSettings::Preset preset = static_cast<AudioSettings::Preset>(myModePopup->getSelectedTag().toInt());
298
299 // Make a copy that does not affect the actual settings...
300 AudioSettings audioSettings = instance().audioSettings();
301 audioSettings.setPersistent(false);
302 // ... and set the requested preset
303 audioSettings.setPreset(preset);
304
305 updateSettingsWithPreset(audioSettings);
306}
307
308// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
309void AudioDialog::handleCommand(CommandSender* sender, int cmd,
310 int data, int id)
311{
312 switch(cmd)
313 {
314 case GuiObject::kOKCmd:
315 saveConfig();
316 close();
317 break;
318
319 case GuiObject::kDefaultsCmd:
320 setDefaults();
321 break;
322
323 case kSoundEnableChanged:
324 updateEnabledState();
325 break;
326
327 case kModeChanged:
328 updatePreset();
329 updateEnabledState();
330 break;
331
332 case kHeadroomChanged:
333 {
334 std::ostringstream ss;
335 ss << std::fixed << std::setprecision(1) << (0.5 * myHeadroomSlider->getValue()) << " frames";
336 myHeadroomSlider->setValueLabel(ss.str());
337 break;
338 }
339 case kBufferSizeChanged:
340 {
341 std::ostringstream ss;
342 ss << std::fixed << std::setprecision(1) << (0.5 * myBufferSizeSlider->getValue()) << " frames";
343 myBufferSizeSlider->setValueLabel(ss.str());
344 break;
345 }
346
347 default:
348 Dialog::handleCommand(sender, cmd, data, 0);
349 break;
350 }
351}
352