Remove the return value from NativeMix, correct mixing with other channel numbers than 2

This commit is contained in:
Henrik Rydgård committed 2025-06-18 23:48:00 +02:00
1 parent 5ace3179f8
commit 0502d79c99
9 files changed
+55 -70

No files matched your search

+5 -7
View File
@@ -59,13 +59,11 @@ void NativeMouseDelta(float dx, float dy);
void NativeFrame(GraphicsContext *graphicsContext);
// This should render num_samples 44khz stereo samples.
// Try not to make too many assumptions on the granularity
// of num_samples.
// This function may be called from a totally separate thread from
// 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, int sampleRateHz, void *userdata);
// Try not to make too many assumptions on the granularity of num_samples.
// This function will likely be called from a totally separate thread from
// the rest of the app, so be careful with synchronization.
// The app must fill the buffer completely, doing its own internal buffering if needed.
void NativeMix(short *audio, int num_samples, int sampleRateHz, void *userdata);
// 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
+2 -5
View File
@@ -160,9 +160,9 @@ inline int16_t MixSingleSample(int16_t s1, int16_t s2, uint16_t frac) {
}
// Executed from sound stream thread, pulling sound out of the buffer.
unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sample_rate) {
void StereoResampler::Mix(s16 *samples, unsigned int numSamples, bool consider_framelimit, int sample_rate) {
if (!samples)
return 0;
return;
unsigned int currentSample;
@@ -242,9 +242,6 @@ unsigned int StereoResampler::Mix(short* samples, unsigned int numSamples, bool
// Flush cached variable
indexR_.store(indexR);
// TODO: What should we actually return here?
return currentSample / 2;
}
// Executes on the emulator thread, pushing sound into the buffer.
+1 -1
View File
@@ -32,7 +32,7 @@ public:
~StereoResampler();
// Called from audio threads
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit, int sampleRate);
void Mix(s16 *samples, unsigned int numSamples, bool consider_framelimit, int sampleRate);
// Called from main thread
// This clamps the samples to 16-bit before starting to work on them.
+2 -3
View File
@@ -792,9 +792,8 @@ void MainAudio::run() {
void MainAudio::timerEvent(QTimerEvent *) {
memset(mixbuf, 0, mixlen);
size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES, AUDIO_FREQ);
if (frames > 0)
feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);
NativeMix((short *)mixbuf, AUDIO_BUFFERS * AUDIO_SAMPLES, AUDIO_FREQ);
feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);
}
#endif
+3 -5
View File
@@ -7,13 +7,11 @@ StereoResampler g_resampler;
// numFrames is number of stereo frames.
// This is called from *outside* the emulator thread.
int NativeMix(int16_t *outStereo, int numFrames, int sampleRateHz, void *userdata) {
int validFrames = g_resampler.Mix(outStereo, numFrames, false, sampleRateHz);
void NativeMix(int16_t *outStereo, int numFrames, int sampleRateHz, void *userdata) {
g_resampler.Mix(outStereo, numFrames, false, sampleRateHz);
// Mix sound effects on top.
g_BackgroundAudio.SFX().Mix(outStereo, validFrames, sampleRateHz);
return validFrames;
g_BackgroundAudio.SFX().Mix(outStereo, numFrames, sampleRateHz);
}
void System_AudioGetDebugStats(char *buf, size_t bufSize) {
+14 -6
View File
@@ -803,19 +803,27 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
void CallbackPostRender(UIContext *dc, void *userdata);
bool CreateGlobalPipelines();
// TODO: Add faster special case for channels == 2.
void NativeMixWrapper(float *dest, int framesToWrite, int channels, int sampleRateHz, void *userdata) {
static int16_t *buffer;
static int bufSize;
if (bufSize < framesToWrite * channels) {
buffer = new int16_t[framesToWrite * channels];
bufSize = framesToWrite * channels;
if (bufSize < framesToWrite * 2) {
buffer = new int16_t[framesToWrite * 2];
bufSize = framesToWrite * 2;
}
NativeMix(buffer, framesToWrite, sampleRateHz, userdata);
for (int i = 0; i < framesToWrite * channels; i++) {
int16_t value = buffer[i];
dest[i] = (float)value * (float)(1.0f / 32767.0f);
for (int i = 0; i < framesToWrite; i++) {
int16_t lvalue = buffer[i * 2];
int16_t rvalue = buffer[i * 2 + 1];
dest[i * channels] = (float)lvalue * (float)(1.0f / 32767.0f);
if (channels > 1) {
dest[i * channels + 1] = (float)lvalue * (float)(1.0f / 32767.0f);
}
for (int j = 2; j < channels; j++) {
dest[i * channels + j] = 0.0f;
}
}
}
+13 -7
View File
@@ -10,11 +10,12 @@
#include <vector>
#include <string_view>
#include "Common/Log.h"
#include "WASAPIContext.h"
static std::string ConvertWStringToUTF8(const std::wstring &wstr) {
int len = (int)wstr.size();
int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL);
const int len = (int)wstr.size();
const int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, NULL, NULL);
std::string s;
s.resize(size);
if (size > 0) {
@@ -24,8 +25,8 @@ static std::string ConvertWStringToUTF8(const std::wstring &wstr) {
}
static std::wstring ConvertUTF8ToWString(const std::string_view source) {
int len = (int)source.size();
int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.data(), len, NULL, 0);
const int len = (int)source.size();
const int size = MultiByteToWideChar(CP_UTF8, 0, source.data(), len, NULL, 0);
std::wstring str;
str.resize(size);
if (size > 0) {
@@ -58,6 +59,11 @@ void WASAPIContext::EnumerateDevices(std::vector<AudioDeviceDesc> *output, bool
IMMDeviceCollection *collection = nullptr;
enumerator_->EnumAudioEndpoints(captureDevices ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection);
if (!collection) {
ERROR_LOG(Log::Audio, "Failed to enumerate devices");
return;
}
UINT count = 0;
collection->GetCount(&count);
@@ -124,7 +130,7 @@ bool WASAPIContext::InitOutputDevice(std::string_view uniqueId, LatencyMode late
audioClient3_->GetMixFormat(&format_);
audioClient3_->GetSharedModeEnginePeriod(format_, &defaultPeriodFrames, &fundamentalPeriodFrames, &minPeriodFrames, &maxPeriodFrames);
printf("default: %d fundamental: %d min: %d max: %d\n", defaultPeriodFrames, fundamentalPeriodFrames, minPeriodFrames, maxPeriodFrames);
printf("default: %d fundamental: %d min: %d max: %d\n", (int)defaultPeriodFrames, (int)fundamentalPeriodFrames, (int)minPeriodFrames, (int)maxPeriodFrames);
printf("initializing with %d frame period at %d Hz, meaning %0.1fms\n", (int)minPeriodFrames, (int)format_->nSamplesPerSec, FramesToMs(minPeriodFrames, format_->nSamplesPerSec));
audioEvent_ = CreateEvent(nullptr, FALSE, FALSE, nullptr);
@@ -218,7 +224,7 @@ void WASAPIContext::FrameUpdate(bool allowAutoChange) {
void WASAPIContext::AudioLoop() {
DWORD taskID = 0;
HANDLE mmcssHandle = NULL;
HANDLE mmcssHandle = nullptr;
if (latencyMode_ == LatencyMode::Aggressive) {
mmcssHandle = AvSetMmThreadCharacteristics(L"Pro Audio", &taskID);
}
@@ -244,7 +250,7 @@ void WASAPIContext::AudioLoop() {
else
audioClient_->GetCurrentPadding(&padding), audioClient_->GetBufferSize(&available);
UINT32 framesToWrite = available - padding;
const UINT32 framesToWrite = available - padding;
BYTE* buffer = nullptr;
if (framesToWrite > 0 && SUCCEEDED(renderClient_->GetBuffer(framesToWrite, &buffer))) {
callback_((float *)buffer, framesToWrite, 2, format_->nSamplesPerSec, userdata_);
+10 -11
View File
@@ -19,7 +19,7 @@ static volatile BOOL done = 0;
#define SAMPLE_SIZE 44100
static short stream[SAMPLE_SIZE];
int NativeMix(short *audio, int numSamples, int sampleRateHz, void *userdata);
void NativeMix(short *audio, int numSamples, int sampleRateHz, void *userdata);
@interface AudioEngine ()
@@ -121,16 +121,15 @@ int NativeMix(short *audio, int numSamples, int sampleRateHz, void *userdata);
{
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, sampleRateHz, 0);
else
while (!done) {
if (![self playing]) {
NativeMix(stream, SAMPLE_SIZE / 2, sampleRateHz, 0);
frames_ready = SAMPLE_SIZE / 2;
} else {
frames_ready = 0;
}
if (frames_ready > 0)
{
if (frames_ready > 0) {
const size_t bytes_ready = frames_ready * sizeof(short) * 2;
alSourcei(source, AL_BUFFER, 0);
alBufferData(buffer, AL_FORMAT_STEREO16, stream, bytes_ready, sampleRateHz);
@@ -139,9 +138,9 @@ int NativeMix(short *audio, int numSamples, int sampleRateHz, void *userdata);
// TODO: Maybe this could get behind?
usleep((1000000 * frames_ready) / sampleRateHz);
}
else
} else {
usleep(100);
}
pthread_yield_np();
}
});
+5 -25
View File
@@ -74,41 +74,21 @@ void iOSCoreAudioSetDisplayConnected(bool connected) {
iOSCoreAudioUpdateSession();
}
int NativeMix(short *audio, int numSamples, int sampleRate, void *userdata);
void NativeMix(short *audio, int numSamples, int sampleRate, void *userdata);
OSStatus iOSCoreAudioCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
// see if we have any sound to play
AudioBufferList *ioData) {
short *output = (short *)ioData->mBuffers[0].mData;
UInt32 framesReady = NativeMix(output, inNumberFrames, SAMPLE_RATE, nullptr);
if (framesReady == 0) {
// oops, we don't currently have any sound, so return silence
*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
}
/*
* You'd think iOS would want to know how many frames were
* actually generated in case it was less than asked for, but
* apparently that causes micro-stuttering and everything just
* works better if we lie and say we successfully generated as
* many frames as it wanted... weird. We still get micro-stuttering
* but it's less noticeable this way.
*/
//UInt32 bytesReady = framesReady * sizeof(short) * 2;
UInt32 bytesReady = inNumberFrames * sizeof(short) * 2;
ioData->mBuffers[0].mDataByteSize = bytesReady;
NativeMix(output, inNumberFrames, SAMPLE_RATE, nullptr);
ioData->mBuffers[0].mDataByteSize = inNumberFrames * sizeof(short) * 2;
return noErr;
}
void iOSCoreAudioInit()
{
void iOSCoreAudioInit() {
iOSCoreAudioUpdateSession();
NSError *error = nil;