40 AXInitParams params = {};
41 params.renderer = AX_INIT_RENDERER_48KHZ;
42 params.pipeline = AX_INIT_PIPELINE_SINGLE;
43 AXInitWithParams(¶ms);
44 AXRegisterAppFrameCallback(wut_frame_callback);
50 AXVoiceDeviceMixData tvMixCentered[AX_TV_CHANNELS];
51 AXVoiceDeviceMixData drcMixCentered[AX_DRC_CHANNELS];
52 buildChannelMix(tvMixCentered,
true,
true);
53 buildChannelMix(drcMixCentered,
true,
true);
55 for (
int i = 0; i < 16; i++) {
56 voices[i].voice = AXAcquireVoice(31, 0, 0);
57 voices[i].active =
false;
58 if (voices[i].voice) {
59 AXVoiceBegin(voices[i].voice);
60 AXSetVoiceType(voices[i].voice, 0);
62 AXSetVoiceDeviceMix(voices[i].voice, AX_DEVICE_TYPE_TV, 0, tvMixCentered);
63 AXSetVoiceDeviceMix(voices[i].voice, AX_DEVICE_TYPE_DRC, 0, drcMixCentered);
64 AXVoiceEnd(voices[i].voice);
68 streamVoiceL = AXAcquireVoice(31, 0, 0);
69 streamVoiceR = AXAcquireVoice(31, 0, 0);
71 if (streamVoiceL && streamVoiceR) {
72 AXVoiceDeviceMixData tvMixL[AX_TV_CHANNELS], drcMixL[AX_DRC_CHANNELS];
73 AXVoiceDeviceMixData tvMixR[AX_TV_CHANNELS], drcMixR[AX_DRC_CHANNELS];
74 buildChannelMix(tvMixL,
true,
false);
75 buildChannelMix(drcMixL,
true,
false);
76 buildChannelMix(tvMixR,
false,
true);
77 buildChannelMix(drcMixR,
false,
true);
79 AXVoiceBegin(streamVoiceL);
80 AXSetVoiceType(streamVoiceL, 0);
81 AXSetVoiceDeviceMix(streamVoiceL, AX_DEVICE_TYPE_TV, 0, tvMixL);
82 AXSetVoiceDeviceMix(streamVoiceL, AX_DEVICE_TYPE_DRC, 0, drcMixL);
83 AXVoiceEnd(streamVoiceL);
85 AXVoiceBegin(streamVoiceR);
86 AXSetVoiceType(streamVoiceR, 0);
87 AXSetVoiceDeviceMix(streamVoiceR, AX_DEVICE_TYPE_TV, 0, tvMixR);
88 AXSetVoiceDeviceMix(streamVoiceR, AX_DEVICE_TYPE_DRC, 0, drcMixR);
89 AXVoiceEnd(streamVoiceR);
94 eofSilenceWritten =
false;
127 int voiceIdx = nextVoiceSlot;
128 nextVoiceSlot = (nextVoiceSlot + 1) % 16;
130 WutVoiceSlot &slot = voices[voiceIdx];
134 AXSetVoiceState(slot.voice, 0);
137 AXVoiceOffsets offsets;
138 memset(&offsets, 0,
sizeof(offsets));
140 offsets.dataType = AX_VOICE_FORMAT_LPCM16;
141 offsets.loopingEnabled = AX_VOICE_LOOP_DISABLED;
142 offsets.endOffset = length / 2;
143 AXSetVoiceOffsets(slot.voice, &offsets);
146 memset(&src, 0,
sizeof(src));
147 uint32_t samplesPerSec = AXGetInputSamplesPerSec();
148 src.ratio = (uint32_t)(0x00010000 * ((
float) 48000 / (
float) samplesPerSec));
149 AXSetVoiceSrc(slot.voice, &src);
150 AXSetVoiceSrcType(slot.voice, (src.ratio == 0x00010000) ? 0 : 1);
152 AXVoiceVeData veData;
153 veData.volume = (volume << 7);
155 AXSetVoiceVe(slot.voice, &veData);
157 AXSetVoiceState(slot.voice, 1);
198 streamVolume = volume;
200 memset(streamBufL, 0,
sizeof(streamBufL));
201 memset(streamBufR, 0,
sizeof(streamBufR));
202 DCFlushRange(streamBufL,
sizeof(streamBufL));
203 DCFlushRange(streamBufR,
sizeof(streamBufR));
206 eofSilenceWritten =
false;
208 if (oggPlayer.play(data, length, 0, loop)) {
209 int32_t readySize = 0;
210 const uint8_t *pcm =
nullptr;
212 while (oggPlayer.isPlaying() && !(pcm = oggPlayer.getReadyBuffer(&readySize))) {
216 if (pcm && readySize > 0) {
217 int samples = readySize / (oggPlayer.getChannels() == 2 ? 4 : 2);
218 int16_t *pcm16 = (int16_t*) pcm;
220 if (oggPlayer.getChannels() == 2) {
221 for (
int i = 0; i < samples; i++) {
222 streamBufL[i] = pcm16[i * 2];
223 streamBufR[i] = pcm16[i * 2 + 1];
226 for (
int i = 0; i < samples; i++) {
227 streamBufL[i] = pcm16[i];
228 streamBufR[i] = pcm16[i];
232 DCFlushRange(streamBufL, samples * 2);
233 DCFlushRange(streamBufR, samples * 2);
234 writeOffset = samples;
235 oggPlayer.consumeBuffer();
237 AXSetVoiceState(streamVoiceL, 0);
238 AXSetVoiceState(streamVoiceR, 0);
240 AXVoiceOffsets offsetsL, offsetsR;
241 memset(&offsetsL, 0,
sizeof(offsetsL));
242 offsetsL.data = streamBufL;
243 offsetsL.dataType = AX_VOICE_FORMAT_LPCM16;
244 offsetsL.loopingEnabled = AX_VOICE_LOOP_ENABLED;
245 offsetsL.loopOffset = 0;
246 offsetsL.endOffset = STREAM_BUFFER_SAMPLES - 1;
247 AXSetVoiceOffsets(streamVoiceL, &offsetsL);
250 offsetsR.data = streamBufR;
251 AXSetVoiceOffsets(streamVoiceR, &offsetsR);
254 memset(&src, 0,
sizeof(src));
255 uint32_t samplesPerSec = AXGetInputSamplesPerSec();
256 src.ratio = (uint32_t)(0x00010000 * ((
float) oggPlayer.getSampleRate() / (
float) samplesPerSec));
257 AXSetVoiceSrc(streamVoiceL, &src);
258 AXSetVoiceSrc(streamVoiceR, &src);
260 uint16_t srcType = (src.ratio == 0x00010000) ? 0 : 1;
261 AXSetVoiceSrcType(streamVoiceL, srcType);
262 AXSetVoiceSrcType(streamVoiceR, srcType);
264 AXVoiceVeData veData;
265 veData.volume = (streamVolume << 7);
267 AXSetVoiceVe(streamVoiceL, &veData);
268 AXSetVoiceVe(streamVoiceR, &veData);
270 AXSetVoiceState(streamVoiceL, 1);
271 AXSetVoiceState(streamVoiceR, 1);
277 if (!isForeground()) {
284 if (!streamVoiceL || !streamVoiceR || oggPlayer.isPaused() || streamVoiceL->state != AX_VOICE_STATE_PLAYING)
288 uint32_t currentOffset = AXGetVoiceCurrentOffsetEx(streamVoiceL, streamBufL);
291 uint32_t queuedSamples = (writeOffset - currentOffset + STREAM_BUFFER_SAMPLES) % STREAM_BUFFER_SAMPLES;
293 if (queuedSamples < (STREAM_BUFFER_SAMPLES / 2)) {
294 int32_t readySize = 0;
295 const uint8_t *pcm = oggPlayer.getReadyBuffer(&readySize);
297 if (pcm && readySize > 0) {
298 int samples = readySize / (oggPlayer.getChannels() == 2 ? 4 : 2);
301 int samplesToEnd = STREAM_BUFFER_SAMPLES - writeOffset;
302 int write1 = (samples < samplesToEnd) ? samples : samplesToEnd;
303 int write2 = samples - write1;
304 int16_t *pcm16 = (int16_t*) pcm;
306 auto copySamples = [&](
int dest,
int src,
int count) {
307 if (oggPlayer.getChannels() == 2) {
308 for (
int i = 0; i < count; i++) {
309 streamBufL[dest + i] = pcm16[(src + i) * 2];
310 streamBufR[dest + i] = pcm16[(src + i) * 2 + 1];
313 for (
int i = 0; i < count; i++) {
314 streamBufL[dest + i] = pcm16[src + i];
315 streamBufR[dest + i] = pcm16[src + i];
320 if (write1 > 0) copySamples(writeOffset, 0, write1);
321 if (write2 > 0) copySamples(0, write1, write2);
324 DCFlushRange(&streamBufL[writeOffset], write1 * 2);
325 DCFlushRange(&streamBufR[writeOffset], write1 * 2);
328 DCFlushRange(&streamBufL[0], write2 * 2);
329 DCFlushRange(&streamBufR[0], write2 * 2);
332 writeOffset = (writeOffset + samples) % STREAM_BUFFER_SAMPLES;
333 oggPlayer.consumeBuffer();
335 }
else if (!oggPlayer.isPlaying()) {
339 if (!eofSilenceWritten) {
340 uint32_t clearCount = STREAM_BUFFER_SAMPLES - queuedSamples;
341 if (clearCount > 0) {
342 uint32_t clear1 = (clearCount < (STREAM_BUFFER_SAMPLES - writeOffset)) ? clearCount : (STREAM_BUFFER_SAMPLES - writeOffset);
343 uint32_t clear2 = clearCount - clear1;
345 memset(&streamBufL[writeOffset], 0, clear1 * 2);
346 memset(&streamBufR[writeOffset], 0, clear1 * 2);
347 DCFlushRange(&streamBufL[writeOffset], clear1 * 2);
348 DCFlushRange(&streamBufR[writeOffset], clear1 * 2);
351 memset(&streamBufL[0], 0, clear2 * 2);
352 memset(&streamBufR[0], 0, clear2 * 2);
353 DCFlushRange(&streamBufL[0], clear2 * 2);
354 DCFlushRange(&streamBufR[0], clear2 * 2);
357 eofSilenceWritten =
true;
361 if (queuedSamples < 128) {
362 AXSetVoiceState(streamVoiceL, 0);
363 AXSetVoiceState(streamVoiceR, 0);