mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
Merge pull request #17175 from hrydgard/more-audio-refactor
Some more audio refactoring
This commit is contained in:
commit
b6b6066ee5
@ -75,8 +75,7 @@ void NativeRender(GraphicsContext *graphicsContext);
|
||||
// the rest of the game, so be careful with synchronization.
|
||||
// Returns the number of samples actually output. The app should do everything it can
|
||||
// to fill the buffer completely.
|
||||
int NativeMix(short *audio, int num_samples);
|
||||
void NativeSetMixer(void* mixer);
|
||||
int NativeMix(short *audio, int num_samples, int sampleRateHz);
|
||||
|
||||
// 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
|
||||
|
@ -49,6 +49,33 @@
|
||||
|
||||
StereoResampler resampler;
|
||||
|
||||
// numFrames is number of stereo frames.
|
||||
// This is called from *outside* the emulator thread.
|
||||
int __AudioMix(short *outstereo, int numFrames, int sampleRate) {
|
||||
return resampler.Mix(outstereo, numFrames, false, sampleRate);
|
||||
}
|
||||
|
||||
void __AudioGetDebugStats(char *buf, size_t bufSize) {
|
||||
resampler.GetAudioDebugStats(buf, bufSize);
|
||||
}
|
||||
|
||||
void __AudioClear() {
|
||||
resampler.Clear();
|
||||
}
|
||||
|
||||
void __AudioPushSamples(const s32 *audio, int numSamples) {
|
||||
if (audio) {
|
||||
resampler.PushSamples(audio, numSamples);
|
||||
} else {
|
||||
resampler.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void __AudioResetStatCounters() {
|
||||
resampler.ResetStatCounters();
|
||||
}
|
||||
|
||||
|
||||
// Should be used to lock anything related to the outAudioQueue.
|
||||
// atomic locks are used on the lock. TODO: make this lock-free
|
||||
std::atomic_flag atomicLock_;
|
||||
@ -109,7 +136,7 @@ static void __AudioCPUMHzChange() {
|
||||
|
||||
|
||||
void __AudioInit() {
|
||||
resampler.ResetStatCounters();
|
||||
__AudioResetStatCounters();
|
||||
mixFrequency = 44100;
|
||||
srcFrequency = 0;
|
||||
|
||||
@ -132,7 +159,7 @@ void __AudioInit() {
|
||||
clampedMixBuffer = new s16[hwBlockSize * 2];
|
||||
memset(mixBuffer, 0, hwBlockSize * 2 * sizeof(s32));
|
||||
|
||||
resampler.Clear();
|
||||
__AudioClear();
|
||||
CoreTiming::RegisterMHzChangeCallback(&__AudioCPUMHzChange);
|
||||
}
|
||||
|
||||
@ -155,15 +182,18 @@ void __AudioDoState(PointerWrap &p) {
|
||||
mixFrequency = 44100;
|
||||
}
|
||||
|
||||
// TODO: This never happens because maxVer=1.
|
||||
if (s >= 2) {
|
||||
resampler.DoState(p);
|
||||
// TODO: Next time we bump, get rid of this. It's kinda useless.
|
||||
StereoResampler::DoState(p);
|
||||
if (p.mode == p.MODE_READ) {
|
||||
__AudioClear();
|
||||
}
|
||||
} else {
|
||||
// Only to preserve the previous file format. Might cause a slight audio glitch on upgrades?
|
||||
FixedSizeQueue<s16, 512 * 16> outAudioQueue;
|
||||
outAudioQueue.DoState(p);
|
||||
|
||||
resampler.Clear();
|
||||
__AudioClear();
|
||||
}
|
||||
|
||||
int chanCount = ARRAY_SIZE(chans);
|
||||
@ -333,9 +363,7 @@ void __AudioSetSRCFrequency(int freq) {
|
||||
srcFrequency = freq;
|
||||
}
|
||||
|
||||
// Mix samples from the various audio channels into a single sample queue.
|
||||
// This single sample queue is where __AudioMix should read from. If the sample queue is full, we should
|
||||
// just sleep the main emulator thread a little.
|
||||
// Mix samples from the various audio channels into a single sample queue, managed by the backend implementation.
|
||||
void __AudioUpdate(bool resetRecording) {
|
||||
// Audio throttle doesn't really work on the PSP since the mixing intervals are so closely tied
|
||||
// to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
|
||||
@ -427,7 +455,7 @@ void __AudioUpdate(bool resetRecording) {
|
||||
}
|
||||
|
||||
if (g_Config.bEnableSound) {
|
||||
resampler.PushSamples(mixBuffer, hwBlockSize);
|
||||
__AudioPushSamples(mixBuffer, hwBlockSize);
|
||||
#ifndef MOBILE_DEVICE
|
||||
if (g_Config.bSaveLoadResetsAVdumping && resetRecording) {
|
||||
__StopLogAudio();
|
||||
@ -465,23 +493,6 @@ void __AudioUpdate(bool resetRecording) {
|
||||
}
|
||||
}
|
||||
|
||||
// numFrames is number of stereo frames.
|
||||
// This is called from *outside* the emulator thread.
|
||||
int __AudioMix(short *outstereo, int numFrames, int sampleRate) {
|
||||
return resampler.Mix(outstereo, numFrames, false, sampleRate);
|
||||
}
|
||||
|
||||
void __AudioGetDebugStats(char *buf, size_t bufSize) {
|
||||
resampler.GetAudioDebugStats(buf, bufSize);
|
||||
}
|
||||
|
||||
void __PushExternalAudio(const s32 *audio, int numSamples) {
|
||||
if (audio) {
|
||||
resampler.PushSamples(audio, numSamples);
|
||||
} else {
|
||||
resampler.Clear();
|
||||
}
|
||||
}
|
||||
#ifndef MOBILE_DEVICE
|
||||
void __StartLogAudio(const Path& filename) {
|
||||
if (!m_logAudio) {
|
||||
|
@ -46,9 +46,12 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);
|
||||
void __AudioWakeThreads(AudioChannel &chan, int result, int step);
|
||||
void __AudioWakeThreads(AudioChannel &chan, int result);
|
||||
|
||||
// Resampler API, to be extracted
|
||||
int __AudioMix(short *outstereo, int numSamples, int sampleRate);
|
||||
void __AudioGetDebugStats(char *buf, size_t bufSize);
|
||||
void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu!
|
||||
void __AudioClear();
|
||||
void __AudioPushSamples(const s32 *audio, int numSamples); // Should not be used in-game, only at the menu!
|
||||
void __AudioResetStatCounters();
|
||||
|
||||
int __AudioGetHostAttemptBlockSize();
|
||||
|
||||
|
@ -346,8 +346,4 @@ void StereoResampler::ResetStatCounters() {
|
||||
|
||||
void StereoResampler::DoState(PointerWrap &p) {
|
||||
auto s = p.Section("resampler", 1);
|
||||
if (!s)
|
||||
return;
|
||||
if (p.mode == p.MODE_READ)
|
||||
Clear();
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ public:
|
||||
|
||||
void Clear();
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
// TODO: Get rid of this.
|
||||
static void DoState(PointerWrap &p);
|
||||
|
||||
void GetAudioDebugStats(char *buf, size_t bufSize);
|
||||
void ResetStatCounters();
|
||||
|
@ -54,6 +54,13 @@
|
||||
#include <signal.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;
|
||||
static float refreshRate = 60.f;
|
||||
static int browseFileEvent = -1;
|
||||
@ -69,7 +76,7 @@ SDL_AudioSpec g_retFmt;
|
||||
static SDL_AudioDeviceID audioDev = 0;
|
||||
|
||||
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
|
||||
NativeMix((short *)stream, len / 4);
|
||||
NativeMix((short *)stream, len / 4, AUDIO_FREQ);
|
||||
}
|
||||
|
||||
static void InitSDLAudioDevice() {
|
||||
@ -726,12 +733,6 @@ void MainUI::updateAccelerometer() {
|
||||
}
|
||||
|
||||
#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() {
|
||||
if (feed != nullptr) {
|
||||
@ -770,7 +771,7 @@ void MainAudio::run() {
|
||||
|
||||
void MainAudio::timerEvent(QTimerEvent *) {
|
||||
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)
|
||||
feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ static int g_QuitRequested = 0;
|
||||
static int g_DesktopWidth = 0;
|
||||
static int g_DesktopHeight = 0;
|
||||
static float g_RefreshRate = 60.f;
|
||||
static int g_sampleRate = 44100;
|
||||
|
||||
static SDL_AudioSpec g_retFmt;
|
||||
|
||||
@ -92,7 +93,7 @@ int getDisplayNumber(void) {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -101,7 +102,7 @@ static SDL_AudioDeviceID audioDev = 0;
|
||||
static void InitSDLAudioDevice(const std::string &name = "") {
|
||||
SDL_AudioSpec fmt;
|
||||
memset(&fmt, 0, sizeof(fmt));
|
||||
fmt.freq = 44100;
|
||||
fmt.freq = g_sampleRate;
|
||||
fmt.format = AUDIO_S16;
|
||||
fmt.channels = 2;
|
||||
fmt.samples = 256;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Common/Data/Collections/FixedSizeQueue.h"
|
||||
#include "Core/HW/SimpleAudioDec.h"
|
||||
#include "Core/HLE/__sceAudio.h"
|
||||
#include "Core/System.h"
|
||||
#include "GameInfoCache.h"
|
||||
#include "Core/Config.h"
|
||||
#include "UI/BackgroundAudio.h"
|
||||
@ -333,14 +334,18 @@ void BackgroundAudio::SetGame(const Path &path) {
|
||||
bgGamePath_ = path;
|
||||
}
|
||||
|
||||
int BackgroundAudio::Play() {
|
||||
bool BackgroundAudio::Play() {
|
||||
if (GetUIState() == UISTATE_INGAME) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// Immediately stop the sound if it is turned off while playing.
|
||||
if (!g_Config.bEnableSound) {
|
||||
Clear(true);
|
||||
__PushExternalAudio(0, 0);
|
||||
return 0;
|
||||
__AudioClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
double now = time_now_d();
|
||||
@ -388,7 +393,7 @@ int BackgroundAudio::Play() {
|
||||
}
|
||||
}
|
||||
|
||||
__PushExternalAudio(buffer, sz);
|
||||
__AudioPushSamples(buffer, sz);
|
||||
|
||||
if (at3Reader_ && fadingOut_ && volume_ <= 0.0f) {
|
||||
Clear(true);
|
||||
@ -398,7 +403,7 @@ int BackgroundAudio::Play() {
|
||||
|
||||
lastPlaybackTime_ = now;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BackgroundAudio::Update() {
|
||||
|
@ -17,7 +17,7 @@ public:
|
||||
|
||||
void SetGame(const Path &path);
|
||||
void Update();
|
||||
int Play();
|
||||
bool Play();
|
||||
|
||||
void LoadSamples();
|
||||
void PlaySFX(UI::UISound sfx);
|
||||
|
@ -203,12 +203,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
int Win32Mix(short *buffer, int numSamples, int bits, int rate) {
|
||||
return NativeMix(buffer, numSamples);
|
||||
}
|
||||
#endif
|
||||
|
||||
// globals
|
||||
static LogListener *logger = nullptr;
|
||||
Path boot_filename;
|
||||
@ -235,12 +229,8 @@ std::string NativeQueryConfig(std::string query) {
|
||||
}
|
||||
}
|
||||
|
||||
int NativeMix(short *audio, int num_samples) {
|
||||
if (GetUIState() != UISTATE_INGAME) {
|
||||
g_BackgroundAudio.Play();
|
||||
}
|
||||
int sample_rate = System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE);
|
||||
return __AudioMix(audio, num_samples, sample_rate > 0 ? sample_rate : 44100);
|
||||
int NativeMix(short *audio, int numSamples, int sampleRateHz) {
|
||||
return __AudioMix(audio, numSamples, sampleRateHz);
|
||||
}
|
||||
|
||||
// This is called before NativeInit so we do a little bit of initialization here.
|
||||
@ -860,9 +850,9 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
|
||||
#ifdef _WIN32
|
||||
winAudioBackend = CreateAudioBackend((AudioBackendType)g_Config.iAudioBackend);
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
winAudioBackend->Init(0, &Win32Mix, 44100);
|
||||
winAudioBackend->Init(0, &NativeMix, 44100);
|
||||
#else
|
||||
winAudioBackend->Init(MainWindow::GetHWND(), &Win32Mix, 44100);
|
||||
winAudioBackend->Init(MainWindow::GetHWND(), &NativeMix, 44100);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -1235,6 +1225,7 @@ void NativeUpdate() {
|
||||
g_screenManager->update();
|
||||
|
||||
g_Discord.Update();
|
||||
g_BackgroundAudio.Play();
|
||||
|
||||
UI::SetSoundEnabled(g_Config.bUISound);
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ void XAudioBackend::PollLoop() {
|
||||
// take ownership of the data. It needs to be big enough to fit the max number of buffers we check for
|
||||
// above, which it is, easily.
|
||||
|
||||
int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, 16, sampleRate_);
|
||||
int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, sampleRate_);
|
||||
int numBytesRendered = 2 * sizeof(short) * stereoSamplesRendered;
|
||||
|
||||
XAUDIO2_BUFFER xaudioBuffer{};
|
||||
|
@ -124,7 +124,7 @@ int DSoundAudioBackend::RunThread() {
|
||||
int numBytesToRender = RoundDown128(ModBufferSize(currentPos_ - lastPos_));
|
||||
|
||||
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
|
||||
//If we don't do this then the sound will loop if the sound stops and the mixer writes only zeroes
|
||||
numBytesRendered = numBytesToRender;
|
||||
|
@ -501,11 +501,11 @@ void WASAPIAudioThread::Run() {
|
||||
int chans = deviceFormat_->Format.nChannels;
|
||||
switch (format_) {
|
||||
case Format::IEEE_FLOAT:
|
||||
callback_(shortBuf_, pNumAvFrames, 16, sampleRate_);
|
||||
callback_(shortBuf_, pNumAvFrames, sampleRate_);
|
||||
if (chans == 1) {
|
||||
float *ptr = (float *)pData;
|
||||
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);
|
||||
}
|
||||
} else if (chans == 2) {
|
||||
@ -513,14 +513,14 @@ void WASAPIAudioThread::Run() {
|
||||
} else if (chans > 2) {
|
||||
float *ptr = (float *)pData;
|
||||
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 + 1] = (float)shortBuf_[i * 2 + 1] * (1.0f / 32768.0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Format::PCM16:
|
||||
callback_((short *)pData, pNumAvFrames, 16, sampleRate_);
|
||||
callback_((short *)pData, pNumAvFrames, sampleRate_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
#include "Common/CommonWindows.h"
|
||||
#include "Core/ConfigValues.h"
|
||||
|
||||
// Always 2 channels.
|
||||
typedef int(*StreamCallback)(short *buffer, int numSamples, int bits, int rate);
|
||||
// Always 2 channels, 16-bit audio.
|
||||
typedef int (*StreamCallback)(short *buffer, int numSamples, int rate);
|
||||
|
||||
// Note that the backend may override the passed in sample rate. The actual sample rate
|
||||
// should be returned by GetSampleRate though.
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
|
||||
typedef int (*AndroidAudioCallback)(short *buffer, int numSamples, int sampleRateHz);
|
||||
|
||||
class AudioContext {
|
||||
public:
|
||||
@ -12,6 +12,8 @@ public:
|
||||
virtual bool AudioRecord_Start(int sampleRate) { return false; };
|
||||
virtual bool AudioRecord_Stop() { return false; };
|
||||
|
||||
int SampleRate() const { return sampleRate; }
|
||||
|
||||
virtual ~AudioContext() {}
|
||||
|
||||
protected:
|
||||
|
@ -50,7 +50,7 @@ void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
|
||||
return;
|
||||
}
|
||||
|
||||
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer);
|
||||
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer, SampleRate());
|
||||
|
||||
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
|
||||
int byteCount = (framesPerBuffer - renderedFrames) * 4;
|
||||
|
@ -19,7 +19,7 @@ static volatile BOOL done = 0;
|
||||
#define SAMPLE_SIZE 44100
|
||||
static short stream[SAMPLE_SIZE];
|
||||
|
||||
int NativeMix(short *audio, int num_samples);
|
||||
int NativeMix(short *audio, int numSamples, int sampleRateHz);
|
||||
|
||||
@interface AudioEngine ()
|
||||
|
||||
@ -120,11 +120,12 @@ int NativeMix(short *audio, int num_samples);
|
||||
- (void)audioLoop
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
|
||||
const int sampleRateHz = 44100;
|
||||
while (!done)
|
||||
{
|
||||
size_t frames_ready;
|
||||
if (![self playing])
|
||||
frames_ready = NativeMix(stream, SAMPLE_SIZE / 2);
|
||||
frames_ready = NativeMix(stream, SAMPLE_SIZE / 2, sampleRateHz);
|
||||
else
|
||||
frames_ready = 0;
|
||||
|
||||
@ -132,12 +133,12 @@ int NativeMix(short *audio, int num_samples);
|
||||
{
|
||||
const size_t bytes_ready = frames_ready * sizeof(short) * 2;
|
||||
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);
|
||||
alSourcePlay(source);
|
||||
|
||||
// TODO: Maybe this could get behind?
|
||||
usleep((1000000 * frames_ready) / 44100);
|
||||
usleep((1000000 * frames_ready) / sampleRateHz);
|
||||
}
|
||||
else
|
||||
usleep(100);
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
AudioComponentInstance audioInstance = nil;
|
||||
|
||||
int NativeMix(short *audio, int num_samples);
|
||||
int NativeMix(short *audio, int numSamples, int sampleRate);
|
||||
|
||||
OSStatus iOSCoreAudioCallback(void *inRefCon,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
@ -41,7 +41,7 @@ OSStatus iOSCoreAudioCallback(void *inRefCon,
|
||||
{
|
||||
// see if we have any sound to play
|
||||
short *output = (short *)ioData->mBuffers[0].mData;
|
||||
UInt32 framesReady = NativeMix(output, inNumberFrames);
|
||||
UInt32 framesReady = NativeMix(output, inNumberFrames, SAMPLE_RATE);
|
||||
|
||||
if (framesReady == 0) {
|
||||
// oops, we don't currently have any sound, so return silence
|
||||
|
Loading…
Reference in New Issue
Block a user