mirror of https://github.com/mgba-emu/mgba.git
Volume control
This commit is contained in:
parent
3182b5e35d
commit
8266f54d76
1
CHANGES
1
CHANGES
|
@ -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 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
|
|||
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 @@ static int _applyBias(struct GBAAudio* audio, int sample) {
|
|||
} 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 @@ struct GBAAudio {
|
|||
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);
|
||||
|
@ -203,6 +204,9 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
|
|||
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 @@ void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* op
|
|||
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 @@ struct GBAOptions {
|
|||
bool lockAspectRatio;
|
||||
bool resampleVideo;
|
||||
|
||||
int volume;
|
||||
bool mute;
|
||||
|
||||
bool videoSync;
|
||||
bool audioSync;
|
||||
|
||||
|
|
|
@ -233,6 +233,15 @@ static THREAD_ENTRY _GBAThreadRun(void* context) {
|
|||
|
||||
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 @@ void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* thr
|
|||
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 @@ struct GBAThread {
|
|||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
bool skipBios;
|
||||
int volume;
|
||||
bool mute;
|
||||
|
||||
// Threading state
|
||||
Thread thread;
|
||||
|
|
|
@ -108,6 +108,7 @@ ConfigController::ConfigController(QObject* parent)
|
|||
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 @@ void GameController::setOptions(const GBAOptions* opts) {
|
|||
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;
|
||||
|
@ -496,6 +498,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) {
|
||||
if (m_turboForced && !forced) {
|
||||
return;
|
||||
|
|
|
@ -114,6 +114,8 @@ public slots:
|
|||
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 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
|
|||
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 @@ void SettingsView::updateConfig() {
|
|||
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());
|
||||
}
|
||||
|
@ -144,6 +152,11 @@ void SettingsView::loadSetting(const char* key, QLineEdit* field) {
|
|||
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) {
|
||||
QString option = loadSetting(key);
|
||||
field->setValue(option.toInt());
|
||||
|
|
|
@ -36,12 +36,14 @@ private:
|
|||
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 @@
|
|||
<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 @@
|
|||
</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 @@
|
|||
</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 @@
|
|||
</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 @@
|
|||
</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 @@
|
|||
</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 @@
|
|||
</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">
|
||||
|
@ -343,13 +384,6 @@
|
|||
</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>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -891,9 +891,14 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
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");
|
||||
|
|
Loading…
Reference in New Issue