Add samplerate argument to NativeMix

This commit is contained in:
Henrik Rydgård 2023-03-24 13:59:27 +01:00
parent 8421c1e2a1
commit a5fdf3d05b
13 changed files with 38 additions and 37 deletions

View File

@ -75,8 +75,7 @@ void NativeRender(GraphicsContext *graphicsContext);
// the rest of the game, so be careful with synchronization. // the rest of the game, so be careful with synchronization.
// Returns the number of samples actually output. The app should do everything it can // Returns the number of samples actually output. The app should do everything it can
// to fill the buffer completely. // to fill the buffer completely.
int NativeMix(short *audio, int num_samples); int NativeMix(short *audio, int num_samples, int sampleRateHz);
void NativeSetMixer(void* mixer);
// Called when it's time to shutdown. After this has been called, // Called when it's time to shutdown. After this has been called,
// no more calls to any other function will be made from the framework // no more calls to any other function will be made from the framework

View File

@ -482,6 +482,7 @@ void __PushExternalAudio(const s32 *audio, int numSamples) {
resampler.Clear(); resampler.Clear();
} }
} }
#ifndef MOBILE_DEVICE #ifndef MOBILE_DEVICE
void __StartLogAudio(const Path& filename) { void __StartLogAudio(const Path& filename) {
if (!m_logAudio) { if (!m_logAudio) {

View File

@ -41,6 +41,9 @@ void __AudioShutdown();
void __AudioSetOutputFrequency(int freq); void __AudioSetOutputFrequency(int freq);
void __AudioSetSRCFrequency(int freq); void __AudioSetSRCFrequency(int freq);
typedef void(*AudioUserCallback);
void __AudioSetUserCallback(AudioUserCallback callback);
// May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large // May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking); u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);
void __AudioWakeThreads(AudioChannel &chan, int result, int step); void __AudioWakeThreads(AudioChannel &chan, int result, int step);

View File

@ -54,6 +54,13 @@
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
// Audio
#define AUDIO_FREQ 44100
#define AUDIO_CHANNELS 2
#define AUDIO_SAMPLES 2048
#define AUDIO_SAMPLESIZE 16
#define AUDIO_BUFFERS 5
MainUI *emugl = nullptr; MainUI *emugl = nullptr;
static float refreshRate = 60.f; static float refreshRate = 60.f;
static int browseFileEvent = -1; static int browseFileEvent = -1;
@ -69,7 +76,7 @@ SDL_AudioSpec g_retFmt;
static SDL_AudioDeviceID audioDev = 0; static SDL_AudioDeviceID audioDev = 0;
extern void mixaudio(void *userdata, Uint8 *stream, int len) { extern void mixaudio(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / 4); NativeMix((short *)stream, len / 4, AUDIO_FREQ);
} }
static void InitSDLAudioDevice() { static void InitSDLAudioDevice() {
@ -726,12 +733,6 @@ void MainUI::updateAccelerometer() {
} }
#ifndef SDL #ifndef SDL
// Audio
#define AUDIO_FREQ 44100
#define AUDIO_CHANNELS 2
#define AUDIO_SAMPLES 2048
#define AUDIO_SAMPLESIZE 16
#define AUDIO_BUFFERS 5
MainAudio::~MainAudio() { MainAudio::~MainAudio() {
if (feed != nullptr) { if (feed != nullptr) {
@ -770,7 +771,7 @@ void MainAudio::run() {
void MainAudio::timerEvent(QTimerEvent *) { void MainAudio::timerEvent(QTimerEvent *) {
memset(mixbuf, 0, mixlen); memset(mixbuf, 0, mixlen);
size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES); size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES, AUDIO_FREQ);
if (frames > 0) if (frames > 0)
feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames); feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);
} }

View File

@ -74,6 +74,7 @@ static int g_QuitRequested = 0;
static int g_DesktopWidth = 0; static int g_DesktopWidth = 0;
static int g_DesktopHeight = 0; static int g_DesktopHeight = 0;
static float g_RefreshRate = 60.f; static float g_RefreshRate = 60.f;
static int g_sampleRate = 44100;
static SDL_AudioSpec g_retFmt; static SDL_AudioSpec g_retFmt;
@ -92,7 +93,7 @@ int getDisplayNumber(void) {
} }
void sdl_mixaudio_callback(void *userdata, Uint8 *stream, int len) { void sdl_mixaudio_callback(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / (2 * 2)); NativeMix((short *)stream, len / (2 * 2), g_sampleRate);
} }
static SDL_AudioDeviceID audioDev = 0; static SDL_AudioDeviceID audioDev = 0;
@ -101,7 +102,7 @@ static SDL_AudioDeviceID audioDev = 0;
static void InitSDLAudioDevice(const std::string &name = "") { static void InitSDLAudioDevice(const std::string &name = "") {
SDL_AudioSpec fmt; SDL_AudioSpec fmt;
memset(&fmt, 0, sizeof(fmt)); memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100; fmt.freq = g_sampleRate;
fmt.format = AUDIO_S16; fmt.format = AUDIO_S16;
fmt.channels = 2; fmt.channels = 2;
fmt.samples = 256; fmt.samples = 256;

View File

@ -203,12 +203,6 @@ public:
} }
}; };
#ifdef _WIN32
int Win32Mix(short *buffer, int numSamples, int bits, int rate) {
return NativeMix(buffer, numSamples);
}
#endif
// globals // globals
static LogListener *logger = nullptr; static LogListener *logger = nullptr;
Path boot_filename; Path boot_filename;
@ -235,9 +229,8 @@ std::string NativeQueryConfig(std::string query) {
} }
} }
int NativeMix(short *audio, int num_samples) { int NativeMix(short *audio, int numSamples, int sampleRateHz) {
int sample_rate = System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE); return __AudioMix(audio, numSamples, sampleRateHz);
return __AudioMix(audio, num_samples, sample_rate > 0 ? sample_rate : 44100);
} }
// This is called before NativeInit so we do a little bit of initialization here. // This is called before NativeInit so we do a little bit of initialization here.
@ -859,7 +852,7 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
#if PPSSPP_PLATFORM(UWP) #if PPSSPP_PLATFORM(UWP)
winAudioBackend->Init(0, &Win32Mix, 44100); winAudioBackend->Init(0, &Win32Mix, 44100);
#else #else
winAudioBackend->Init(MainWindow::GetHWND(), &Win32Mix, 44100); winAudioBackend->Init(MainWindow::GetHWND(), &NativeMix, 44100);
#endif #endif
#endif #endif

View File

@ -124,7 +124,7 @@ int DSoundAudioBackend::RunThread() {
int numBytesToRender = RoundDown128(ModBufferSize(currentPos_ - lastPos_)); int numBytesToRender = RoundDown128(ModBufferSize(currentPos_ - lastPos_));
if (numBytesToRender >= 256) { if (numBytesToRender >= 256) {
int numBytesRendered = 4 * (*callback_)(realtimeBuffer_, numBytesToRender >> 2, 16, 44100); int numBytesRendered = 4 * (*callback_)(realtimeBuffer_, numBytesToRender >> 2, 44100);
//We need to copy the full buffer, regardless of what the mixer claims to have filled //We need to copy the full buffer, regardless of what the mixer claims to have filled
//If we don't do this then the sound will loop if the sound stops and the mixer writes only zeroes //If we don't do this then the sound will loop if the sound stops and the mixer writes only zeroes
numBytesRendered = numBytesToRender; numBytesRendered = numBytesToRender;

View File

@ -501,11 +501,11 @@ void WASAPIAudioThread::Run() {
int chans = deviceFormat_->Format.nChannels; int chans = deviceFormat_->Format.nChannels;
switch (format_) { switch (format_) {
case Format::IEEE_FLOAT: case Format::IEEE_FLOAT:
callback_(shortBuf_, pNumAvFrames, 16, sampleRate_); callback_(shortBuf_, pNumAvFrames, sampleRate_);
if (chans == 1) { if (chans == 1) {
float *ptr = (float *)pData; float *ptr = (float *)pData;
memset(ptr, 0, pNumAvFrames * chans * sizeof(float)); memset(ptr, 0, pNumAvFrames * chans * sizeof(float));
for (UINT32 i = 0; i < pNumAvFrames; i++) { for (uint32_t i = 0; i < pNumAvFrames; i++) {
ptr[i * chans + 0] = 0.5f * ((float)shortBuf_[i * 2] + (float)shortBuf_[i * 2 + 1]) * (1.0f / 32768.0f); ptr[i * chans + 0] = 0.5f * ((float)shortBuf_[i * 2] + (float)shortBuf_[i * 2 + 1]) * (1.0f / 32768.0f);
} }
} else if (chans == 2) { } else if (chans == 2) {
@ -513,14 +513,14 @@ void WASAPIAudioThread::Run() {
} else if (chans > 2) { } else if (chans > 2) {
float *ptr = (float *)pData; float *ptr = (float *)pData;
memset(ptr, 0, pNumAvFrames * chans * sizeof(float)); memset(ptr, 0, pNumAvFrames * chans * sizeof(float));
for (UINT32 i = 0; i < pNumAvFrames; i++) { for (uint32_t i = 0; i < pNumAvFrames; i++) {
ptr[i * chans + 0] = (float)shortBuf_[i * 2] * (1.0f / 32768.0f); ptr[i * chans + 0] = (float)shortBuf_[i * 2] * (1.0f / 32768.0f);
ptr[i * chans + 1] = (float)shortBuf_[i * 2 + 1] * (1.0f / 32768.0f); ptr[i * chans + 1] = (float)shortBuf_[i * 2 + 1] * (1.0f / 32768.0f);
} }
} }
break; break;
case Format::PCM16: case Format::PCM16:
callback_((short *)pData, pNumAvFrames, 16, sampleRate_); callback_((short *)pData, pNumAvFrames, sampleRate_);
break; break;
} }
} }

View File

@ -3,8 +3,8 @@
#include "Common/CommonWindows.h" #include "Common/CommonWindows.h"
#include "Core/ConfigValues.h" #include "Core/ConfigValues.h"
// Always 2 channels. // Always 2 channels, 16-bit audio.
typedef int(*StreamCallback)(short *buffer, int numSamples, int bits, int rate); typedef int (*StreamCallback)(short *buffer, int numSamples, int rate);
// Note that the backend may override the passed in sample rate. The actual sample rate // Note that the backend may override the passed in sample rate. The actual sample rate
// should be returned by GetSampleRate though. // should be returned by GetSampleRate though.

View File

@ -3,7 +3,7 @@
#include <string> #include <string>
#include <mutex> #include <mutex>
typedef int (*AndroidAudioCallback)(short *buffer, int num_samples); typedef int (*AndroidAudioCallback)(short *buffer, int numSamples, int sampleRateHz);
class AudioContext { class AudioContext {
public: public:
@ -12,6 +12,8 @@ public:
virtual bool AudioRecord_Start(int sampleRate) { return false; }; virtual bool AudioRecord_Start(int sampleRate) { return false; };
virtual bool AudioRecord_Stop() { return false; }; virtual bool AudioRecord_Stop() { return false; };
int SampleRate() const { return sampleRate; }
virtual ~AudioContext() {} virtual ~AudioContext() {}
protected: protected:

View File

@ -50,7 +50,7 @@ void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
return; return;
} }
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer); int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer, SampleRate());
int sizeInBytes = framesPerBuffer * 2 * sizeof(short); int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
int byteCount = (framesPerBuffer - renderedFrames) * 4; int byteCount = (framesPerBuffer - renderedFrames) * 4;

View File

@ -19,7 +19,7 @@ static volatile BOOL done = 0;
#define SAMPLE_SIZE 44100 #define SAMPLE_SIZE 44100
static short stream[SAMPLE_SIZE]; static short stream[SAMPLE_SIZE];
int NativeMix(short *audio, int num_samples); int NativeMix(short *audio, int numSamples, int sampleRateHz);
@interface AudioEngine () @interface AudioEngine ()
@ -120,11 +120,12 @@ int NativeMix(short *audio, int num_samples);
- (void)audioLoop - (void)audioLoop
{ {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
const int sampleRateHz = 44100;
while (!done) while (!done)
{ {
size_t frames_ready; size_t frames_ready;
if (![self playing]) if (![self playing])
frames_ready = NativeMix(stream, SAMPLE_SIZE / 2); frames_ready = NativeMix(stream, SAMPLE_SIZE / 2, sampleRateHz);
else else
frames_ready = 0; frames_ready = 0;
@ -132,12 +133,12 @@ int NativeMix(short *audio, int num_samples);
{ {
const size_t bytes_ready = frames_ready * sizeof(short) * 2; const size_t bytes_ready = frames_ready * sizeof(short) * 2;
alSourcei(source, AL_BUFFER, 0); alSourcei(source, AL_BUFFER, 0);
alBufferData(buffer, AL_FORMAT_STEREO16, stream, bytes_ready, 44100); alBufferData(buffer, AL_FORMAT_STEREO16, stream, bytes_ready, sampleRateHz);
alSourcei(source, AL_BUFFER, buffer); alSourcei(source, AL_BUFFER, buffer);
alSourcePlay(source); alSourcePlay(source);
// TODO: Maybe this could get behind? // TODO: Maybe this could get behind?
usleep((1000000 * frames_ready) / 44100); usleep((1000000 * frames_ready) / sampleRateHz);
} }
else else
usleep(100); usleep(100);

View File

@ -30,7 +30,7 @@
AudioComponentInstance audioInstance = nil; AudioComponentInstance audioInstance = nil;
int NativeMix(short *audio, int num_samples); int NativeMix(short *audio, int numSamples, int sampleRate);
OSStatus iOSCoreAudioCallback(void *inRefCon, OSStatus iOSCoreAudioCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags, AudioUnitRenderActionFlags *ioActionFlags,
@ -41,7 +41,7 @@ OSStatus iOSCoreAudioCallback(void *inRefCon,
{ {
// see if we have any sound to play // see if we have any sound to play
short *output = (short *)ioData->mBuffers[0].mData; short *output = (short *)ioData->mBuffers[0].mData;
UInt32 framesReady = NativeMix(output, inNumberFrames); UInt32 framesReady = NativeMix(output, inNumberFrames, SAMPLE_RATE);
if (framesReady == 0) { if (framesReady == 0) {
// oops, we don't currently have any sound, so return silence // oops, we don't currently have any sound, so return silence