Introduce RMG-Audio

This commit is contained in:
Rosalie Wanders committed 2021-12-26 16:56:22 +01:00
1 parent e7b0147eb4
commit be03f352dd
7 files changed
+603

No files matched your search

+4
View File
@@ -19,9 +19,13 @@ set(INSTALL_PATH Bin/${CMAKE_BUILD_TYPE})
add_subdirectory(Source/RMG-Core)
add_subdirectory(Source/RMG)
add_subdirectory(Source/RMG-Audio)
install(TARGETS RMG
DESTINATION ${INSTALL_PATH}
)
install(TARGETS RMG-Audio
DESTINATION ${INSTALL_PATH}/Plugin/Audio
)
if (WIN32)
add_subdirectory(Source/Installer)
+36
View File
@@ -0,0 +1,36 @@
#
# RMG-Audio CMakeLists.txt
#
project(RMG-Audio)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt5 COMPONENTS Gui Widgets Core REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
set(RMG_AUDIO_SOURCES
UserInterface/MainDialog.cpp
UserInterface/MainDialog.ui
main.cpp
)
add_library(RMG-Audio SHARED ${RMG_AUDIO_SOURCES})
set_target_properties(RMG-Audio PROPERTIES PREFIX "")
target_link_libraries(RMG-Audio
RMG-Core
${SDL2_LIBRARIES}
Qt5::Gui Qt5::Widgets
)
target_include_directories(RMG-Audio PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../
${SDL2_INCLUDE_DIRS}
)
+2
View File
@@ -0,0 +1,2 @@
# Rosalie's Mupen GUI - Audio Plugin
This is a simple SDL2 audio plugin, based on https://github.com/m64p/mupen64plus-audio-sdl2
@@ -0,0 +1,46 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "MainDialog.hpp"
#include <RMG-Core/Core.hpp>
using namespace UserInterface;
MainDialog::MainDialog(QWidget* parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
this->volumeSlider->setValue(CoreSettingsGetIntValue(SettingsID::Audio_Volume));
this->mutedCheckbox->setChecked(CoreSettingsGetBoolValue(SettingsID::Audio_Muted));
}
MainDialog::~MainDialog()
{
}
void MainDialog::on_volumeSlider_valueChanged(int value)
{
this->volumeLabel->setText(QString::number(value) + "%");
}
void MainDialog::on_buttonBox_clicked(QAbstractButton* button)
{
QPushButton *pushButton = (QPushButton *)button;
QPushButton *okButton = this->buttonBox->button(QDialogButtonBox::Ok);
int volume = this->volumeSlider->value();
bool muted = this->mutedCheckbox->isChecked();
if (pushButton == okButton)
{
CoreSettingsSetValue(SettingsID::Audio_Volume, volume);
CoreSettingsSetValue(SettingsID::Audio_Muted, muted);
CoreSettingsSave();
}
}
@@ -0,0 +1,33 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QDialog>
#include <QAbstractButton>
#include "ui_MainDialog.h"
namespace UserInterface
{
class MainDialog : public QDialog, private Ui::MainDialog
{
Q_OBJECT
public:
MainDialog(QWidget *parent);
~MainDialog(void);
private slots:
void on_volumeSlider_valueChanged(int value);
void on_buttonBox_clicked(QAbstractButton* button);
};
}
#endif // MAINWINDOW_HPP
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainDialog</class>
<widget class="QDialog" name="MainDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>330</width>
<height>476</height>
</rect>
</property>
<property name="windowTitle">
<string>Rosalie's Mupen GUI - Audio Plugin</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QSlider" name="volumeSlider">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="pageStep">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>0%</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="mutedCheckbox">
<property name="text">
<string>Muted</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MainDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>MainDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+351
View File
@@ -0,0 +1,351 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#define CORE_PLUGIN 1
#define M64P_PLUGIN_PROTOTYPES 1
#define AUDIO_PLUGIN_API_VERSION 0x020100
#include <SDL.h>
#include <SDL_audio.h>
#include <UserInterface/MainDialog.hpp>
#include <RMG-Core/Core.hpp>
//
// Local variables
//
static SDL_AudioDeviceID l_SDLDevice;
static SDL_AudioSpec* l_HardwareSpec = nullptr;
static bool l_PluginInit = false;
static int l_GameFreq = 0;
static AUDIO_INFO l_AudioInfo;
static bool l_VolIsMuted = false;
static bool l_Paused = false;
static bool l_FastForward = false;
static int l_VolSDL = SDL_MIX_MAXVOLUME;
static SDL_AudioStream* l_AudioStream = nullptr;
static uint8_t l_PrimaryBuffer[0x40000];
static uint8_t l_OutputBuffer[0x40000];
static uint8_t l_MixBuffer[0x40000];
//
// Local Functions
//
static void load_settings(void)
{
l_VolIsMuted = CoreSettingsGetBoolValue(SettingsID::Audio_Muted);
l_VolSDL = SDL_MIX_MAXVOLUME * CoreSettingsGetIntValue(SettingsID::Audio_Volume) / 100;
l_FastForward = false;
}
//
// Basic Plugin Functions
//
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, void (*DebugCallback)(void *, int, const char *))
{
if (l_PluginInit)
{
return M64ERR_ALREADY_INIT;
}
if (SDL_Init(SDL_INIT_AUDIO) < 0)
{
return M64ERR_SYSTEM_FAIL;
}
if (!CoreInit(CoreLibHandle))
{
return M64ERR_SYSTEM_FAIL;
}
load_settings();
l_PluginInit = true;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginShutdown(void)
{
if (!l_PluginInit)
{
return M64ERR_NOT_INIT;
}
if (l_HardwareSpec != nullptr)\
{
free(l_HardwareSpec);
l_HardwareSpec = NULL;
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
l_PluginInit = false;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *pluginType, int *pluginVersion,
int *apiVersion, const char **pluginNamePtr, int *capabilities)
{
if (pluginType != nullptr)
{
*pluginType = M64PLUGIN_AUDIO;
}
if (pluginVersion != nullptr)
{
*pluginVersion = 0x010000;
}
if (apiVersion != nullptr)
{
*apiVersion = AUDIO_PLUGIN_API_VERSION;
}
if (pluginNamePtr != nullptr)
{
*pluginNamePtr = "Rosalie's Mupen GUI - Audio Plugin";
}
if (capabilities != nullptr)
{
*capabilities = 0;
}
return M64ERR_SUCCESS;
}
//
// Custom Plugin Functions
//
EXPORT m64p_error CALL PluginConfig(void)
{
if (!l_PluginInit)
{
return M64ERR_NOT_INIT;
}
UserInterface::MainDialog dialog(nullptr);
dialog.exec();
// reload settings
load_settings();
return M64ERR_SUCCESS;
}
//
// Audio Plugin Functions
//
EXPORT void CALL AiDacrateChanged( int SystemType )
{
if (!l_PluginInit)
{
return;
}
switch (SystemType)
{
case SYSTEM_NTSC:
l_GameFreq = 48681812 / (*l_AudioInfo.AI_DACRATE_REG + 1);
break;
case SYSTEM_PAL:
l_GameFreq = 49656530 / (*l_AudioInfo.AI_DACRATE_REG + 1);
break;
case SYSTEM_MPAL:
l_GameFreq = 48628316 / (*l_AudioInfo.AI_DACRATE_REG + 1);
break;
}
SDL_FreeAudioStream(l_AudioStream);
l_AudioStream = SDL_NewAudioStream(AUDIO_S16SYS, 2, l_GameFreq, l_HardwareSpec->format, 2, l_HardwareSpec->freq);
}
EXPORT void CALL AiLenChanged( void )
{
if (!l_PluginInit)
{
return;
}
unsigned int LenReg = *l_AudioInfo.AI_LEN_REG;
unsigned char *p = l_AudioInfo.RDRAM + (*l_AudioInfo.AI_DRAM_ADDR_REG & 0xFFFFFF);
unsigned int i;
for ( i = 0 ; i < LenReg ; i += 4 )
{
// Left channel
l_PrimaryBuffer[ i ] = p[ i + 2 ];
l_PrimaryBuffer[ i + 1 ] = p[ i + 3 ];
// Right channel
l_PrimaryBuffer[ i + 2 ] = p[ i ];
l_PrimaryBuffer[ i + 3 ] = p[ i + 1 ];
}
if (!l_VolIsMuted && !l_FastForward)
{
unsigned int audio_queued = SDL_GetQueuedAudioSize(l_SDLDevice);
unsigned int acceptable_latency = (l_HardwareSpec->freq * 0.2) * 4;
unsigned int min_latency = (l_HardwareSpec->freq * 0.02) * 4;
if (!l_Paused && audio_queued < min_latency)
{
SDL_PauseAudioDevice(l_SDLDevice, 1);
l_Paused = true;
}
else if (l_Paused && audio_queued >= (min_latency * 2))
{
SDL_PauseAudioDevice(l_SDLDevice, 0);
l_Paused = true;
}
if (audio_queued < acceptable_latency)
{
SDL_AudioStreamPut(l_AudioStream, l_PrimaryBuffer, LenReg);
}
int output_length = SDL_AudioStreamGet(l_AudioStream, l_OutputBuffer, sizeof(l_OutputBuffer));
if (output_length > 0)
{
SDL_memset(l_MixBuffer, 0, output_length);
SDL_MixAudioFormat(l_MixBuffer, l_OutputBuffer, l_HardwareSpec->format, output_length, l_VolSDL);
SDL_QueueAudio(l_SDLDevice, l_MixBuffer, output_length);
}
}
}
EXPORT int CALL InitiateAudio( AUDIO_INFO Audio_Info )
{
if (!l_PluginInit)
{
return 0;
}
l_GameFreq = 33600;
l_AudioInfo = Audio_Info;
return 1;
}
EXPORT int CALL RomOpen(void)
{
if (!l_PluginInit)
{
return 0;
}
SDL_AudioSpec *desired, *obtained;
if (l_HardwareSpec != nullptr)
{
free(l_HardwareSpec);
}
desired = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
obtained = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
desired->freq = 44100;
desired->format = AUDIO_S16SYS;
desired->channels = 2;
desired->samples = 1024;
desired->callback = nullptr;
desired->userdata = nullptr;
const char *dev_name = SDL_GetAudioDeviceName(-1, 0);
l_SDLDevice = SDL_OpenAudioDevice(dev_name, 0, desired, obtained, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
free(desired);
l_HardwareSpec = obtained;
SDL_PauseAudioDevice(l_SDLDevice, 0);
l_Paused = 0;
l_AudioStream = SDL_NewAudioStream(AUDIO_S16SYS, 2, l_GameFreq, l_HardwareSpec->format, 2, l_HardwareSpec->freq);
return 1;
}
EXPORT void CALL RomClosed( void )
{
if (!l_PluginInit)
{
return;
}
SDL_ClearQueuedAudio(l_SDLDevice);
SDL_CloseAudioDevice(l_SDLDevice);
if (l_HardwareSpec != nullptr)
{
free(l_HardwareSpec);
l_HardwareSpec = nullptr;
}
SDL_FreeAudioStream(l_AudioStream);
l_AudioStream = nullptr;
}
EXPORT void CALL ProcessAList(void)
{
}
EXPORT void CALL SetSpeedFactor(int percentage)
{
if (percentage > 100)
{
l_FastForward = true;
}
else
{
l_FastForward = false;
}
}
EXPORT void CALL VolumeMute(void)
{
if (!l_PluginInit)
{
return;
}
l_VolIsMuted = !l_VolIsMuted;
}
EXPORT void CALL VolumeUp(void)
{
}
EXPORT void CALL VolumeDown(void)
{
}
EXPORT int CALL VolumeGetLevel(void)
{
return l_VolIsMuted ? 0 : 100;
}
EXPORT void CALL VolumeSetLevel(int level)
{
if (level < 0)
{
level = 0;
}
else if (level > 100)
{
level = 100;
}
l_VolSDL = SDL_MIX_MAXVOLUME * level / 100;
}
EXPORT const char * CALL VolumeGetString(void)
{
return "";
}