Nintendo 3DS >> C/C++

Audio (Opus)


參考資訊:
1. libctru doc
2. 3ds-examples

說明:

API
int op_read_stereo(OggOpusFile *_of, opus_int16 *_pcm, int _buf_size)
void LightEvent_Signal(LightEvent *event)
void LightEvent_Init(LightEvent *event, ResetType reset_type)
OggOpusFile *op_open_file(const char *_path, int *_error)
void ndspChnReset(int id)
void ndspSetCallback(ndspCallback callback, void *data)
void LightEvent_Wait(LightEvent *event)
void LightEvent_Signal(LightEvent *event)
void ndspChnReset(int id)
void op_free(OggOpusFile *_of)

main.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <3ds.h>
#include <opusfile.h>

#define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))
#define SAMPLE_RATE     48000
#define SAMPLE_BUF      (SAMPLE_RATE * 120 / 1000)
#define CHANNEL         2
#define WAVEBUF_SIZE    (SAMPLE_BUF * CHANNEL * sizeof(int16_t))

LightEvent event = {0};
int16_t *audio_buffer = NULL;
ndspWaveBuf wave_buffer[3]= {0};

bool fillBuffer(OggOpusFile *opusFile, ndspWaveBuf *waveBuf)
{
    int totalSamples = 0;
    while (totalSamples < SAMPLE_BUF) {
        int16_t *buffer = waveBuf->data_pcm16 + (totalSamples * CHANNEL);
        const size_t bufferSize = (SAMPLE_BUF - totalSamples) * CHANNEL;
        const int samples = op_read_stereo(opusFile, buffer, bufferSize);
        if (samples <= 0) {
            break;
        }
        totalSamples += samples;
    }

    if (totalSamples == 0) {
        return false;
    }

    waveBuf->nsamples = totalSamples;
    ndspChnWaveBufAdd(0, waveBuf);
    DSP_FlushDataCache(waveBuf->data_pcm16, totalSamples * CHANNEL * sizeof(int16_t));
    return true;
}

void audioCallback(void *const argv)
{
    LightEvent_Signal(&event);
}

int main(void)
{
    int rc = 0, i = 0;

    gfxInitDefault();
    LightEvent_Init(&event, RESET_ONESHOT);

    OggOpusFile *opusFile = op_open_file("sdmc:/3ds/main.opus", &rc);
    const size_t bufferSize = WAVEBUF_SIZE * ARRAY_SIZE(wave_buffer);
    audio_buffer = (int16_t*)linearAlloc(bufferSize);
    if (!audio_buffer) {
        return 0;
    }

    memset(&wave_buffer, 0, sizeof(wave_buffer));
    int16_t *buffer = audio_buffer;
    for (i=0; i<ARRAY_SIZE(wave_buffer); i++) {
        wave_buffer[i].data_vaddr = buffer;
        wave_buffer[i].status = NDSP_WBUF_DONE;
        buffer += WAVEBUF_SIZE / sizeof(buffer[0]);
    }

    ndspInit();
    ndspChnReset(0);
    ndspSetOutputMode(NDSP_OUTPUT_STEREO);
    ndspChnSetInterp(0, NDSP_INTERP_POLYPHASE);
    ndspChnSetRate(0, SAMPLE_RATE);
    ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16);
    ndspSetCallback(audioCallback, NULL);

    while (aptMainLoop()) {
        for (i=0; i<ARRAY_SIZE(wave_buffer); i++) {
            if (wave_buffer[i].status != NDSP_WBUF_DONE) {
                continue;
            }
            
            if (!fillBuffer(opusFile, &wave_buffer[i])) {
                break;
            }
        }
        LightEvent_Wait(&event);
    }
    LightEvent_Signal(&event);

    ndspChnReset(0);
    linearFree(audio_buffer);
    ndspExit();
    op_free(opusFile);
    gfxExit();
    return 0;
}


返回上一頁