Volume control
jump to
@@ -3,6 +3,7 @@ Features:
- Ability to hide individual background layers, or OBJs - Ability to mute individual audio channels - Palette viewer + - Volume control Bugfixes: - GBA: Fix timers not updating timing when writing to only the reload register - All: Fix sanitize-deb script not cleaning up after itself
@@ -14,6 +14,7 @@
const unsigned GBA_AUDIO_SAMPLES = 2048; const unsigned BLIP_BUFFER_SIZE = 0x4000; const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); +const int GBA_AUDIO_VOLUME_MAX = 0x100; #define SWEEP_CYCLES (GBA_ARM7TDMI_FREQUENCY / 128) static bool _writeEnvelope(struct GBAAudioEnvelope* envelope, uint16_t value);@@ -48,6 +49,7 @@ audio->forceDisableCh[2] = false;
audio->forceDisableCh[3] = false; audio->forceDisableChA = false; audio->forceDisableChB = false; + audio->masterVolume = GBA_AUDIO_VOLUME_MAX; } void GBAAudioReset(struct GBAAudio* audio) {@@ -726,7 +728,7 @@ sample = 0x3FF;
} else if (sample < 0) { sample = 0; } - return (sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) << 5; + return ((sample - GBARegisterSOUNDBIASGetBias(audio->soundbias)) * audio->masterVolume) >> 3; } static void _sample(struct GBAAudio* audio) {
@@ -21,6 +21,7 @@
struct GBADMA; extern const unsigned GBA_AUDIO_SAMPLES; +extern const int GBA_AUDIO_VOLUME_MAX; DECL_BITFIELD(GBAAudioRegisterEnvelope, uint16_t); DECL_BITS(GBAAudioRegisterEnvelope, Length, 0, 6);@@ -240,6 +241,7 @@
bool forceDisableCh[4]; bool forceDisableChA; bool forceDisableChB; + int masterVolume; }; struct GBAStereoSample {
@@ -179,6 +179,7 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
_lookupCharValue(config, "bios", &opts->bios); _lookupIntValue(config, "logLevel", &opts->logLevel); _lookupIntValue(config, "frameskip", &opts->frameskip); + _lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); _lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);@@ -202,6 +203,9 @@ opts->lockAspectRatio = fakeBool;
} if (_lookupIntValue(config, "resampleVideo", &fakeBool)) { opts->resampleVideo = fakeBool; + } + if (_lookupIntValue(config, "mute", &fakeBool)) { + opts->mute = fakeBool; } if (_lookupIntValue(config, "skipBios", &fakeBool)) { opts->skipBios = fakeBool;@@ -243,6 +247,8 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen); ConfigurationSetIntValue(&config->defaultsTable, 0, "width", opts->width); ConfigurationSetIntValue(&config->defaultsTable, 0, "height", opts->height); + ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume); + ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute); ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio); ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
@@ -36,6 +36,9 @@ int height;
bool lockAspectRatio; bool resampleVideo; + int volume; + bool mute; + bool videoSync; bool audioSync;
@@ -233,6 +233,15 @@ }
GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers); + if (threadContext->volume == 0) { + threadContext->volume = GBA_AUDIO_VOLUME_MAX; + } + if (threadContext->mute) { + gba.audio.volume = 0; + } else { + gba.audio.volume = threadContext->volume; + } + gba.keySource = &threadContext->activeKeys; if (threadContext->startCallback) {@@ -316,6 +325,8 @@ } else {
threadContext->bios = 0; } threadContext->frameskip = opts->frameskip; + threadContext->volume = opts->volume; + threadContext->mute = opts->mute; threadContext->logLevel = opts->logLevel; if (opts->rewindEnable) { threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
@@ -80,6 +80,8 @@ int frameskip;
float fpsTarget; size_t audioBuffers; bool skipBios; + int volume; + bool mute; // Threading state Thread thread;
@@ -108,6 +108,7 @@ m_opts.audioSync = GameController::AUDIO_SYNC;
m_opts.videoSync = GameController::VIDEO_SYNC; m_opts.fpsTarget = 60; m_opts.audioBuffers = 2048; + m_opts.volume = GBA_AUDIO_VOLUME_MAX; m_opts.logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL; m_opts.rewindEnable = false; m_opts.rewindBufferInterval = 0;
@@ -186,6 +186,8 @@ setVideoSync(opts->videoSync);
setSkipBIOS(opts->skipBios); setUseBIOS(opts->useBios); setRewind(opts->rewindEnable, opts->rewindBufferCapacity, opts->rewindBufferInterval); + setVolume(opts->volume); + setMute(opts->mute); threadInterrupt(); m_threadContext.idleOptimization = opts->idleOptimization;@@ -494,6 +496,24 @@ }
void GameController::setFrameskip(int skip) { m_threadContext.frameskip = skip; +} + +void GameController::setVolume(int volume) { + threadInterrupt(); + m_threadContext.volume = volume; + if (m_gameOpen) { + m_threadContext.gba->audio.masterVolume = volume; + } + threadContinue(); +} + +void GameController::setMute(bool mute) { + threadInterrupt(); + m_threadContext.mute = mute; + if (m_gameOpen) { + m_threadContext.gba->audio.masterVolume = mute ? 0 : m_threadContext.volume; + } + threadContinue(); } void GameController::setTurbo(bool set, bool forced) {
@@ -114,6 +114,8 @@ void saveState(int slot);
void setVideoSync(bool); void setAudioSync(bool); void setFrameskip(int); + void setVolume(int); + void setMute(bool); void setTurbo(bool, bool forced = true); void setAVStream(GBAAVStream*); void clearAVStream();
@@ -27,6 +27,8 @@ loadSetting("audioSync", m_ui.audioSync);
loadSetting("frameskip", m_ui.frameskip); loadSetting("fpsTarget", m_ui.fpsTarget); loadSetting("lockAspectRatio", m_ui.lockAspectRatio); + loadSetting("volume", m_ui.volume); + loadSetting("mute", m_ui.mute); loadSetting("rewindEnable", m_ui.rewind); loadSetting("rewindBufferInterval", m_ui.rewindInterval); loadSetting("rewindBufferCapacity", m_ui.rewindCapacity);@@ -78,6 +80,8 @@ saveSetting("audioSync", m_ui.audioSync);
saveSetting("frameskip", m_ui.frameskip); saveSetting("fpsTarget", m_ui.fpsTarget); saveSetting("lockAspectRatio", m_ui.lockAspectRatio); + saveSetting("volume", m_ui.volume); + saveSetting("mute", m_ui.mute); saveSetting("rewindEnable", m_ui.rewind); saveSetting("rewindBufferInterval", m_ui.rewindInterval); saveSetting("rewindBufferCapacity", m_ui.rewindCapacity);@@ -121,6 +125,10 @@ void SettingsView::saveSetting(const char* key, const QLineEdit* field) {
saveSetting(key, field->text()); } +void SettingsView::saveSetting(const char* key, const QSlider* field) { + saveSetting(key, QString::number(field->value())); +} + void SettingsView::saveSetting(const char* key, const QSpinBox* field) { saveSetting(key, field->cleanText()); }@@ -142,6 +150,11 @@
void SettingsView::loadSetting(const char* key, QLineEdit* field) { QString option = loadSetting(key); field->setText(option); +} + +void SettingsView::loadSetting(const char* key, QSlider* field) { + QString option = loadSetting(key); + field->setValue(option.toInt()); } void SettingsView::loadSetting(const char* key, QSpinBox* field) {
@@ -36,12 +36,14 @@
void saveSetting(const char* key, const QAbstractButton*); void saveSetting(const char* key, const QComboBox*); void saveSetting(const char* key, const QLineEdit*); + void saveSetting(const char* key, const QSlider*); void saveSetting(const char* key, const QSpinBox*); void saveSetting(const char* key, const QString&); void loadSetting(const char* key, QAbstractButton*); void loadSetting(const char* key, QComboBox*); void loadSetting(const char* key, QLineEdit*); + void loadSetting(const char* key, QSlider*); void loadSetting(const char* key, QSpinBox*); QString loadSetting(const char* key); };
@@ -6,8 +6,8 @@ <property name="geometry">
<rect> <x>0</x> <y>0</y> - <width>360</width> - <height>569</height> + <width>374</width> + <height>608</height> </rect> </property> <property name="sizePolicy">@@ -146,13 +146,54 @@ </item>
</layout> </item> <item row="6" column="0"> + <widget class="QLabel" name="label_16"> + <property name="text"> + <string>Volume:</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QSlider" name="volume"> + <property name="maximum"> + <number>256</number> + </property> + <property name="pageStep"> + <number>16</number> + </property> + <property name="value"> + <number>256</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="mute"> + <property name="text"> + <string>Mute</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="7" column="0" colspan="2"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="8" column="0"> <widget class="QLabel" name="label_2"> <property name="text"> <string>Sync:</string> </property> </widget> </item> - <item row="6" column="1"> + <item row="8" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_10"> <item> <widget class="QCheckBox" name="videoSync">@@ -170,14 +211,14 @@ </widget>
</item> </layout> </item> - <item row="7" column="0"> + <item row="9" column="0"> <widget class="QLabel" name="label_9"> <property name="text"> <string>Frameskip:</string> </property> </widget> </item> - <item row="7" column="1"> + <item row="9" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_16"> <item> <widget class="QLabel" name="label_12">@@ -198,14 +239,14 @@ </widget>
</item> </layout> </item> - <item row="8" column="0"> + <item row="10" column="0"> <widget class="QLabel" name="label_3"> <property name="text"> <string>FPS target:</string> </property> </widget> </item> - <item row="8" column="1"> + <item row="10" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QSpinBox" name="fpsTarget">@@ -226,49 +267,42 @@ </widget>
</item> </layout> </item> - <item row="9" column="0" colspan="2"> - <widget class="Line" name="line"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - </widget> - </item> - <item row="10" column="1"> + <item row="11" column="1"> <widget class="QCheckBox" name="lockAspectRatio"> <property name="text"> <string>Lock aspect ratio</string> </property> </widget> </item> - <item row="11" column="1"> + <item row="12" column="1"> <widget class="QCheckBox" name="resampleVideo"> <property name="text"> <string>Resample video</string> </property> </widget> </item> - <item row="12" column="0" colspan="2"> + <item row="13" column="0" colspan="2"> <widget class="Line" name="line_3"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="13" column="1"> + <item row="14" column="1"> <widget class="QCheckBox" name="rewind"> <property name="text"> <string>Enable rewind</string> </property> </widget> </item> - <item row="14" column="0"> + <item row="15" column="0"> <widget class="QLabel" name="label_4"> <property name="text"> <string>Rewind interval:</string> </property> </widget> </item> - <item row="14" column="1"> + <item row="15" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_12"> <item> <widget class="QLabel" name="label_5">@@ -289,14 +323,14 @@ </widget>
</item> </layout> </item> - <item row="15" column="0"> + <item row="16" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> <string>Rewind length:</string> </property> </widget> </item> - <item row="15" column="1"> + <item row="16" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_13"> <item> <widget class="QSpinBox" name="rewindCapacity"/>@@ -310,21 +344,28 @@ </widget>
</item> </layout> </item> - <item row="17" column="0" colspan="2"> + <item row="17" column="1"> + <widget class="QCheckBox" name="allowOpposingDirections"> + <property name="text"> + <string>Allow opposing input directions</string> + </property> + </widget> + </item> + <item row="18" column="0" colspan="2"> <widget class="Line" name="line_4"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="18" column="0"> + <item row="19" column="0"> <widget class="QLabel" name="label_15"> <property name="text"> <string>Idle loops</string> </property> </widget> </item> - <item row="18" column="1"> + <item row="19" column="1"> <widget class="QComboBox" name="idleOptimization"> <item> <property name="text">@@ -341,13 +382,6 @@ <property name="text">
<string>Detect and remove</string> </property> </item> - </widget> - </item> - <item row="16" column="1"> - <widget class="QCheckBox" name="allowOpposingDirections"> - <property name="text"> - <string>Allow opposing input directions</string> - </property> </widget> </item> </layout>
@@ -891,9 +891,14 @@ skipBios->connect([this](const QVariant& value) {
m_controller->setSkipBIOS(value.toBool()); }, this); - ConfigOption* useBios = m_config->addOption("useBios"); - useBios->connect([this](const QVariant& value) { - m_controller->setUseBIOS(value.toBool()); + ConfigOption* volume = m_config->addOption("volume"); + volume->connect([this](const QVariant& value) { + m_controller->setVolume(value.toInt()); + }, this); + + ConfigOption* mute = m_config->addOption("mute"); + mute->connect([this](const QVariant& value) { + m_controller->setMute(value.toBool()); }, this); ConfigOption* rewindEnable = m_config->addOption("rewindEnable");