audio: Add *very* early wiiu audio support

This commit is contained in:
Ash 2018-09-29 20:07:22 +10:00 committed by Ash Logan
parent a910c0765a
commit bfb51a0f00
6 changed files with 372 additions and 3 deletions

View File

@ -19,7 +19,7 @@ SOURCES = \
src/*.c \
src/atomic/*.c \
src/audio/*.c \
src/audio/dummy/*.c \
src/audio/wiiu/*.c \
src/cpuinfo/*.c \
src/events/*.c \
src/file/*.c \

View File

@ -136,8 +136,8 @@
/* Wii U joystick driver (src/joystick/wiiu/\*.c) */
#define SDL_JOYSTICK_WIIU 1
/* Enable the stub audio driver (src/audio/dummy/\*.c) */
#define SDL_AUDIO_DRIVER_DUMMY 1
/* Enable the wiiu audio driver (src/audio/wiiu/\*.c) */
#define SDL_AUDIO_DRIVER_WIIU 1
/* Wii U video dirver */
#define SDL_VIDEO_DRIVER_WIIU 1

View File

@ -101,6 +101,9 @@ static const AudioBootStrap *const bootstrap[] = {
#if SDL_AUDIO_DRIVER_JACK
&JACK_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_WIIU
&WIIUAUDIO_bootstrap,
#endif
#if SDL_AUDIO_DRIVER_DISK
&DISKAUDIO_bootstrap,
#endif

View File

@ -207,6 +207,7 @@ extern AudioBootStrap FUSIONSOUND_bootstrap;
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
extern AudioBootStrap PSPAUDIO_bootstrap;
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
extern AudioBootStrap WIIUAUDIO_bootstrap;
#endif /* SDL_sysaudio_h_ */

View File

@ -0,0 +1,318 @@
/*
Simple DirectMedia Layer
Copyright (C) 2018-2018 Ash Logan <ash@heyquark.com>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_WIIU
#include <stdio.h>
#include "SDL_audio.h"
#include "SDL_error.h"
#include "SDL_timer.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "../SDL_sysaudio.h"
#include "SDL_wiiuaudio.h"
#include <sndcore2/core.h>
#include <sndcore2/voice.h>
#include <coreinit/core.h>
#include <coreinit/cache.h>
#include <coreinit/thread.h>
#include <coreinit/time.h>
#define WIIUAUDIO_DRIVER_NAME "wiiu"
#define AX_MAIN_AFFINITY OS_THREAD_ATTRIB_AFFINITY_CPU1
static void _WIIUAUDIO_framecallback();
static SDL_AudioDevice* cb_this;
#define cb_hidden cb_this->hidden
/* Some helpers for AX-related math */
/* Absolute address to an AXVoiceOffsets offset */
#define calc_ax_offset(offs, addr) (((void*)addr - offs.data) \
/ (offs.dataType == AX_VOICE_FORMAT_LPCM8 ? 1 : 2))
/* AX will play the offset *at* endOffset, so we need to subtract 1 sample for
end offsets */
#define calc_buf_endaddr(offs, buf, sz) (buf + sz \
- (offs.dataType == AX_VOICE_FORMAT_LPCM8 ? 1 : 2))
/* +1, but never goes above NUM_BUFFERS */
#define next_id(id) (id + 1) % NUM_BUFFERS
static int WIIUAUDIO_OpenDevice(_THIS, void* handle, const char* devname, int iscapture) {
this->hidden = (struct SDL_PrivateAudioData*)SDL_malloc(sizeof(*this->hidden));
if (this->hidden == NULL) return SDL_OutOfMemory();
SDL_zerop(this->hidden);
/* We *must not* change cores when setting stuff up */
uint32_t old_affinity = OSGetThreadAffinity(OSGetCurrentThread());
OSSetThreadAffinity(OSGetCurrentThread(), AX_MAIN_AFFINITY);
/* Take a quick aside to init the wiiu audio */
if (!AXIsInit()) {
/* Init the AX audio engine */
AXInitParams initparams = {
.renderer = AX_INIT_RENDERER_48KHZ,
.pipeline = AX_INIT_PIPELINE_SINGLE,
};
AXInitWithParams(&initparams);
} else printf("DEBUG: AX already up?\n");
/* Get a voice, top priority (we only need one) */
this->hidden->voice = AXAcquireVoice(31, NULL, NULL);
if (!this->hidden->voice) {
AXQuit();
printf("DEBUG: couldn't get voice\n");
return SDL_OutOfMemory();
}
/* Start messing with it */
AXVoiceBegin(this->hidden->voice);
AXSetVoiceType(this->hidden->voice, 0);
/* Set the voice's volume */
AXVoiceVeData vol = {
.volume = 0x8000,
};
AXSetVoiceVe(this->hidden->voice, &vol);
AXVoiceDeviceMixData drcmix = {
.bus = {
{ .volume = 0x8000 }, //bus 0
{ .volume = 0x0000 }, //bus 1
{ .volume = 0x0000 }, //bus 2
{ .volume = 0x0000 }, //bus 3
}
};
AXSetVoiceDeviceMix(this->hidden->voice, AX_DEVICE_TYPE_DRC, 0, &drcmix);
/* Set the samplerate conversion ratio
<source sample rate> / <target sample rate> */
float srcratio = (float)this->spec.freq / (float)AXGetInputSamplesPerSec();
AXSetVoiceSrcRatio(this->hidden->voice, srcratio);
AXSetVoiceSrcType(this->hidden->voice, AX_VOICE_SRC_TYPE_LINEAR);
/* Okay, we're good */
AXVoiceEnd(this->hidden->voice);
/* Force wiiu-compatible audio formats.
TODO verify - unsigned or signed? */
switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
case 8:
/* TODO 8-bit audio sounds broken */
/*this->spec.format = AUDIO_S8;
break;*/
case 16:
default:
this->spec.format = AUDIO_S16MSB;
break;
}
//TODO
this->spec.channels = 1;
//TODO maybe round this->spec.samples up even when >?
//maybe even force at least 2* so we get more frame callbacks to think
if (this->spec.samples < AXGetInputSamplesPerFrame()) {
this->spec.samples = AXGetInputSamplesPerFrame();
}
/* We changed channels and samples, so recalculate the spec */
SDL_CalculateAudioSpec(&this->spec);
/* Allocate buffers for double-buffering and samples */
for (int i = 0; i < NUM_BUFFERS; i++) {
this->hidden->mixbufs[i] = SDL_malloc(this->spec.size);
if (this->hidden->mixbufs[i] == NULL) {
AXQuit();
printf("DEBUG: Couldn't allocate buffer");
return SDL_SetError("Couldn't allocate buffer");
}
memset(this->hidden->mixbufs[i], 0, this->spec.size);
DCFlushRange(this->hidden->mixbufs[i], this->spec.size);
}
/* Start messing with the voice again */
AXVoiceBegin(this->hidden->voice);
/* Set up the offsets for the first mixbuf */
AXVoiceOffsets offs;
switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
case 8:
offs.dataType = AX_VOICE_FORMAT_LPCM8;
offs.endOffset = this->spec.size - 1;
break;
case 16:
default:
offs.dataType = AX_VOICE_FORMAT_LPCM16;
offs.endOffset = (this->spec.size / 2) - 1;
break;
}
offs.loopingEnabled = AX_VOICE_LOOP_ENABLED;
offs.loopOffset = 0;
offs.currentOffset = 0;
offs.data = this->hidden->mixbufs[0];
AXSetVoiceOffsets(this->hidden->voice, &offs);
/* Set the last good loopcount */
this->hidden->last_loopcount = AXGetVoiceLoopCount(this->hidden->voice);
/* Offsets are set for playing the first mixbuf, so we should render the second */
this->hidden->playingid = 0;
this->hidden->renderingid = 1;
/* Start playing. */
AXSetVoiceState(this->hidden->voice, AX_VOICE_STATE_PLAYING);
/* ..alright! */
AXVoiceEnd(this->hidden->voice);
cb_this = this; //wish there was a better way
AXRegisterAppFrameCallback(_WIIUAUDIO_framecallback);
/* Put the thread affinity back to normal - we won't call any more AX funcs */
OSSetThreadAffinity(OSGetCurrentThread(), old_affinity);
return 0;
}
/* Called every 3ms before a frame of audio is rendered. Keep it fast! */
static void _WIIUAUDIO_framecallback() {
if (!cb_hidden->voice) {
printf("DEBUG: aaah!");
return;
}
AXVoiceOffsets offs;
AXGetVoiceOffsets(cb_hidden->voice, &offs);
/* Figure out which buffer is being played by the hardware */
int playing_buffer = -1;
for (int i = 0; i < NUM_BUFFERS; i++) {
void* buf = cb_hidden->mixbufs[i];
void* bufEnd = calc_buf_endaddr(offs, buf, cb_this->spec.size);
uint32_t startOffset = calc_ax_offset(offs, buf);
uint32_t endOffset = calc_ax_offset(offs, bufEnd);
/* NOTE endOffset definitely needs to be <= (AX plays the sample at
endOffset), dunno about startOffset */
if (offs.currentOffset >= startOffset &&
offs.currentOffset <= endOffset) {
playing_buffer = i;
break;
}
}
if (playing_buffer < 0 || playing_buffer >= NUM_BUFFERS) {
/* UM */
/* Uncomment for craploads of debug info */
/*printf("bad buffer %d\n" "|> %08X, %08X-%08X\n" \
"0: xxxxxxxx, %08X-%08X (%08X@%08X)\n" \
"1: xxxxxxxx, %08X-%08X (%08X@%08X)\n", \
playing_buffer, offs.currentOffset, offs.loopOffset, offs.endOffset,
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[0] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[0],
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1]),
calc_ax_offset(offs, (void*)cb_hidden->mixbufs[1] + cb_this->spec.size),
cb_this->spec.size, (void*)cb_hidden->mixbufs[1]);*/
printf("DEBUG: Playing an invalid buffer? This is not a good sign.\n");
playing_buffer = 0;
}
/* Make sure playingid is in sync with the hardware */
cb_hidden->playingid = playing_buffer;
/* Make sure the end offset is correct for the playing buffer */
void* endaddr = cb_hidden->mixbufs[cb_hidden->playingid] + cb_this->spec.size - 2;
AXSetVoiceEndOffset(cb_hidden->voice, calc_ax_offset(offs, endaddr));
/* The next buffer is good to go, set the loop offset */
if (cb_hidden->renderingid != next_id(cb_hidden->playingid)) {
void* loopaddr = cb_hidden->mixbufs[next_id(cb_hidden->playingid)];
AXSetVoiceLoopOffset(cb_hidden->voice, calc_ax_offset(offs, loopaddr));
/* Otherwise, make sure the loop offset is correct for the playing buffer */
} else {
void* loopaddr = cb_hidden->mixbufs[cb_hidden->playingid];
AXSetVoiceLoopOffset(cb_hidden->voice, calc_ax_offset(offs, loopaddr));
}
}
static void WIIUAUDIO_PlayDevice(_THIS) {
/* Comment this out for broken-record mode ;3 */
DCFlushRange(this->hidden->mixbufs[this->hidden->renderingid], this->spec.size);
/* Signal we're no longer rendering this buffer, AX callback will notice later */
this->hidden->renderingid = next_id(this->hidden->renderingid);
}
static void WIIUAUDIO_WaitDevice(_THIS) {
/* TODO use real thread sync stuff */
while (this->hidden->renderingid == this->hidden->playingid) {
OSSleepTicks(OSMillisecondsToTicks(3));
}
}
static Uint8* WIIUAUDIO_GetDeviceBuf(_THIS) {
/* SDL will write audio samples into this buffer */
return this->hidden->mixbufs[this->hidden->renderingid];
}
static void WIIUAUDIO_CloseDevice(_THIS) {
if (AXIsInit()) {
AXFreeVoice(this->hidden->voice);
this->hidden->voice = NULL;
AXQuit();
}
for (int i = 0; i < NUM_BUFFERS; i++) {
if (this->hidden->mixbufs[i]) SDL_free(this->hidden->mixbufs[i]);
}
SDL_free(this->hidden);
}
static void WIIUAUDIO_ThreadInit(_THIS) {
/* Bump our thread's priority a bit */
OSThread* currentThread = OSGetCurrentThread();
int32_t priority = OSGetThreadPriority(currentThread);
priority -= 1;
OSSetThreadPriority(currentThread, priority);
}
static int WIIUAUDIO_Init(SDL_AudioDriverImpl* impl) {
impl->OpenDevice = WIIUAUDIO_OpenDevice;
impl->PlayDevice = WIIUAUDIO_PlayDevice;
impl->WaitDevice = WIIUAUDIO_WaitDevice;
impl->GetDeviceBuf = WIIUAUDIO_GetDeviceBuf;
impl->CloseDevice = WIIUAUDIO_CloseDevice;
impl->ThreadInit = WIIUAUDIO_ThreadInit;
impl->OnlyHasDefaultOutputDevice = 1;
return 1;
}
AudioBootStrap WIIUAUDIO_bootstrap = {
WIIUAUDIO_DRIVER_NAME, "Wii U AX Audio Driver", WIIUAUDIO_Init, 0,
};
#endif //SDL_AUDIO_DRIVER_WIIU

View File

@ -0,0 +1,47 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_wiiuaudio_h_
#define SDL_wiiuaudio_h_
#include "../SDL_sysaudio.h"
#include <sndcore2/voice.h>
/* Hidden "this" pointer for the audio functions */
#define _THIS SDL_AudioDevice *this
#define NUM_BUFFERS 2
struct SDL_PrivateAudioData {
/* xasxa */
AXVoice* voice;
/* The raw allocated mixing buffer. */
Uint8 *rawbuf;
/* Individual mixing buffers. */
Uint8 *mixbufs[NUM_BUFFERS];
int renderingid;
int playingid;
uint32_t last_loopcount;
};
#endif /* SDL_wiiuaudio_h_ */