mirror of
https://github.com/Rosalie241/RMG.git
synced 2026-11-13 17:48:43 +01:00
Introduce RMG-Audio
This commit is contained in:
1 parent
e7b0147eb4
commit
be03f352dd
7 files changed
+603
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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}
|
||||
)
|
||||
@@ -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>
|
||||
@@ -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 "";
|
||||
}
|
||||
Reference in new issue
Block a user