RetroArch/audio/drivers/wiiu_audio.c

338 lines
8.9 KiB
C
Raw Normal View History

2016-11-06 18:03:41 +00:00
/* RetroArch - A frontend for libretro.
2017-01-22 12:40:32 +00:00
* Copyright (C) 2014-2017 - Ali Bouhlel
* Copyright (C) 2016-2017 - FIX94
2016-11-06 18:03:41 +00:00
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <malloc.h>
#include <stdint.h>
2017-01-23 14:59:44 +00:00
#include <wiiu/os.h>
#include <wiiu/ax.h>
2016-11-06 18:03:41 +00:00
#include "wiiu/wiiu_dbg.h"
2016-11-13 15:42:43 +00:00
#include "wiiu/system/memory.h"
2016-11-06 18:03:41 +00:00
#include "audio/audio_driver.h"
typedef struct
{
AXMVoice* mvoice;
2016-11-06 18:03:41 +00:00
uint16_t* buffer_l;
uint16_t* buffer_r;
bool nonblocking;
uint32_t pos;
uint32_t written;
OSSpinLock spinlock;
2016-11-06 18:03:41 +00:00
} ax_audio_t;
2017-01-14 08:35:42 +00:00
/* 3072 samples main buffer, 64ms total */
2016-12-01 18:46:33 +00:00
#define AX_AUDIO_COUNT 3072
2016-11-13 15:42:43 +00:00
#define AX_AUDIO_SIZE (AX_AUDIO_COUNT << 1u)
2016-11-06 18:03:41 +00:00
2017-01-23 00:33:41 +00:00
#define AX_AUDIO_SAMPLE_COUNT 144 /* 3ms */
#define AX_AUDIO_SAMPLE_MIN (AX_AUDIO_SAMPLE_COUNT * 3) /* 9ms */
#define AX_AUDIO_SAMPLE_LOAD (AX_AUDIO_SAMPLE_COUNT * 10)/* 30ms */
#define AX_AUDIO_MAX_FREE (AX_AUDIO_COUNT - (AX_AUDIO_SAMPLE_COUNT * 2))
2016-11-13 15:42:43 +00:00
#define AX_AUDIO_RATE 48000
2017-01-23 00:33:41 +00:00
#if 0
#define ax_audio_ticks_to_samples(ticks) (((ticks) * 64) / 82875)
#define ax_audio_samples_to_ticks(samples) (((samples) * 82875) / 64)
#endif
2016-11-06 18:03:41 +00:00
static volatile ax_audio_t *wiiu_cb_ax = NULL;
void wiiu_ax_callback(void)
2016-11-06 18:03:41 +00:00
{
2017-01-23 00:33:41 +00:00
/*possibly called before unregister */
if(wiiu_cb_ax == NULL)
return;
ax_audio_t *ax = (ax_audio_t*)wiiu_cb_ax;
if(AXIsMultiVoiceRunning(ax->mvoice))
{
2016-11-22 02:18:36 +00:00
if(OSUninterruptibleSpinLock_Acquire(&ax->spinlock))
{
2017-01-23 00:33:41 +00:00
/* Buffer underrun, stop playback to let it fill up */
if(ax->written < AX_AUDIO_SAMPLE_MIN)
AXSetMultiVoiceState(ax->mvoice, AX_VOICE_STATE_STOPPED);
ax->written -= AX_AUDIO_SAMPLE_COUNT;
OSUninterruptibleSpinLock_Release(&ax->spinlock);
}
}
2016-11-06 18:03:41 +00:00
}
extern void AXRegisterFrameCallback(void *cb);
static void* ax_audio_init(const char* device, unsigned rate, unsigned latency,
unsigned block_frames,
unsigned *new_rate)
2016-11-06 18:03:41 +00:00
{
2017-01-14 08:35:42 +00:00
AXVoiceOffsets offsets[2];
u16 setup_buf[0x30] = {0};
setup_buf[0x25] = 2; /* we request 2 channels */
AXInitParams init = {AX_INIT_RENDERER_48KHZ, 0, 0};
AXVoiceVeData ve = {0xF000, 0};
ax_audio_t* ax = (ax_audio_t*)calloc(1, sizeof(ax_audio_t));
2016-11-06 18:03:41 +00:00
if (!ax)
return NULL;
AXInitWithParams(&init);
AXAcquireMultiVoice(31, NULL, 0, setup_buf, &ax->mvoice);
2016-11-13 15:42:43 +00:00
if (!ax->mvoice || ax->mvoice->channels != 2)
2016-11-06 18:03:41 +00:00
{
free(ax);
return NULL;
}
2016-11-13 15:42:43 +00:00
2017-01-14 08:35:42 +00:00
ax->buffer_l = MEM1_alloc(AX_AUDIO_SIZE, 0x100);
ax->buffer_r = MEM1_alloc(AX_AUDIO_SIZE, 0x100);
memset(ax->buffer_l,0,AX_AUDIO_SIZE);
memset(ax->buffer_r,0,AX_AUDIO_SIZE);
DCFlushRange(ax->buffer_l,AX_AUDIO_SIZE);
DCFlushRange(ax->buffer_r,AX_AUDIO_SIZE);
2016-11-13 15:42:43 +00:00
2017-01-14 08:35:42 +00:00
/* shared by both voices */
offsets[0].currentOffset = 0;
offsets[0].loopOffset = 0;
offsets[0].endOffset = AX_AUDIO_COUNT - 1;
2016-11-21 23:37:22 +00:00
offsets[0].loopingEnabled = AX_VOICE_LOOP_ENABLED;
2017-01-14 08:35:42 +00:00
offsets[0].dataType = AX_VOICE_FORMAT_LPCM16;
2016-11-21 23:37:22 +00:00
memcpy(&offsets[1], &offsets[0], sizeof(AXVoiceOffsets));
2017-01-14 08:35:42 +00:00
/* different buffers per voice */
offsets[0].data = ax->buffer_l;
offsets[1].data = ax->buffer_r;
2016-11-21 23:37:22 +00:00
AXSetMultiVoiceOffsets(ax->mvoice, offsets);
AXSetMultiVoiceSrcType(ax->mvoice, AX_VOICE_SRC_TYPE_NONE);
AXSetMultiVoiceSrcRatio(ax->mvoice, 1.0f);
AXSetMultiVoiceVe(ax->mvoice, &ve);
2016-11-21 23:37:22 +00:00
AXSetMultiVoiceDeviceMix(ax->mvoice, AX_DEVICE_TYPE_DRC, 0, 0, 0x8000, 0);
AXSetMultiVoiceDeviceMix(ax->mvoice, AX_DEVICE_TYPE_TV, 0, 0, 0x8000, 0);
2016-11-06 21:12:26 +00:00
AXSetMultiVoiceState(ax->mvoice, AX_VOICE_STATE_STOPPED);
2016-11-06 18:03:41 +00:00
2017-01-14 08:35:42 +00:00
ax->pos = 0;
ax->written = 0;
*new_rate = AX_AUDIO_RATE;
2016-11-06 18:03:41 +00:00
OSInitSpinLock(&ax->spinlock);
2017-01-14 08:35:42 +00:00
wiiu_cb_ax = ax;
AXRegisterFrameCallback(wiiu_ax_callback);
2016-11-06 18:03:41 +00:00
return ax;
}
static void ax_audio_free(void* data)
{
ax_audio_t* ax = (ax_audio_t*)data;
2017-01-14 08:35:42 +00:00
wiiu_cb_ax = NULL;
2016-11-06 18:03:41 +00:00
AXRegisterFrameCallback(NULL);
AXFreeMultiVoice(ax->mvoice);
AXQuit();
2016-11-13 15:42:43 +00:00
MEM1_free(ax->buffer_l);
MEM1_free(ax->buffer_r);
2016-11-06 18:03:41 +00:00
free(ax);
}
static bool ax_audio_stop(void* data)
2016-11-13 15:42:43 +00:00
{
ax_audio_t* ax = (ax_audio_t*)data;
2016-11-13 15:42:43 +00:00
AXSetMultiVoiceState(ax->mvoice, AX_VOICE_STATE_STOPPED);
return true;
}
2016-11-13 15:42:43 +00:00
2016-12-01 18:46:33 +00:00
static int ax_audio_limit(int in)
{
if(in < 0)
in += AX_AUDIO_COUNT;
else if(in >= AX_AUDIO_COUNT)
in -= AX_AUDIO_COUNT;
return in;
}
static bool ax_audio_start(void* data, bool is_shutdown)
{
ax_audio_t* ax = (ax_audio_t*)data;
2016-11-13 15:42:43 +00:00
/* Prevents restarting audio when the menu
* is toggled off on shutdown */
if (is_shutdown)
return true;
2016-11-13 15:42:43 +00:00
2017-01-23 00:33:41 +00:00
/* Set back to playing on enough buffered data */
2016-11-22 06:34:11 +00:00
if(ax->written > AX_AUDIO_SAMPLE_LOAD)
{
2016-12-01 18:46:33 +00:00
AXSetMultiVoiceCurrentOffset(ax->mvoice, ax_audio_limit(ax->pos - ax->written));
AXSetMultiVoiceState(ax->mvoice, AX_VOICE_STATE_PLAYING);
}
return true;
2016-11-13 15:42:43 +00:00
}
static ssize_t ax_audio_write(void* data, const void* buf, size_t size)
2016-11-06 18:03:41 +00:00
{
uint32_t i;
2017-01-14 08:35:42 +00:00
size_t countAvail = 0;
ax_audio_t* ax = (ax_audio_t*)data;
2016-11-06 18:03:41 +00:00
const uint16_t* src = buf;
size_t count = size >> 2;
2016-11-06 18:03:41 +00:00
if(!size || (size & 0x3))
return 0;
2016-11-06 18:03:41 +00:00
if(count > AX_AUDIO_MAX_FREE)
count = AX_AUDIO_MAX_FREE;
2016-11-06 18:03:41 +00:00
2017-01-14 08:35:42 +00:00
countAvail = ((ax->written > AX_AUDIO_MAX_FREE) ? 0 : (AX_AUDIO_MAX_FREE - ax->written));
if (ax->nonblocking)
{
2017-01-23 00:33:41 +00:00
/* Not enough available for 3ms of data */
if(countAvail < AX_AUDIO_SAMPLE_COUNT)
count = 0;
}
else if(countAvail < count)
{
2017-01-23 00:33:41 +00:00
/* Sync, wait for free memory */
while(AXIsMultiVoiceRunning(ax->mvoice) && (countAvail < count))
{
2017-01-23 00:33:41 +00:00
OSYieldThread(); /* Gives threads with same priority time to run */
countAvail = (ax->written > AX_AUDIO_MAX_FREE ? 0 : (AX_AUDIO_MAX_FREE - ax->written));
}
}
2017-01-23 00:33:41 +00:00
/* Over available space, do as much as possible */
if(count > countAvail)
count = countAvail;
2017-01-23 00:33:41 +00:00
/* make sure we have input size */
if(count > 0)
2016-11-22 02:18:36 +00:00
{
2017-01-23 00:33:41 +00:00
/* write in new data */
size_t startPos = ax->pos;
int flushP2needed = 0;
2017-01-23 00:33:41 +00:00
int flushP2 = 0;
for (i = 0; i < (count << 1); i += 2)
{
ax->buffer_l[ax->pos] = src[i];
ax->buffer_r[ax->pos] = src[i + 1];
2017-01-23 00:33:41 +00:00
ax->pos = ax_audio_limit(ax->pos + 1);
/* wrapped around, make sure to store cache */
if(ax->pos == 0)
{
flushP2needed = 1;
2017-01-23 00:33:41 +00:00
flushP2 = ((count << 1) - i);
DCStoreRangeNoSync(ax->buffer_l+startPos, (AX_AUDIO_COUNT-startPos) << 1);
DCStoreRangeNoSync(ax->buffer_r+startPos, (AX_AUDIO_COUNT-startPos) << 1);
}
}
2017-01-23 00:33:41 +00:00
/* standard cache store case */
if(!flushP2needed)
{
DCStoreRangeNoSync(ax->buffer_l+startPos, count << 1);
DCStoreRange(ax->buffer_r+startPos, count << 1);
2017-01-23 00:33:41 +00:00
}
/* store the rest after wrap */
else if(flushP2 > 0)
{
DCStoreRangeNoSync(ax->buffer_l, flushP2);
DCStoreRange(ax->buffer_r, flushP2);
}
2017-01-23 00:33:41 +00:00
/* add in new audio data */
if(OSUninterruptibleSpinLock_Acquire(&ax->spinlock))
{
ax->written += count;
OSUninterruptibleSpinLock_Release(&ax->spinlock);
}
2016-11-22 02:18:36 +00:00
}
2017-01-23 00:33:41 +00:00
/* Possibly buffer underrun
*
* checks if it can be started
*/
if(!AXIsMultiVoiceRunning(ax->mvoice))
2017-01-23 00:33:41 +00:00
ax_audio_start(ax, false);
/* return what was actually copied */
return (count << 2);
2016-11-06 18:03:41 +00:00
}
static bool ax_audio_alive(void* data)
{
ax_audio_t* ax = (ax_audio_t*)data;
return AXIsMultiVoiceRunning(ax->mvoice);
2016-11-06 18:03:41 +00:00
}
static void ax_audio_set_nonblock_state(void* data, bool state)
{
ax_audio_t* ax = (ax_audio_t*)data;
2016-11-13 15:42:43 +00:00
if (ax)
ax->nonblocking = state;
2016-11-06 18:03:41 +00:00
}
static bool ax_audio_use_float(void* data)
{
(void)data;
return false;
}
static size_t ax_audio_write_avail(void* data)
{
ax_audio_t* ax = (ax_audio_t*)data;
size_t ret = AX_AUDIO_COUNT - ax->written;
2016-11-13 15:42:43 +00:00
return (ret < AX_AUDIO_SAMPLE_COUNT ? 0 : ret);
2016-11-06 18:03:41 +00:00
}
static size_t ax_audio_buffer_size(void* data)
{
(void)data;
return AX_AUDIO_COUNT;
}
audio_driver_t audio_ax =
{
ax_audio_init,
ax_audio_write,
ax_audio_stop,
ax_audio_start,
ax_audio_alive,
ax_audio_set_nonblock_state,
ax_audio_free,
ax_audio_use_float,
"AX",
NULL, /* device_list_new */
NULL, /* device_list_free */
NULL, /* write_avail */
NULL, /* buffer_size */
2016-11-06 18:03:41 +00:00
};