6#include "GuiSoundOggPlayer.h"
12#include <coreinit/cache.h>
17#define OGGPLAYER_THREAD_STACKSIZE (8 * 1024)
19GuiSoundOggPlayer::GuiSoundOggPlayer() : vfOpen(false), threadRunning(false), streamPaused(false), sampleRate(0), channels(0) {
20 pcmBuffer[0] = (uint8_t*)memalign(32, BUFFER_SIZE);
21 pcmBuffer[1] = (uint8_t*)memalign(32, BUFFER_SIZE);
22 bufferReady[0] =
false;
23 bufferReady[1] =
false;
28GuiSoundOggPlayer::~GuiSoundOggPlayer() {
34size_t GuiSoundOggPlayer::readOgg(
void* ptr,
size_t size,
size_t nmemb,
void* datasource) {
35 MemFile* mem =
static_cast<MemFile*
>(datasource);
36 size_t bytesToRead = size * nmemb;
37 if (mem->pos >= mem->size)
return 0;
38 if (
static_cast<size_t>(mem->pos) + bytesToRead >
static_cast<size_t>(mem->size)) {
39 bytesToRead =
static_cast<size_t>(mem->size - mem->pos);
41 memcpy(ptr, mem->data + mem->pos, bytesToRead);
42 mem->pos += bytesToRead;
43 return bytesToRead / size;
46int GuiSoundOggPlayer::seekOgg(
void* datasource, ogg_int64_t offset,
int whence) {
47 MemFile* mem =
static_cast<MemFile*
>(datasource);
49 case SEEK_SET: mem->pos = offset;
break;
50 case SEEK_CUR: mem->pos += offset;
break;
51 case SEEK_END: mem->pos = mem->size + offset;
break;
53 if (mem->pos < 0) mem->pos = 0;
54 if (mem->pos > mem->size) mem->pos = mem->size;
58int GuiSoundOggPlayer::closeOgg(
void*) {
return 0; }
59long GuiSoundOggPlayer::tellOgg(
void* datasource) {
return static_cast<MemFile*
>(datasource)->pos; }
65 memFile.size = length;
68 ov_callbacks cb = { readOgg, seekOgg, closeOgg, tellOgg };
69 if (ov_open_callbacks(&memFile, &vf,
nullptr, 0, cb) < 0) {
74 vorbis_info* vi = ov_info(&vf, -1);
75 sampleRate = vi->rate;
76 channels = vi->channels;
79 if (time_pos > 0) ov_time_seek(&vf, time_pos);
81 bufferReady[0] =
false;
82 bufferReady[1] =
false;
89 if (!decodeThread.start(threadEntry,
this, OGGPLAYER_THREAD_STACKSIZE, ThreadPriority::High)) {
91 threadRunning =
false;
105 threadRunning =
false;
107 if (decodeThread.isRunning())
117 streamPaused =
pause;
121 if (bufferReady[playIndex]) {
122 *outSize = pcmBufferSize[playIndex];
123 return pcmBuffer[playIndex];
129 bufferReady[playIndex] =
false;
133void* GuiSoundOggPlayer::threadEntry(
void* arg) {
138void GuiSoundOggPlayer::threadLoop() {
139 while (threadRunning && !decodeThread.stopRequested()) {
145 if (!bufferReady[decodeIndex]) {
148 while (bytesRead < BUFFER_SIZE && threadRunning && !streamPaused && !decodeThread.stopRequested()) {
149 int currentSection = 0;
150 long ret = ov_read(&vf, (
char*)pcmBuffer[decodeIndex] + bytesRead, BUFFER_SIZE - bytesRead, ¤tSection);
156 if (bytesRead == 0) threadRunning =
false;
159 }
else if (ret < 0) {
160 if (ret != OV_HOLE && !streamLoop) {
161 if (bytesRead == 0) threadRunning =
false;
170 DCFlushRange(pcmBuffer[decodeIndex], bytesRead);
171 pcmBufferSize[decodeIndex] = bytesRead;
172 bufferReady[decodeIndex] =
true;
174 }
else if (!threadRunning) {
Platform-agnostic Tremor (integer OGG) decoder.
void consumeBuffer()
Releases the buffer last returned by getReadyBuffer(), letting the decode thread reuse that ring slot...
void pause(bool pause)
Pauses or resumes the decode thread without stopping it.
const uint8_t * getReadyBuffer(int32_t *outSize)
bool play(const uint8_t *data, int32_t length, int time_pos, bool loop)
Starts decoding data on a new thread.
void stop()
Stops playback and joins the decode thread.