BCSTM Player
This commit is contained in:
272
source/music.c
272
source/music.c
@@ -27,8 +27,31 @@
|
||||
#include "music.h"
|
||||
#include "loading.h"
|
||||
|
||||
// Play a given audio struct
|
||||
Result update_audio(audio_s * audio)
|
||||
u32 read32(char *music_buf, ssize_t *cursor)
|
||||
{
|
||||
u32 ret;
|
||||
memcpy(&ret, music_buf + *cursor, 4);
|
||||
*cursor += 4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
u16 read16(char *music_buf, ssize_t *cursor)
|
||||
{
|
||||
u16 ret;
|
||||
memcpy(&ret, music_buf + *cursor, 2);
|
||||
*cursor += 2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 read8(char *music_buf, ssize_t *cursor)
|
||||
{
|
||||
u8 ret;
|
||||
memcpy(&ret, music_buf + *cursor, 1);
|
||||
*cursor += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result update_audio_ogg(audio_ogg_s * audio)
|
||||
{
|
||||
u32 size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read;
|
||||
DEBUG("<update_audio> Audio Size: %ld\n", size);
|
||||
@@ -64,10 +87,10 @@ Result update_audio(audio_s * audio)
|
||||
return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS);
|
||||
}
|
||||
|
||||
void thread_audio(void * data) {
|
||||
audio_s * audio = (audio_s *)data;
|
||||
void thread_audio_ogg(void * data) {
|
||||
audio_ogg_s * audio = (audio_ogg_s *)data;
|
||||
while(!audio->stop) {
|
||||
update_audio(audio);
|
||||
update_audio_ogg(audio);
|
||||
}
|
||||
ndspChnWaveBufClear(0);
|
||||
ndspChnReset(0);
|
||||
@@ -77,6 +100,245 @@ void thread_audio(void * data) {
|
||||
linearFree((void *)audio->wave_buf[1].data_vaddr);
|
||||
}
|
||||
|
||||
void play_audio_ogg(audio_ogg_s * audio) {
|
||||
audio->playing_thread = threadCreate(thread_audio_ogg, audio, 0x1000, 0x3F, 1, false);
|
||||
}
|
||||
|
||||
void stop_audio_ogg(audio_ogg_s ** audio_ptr) {
|
||||
audio_ogg_s * audio = *audio_ptr;
|
||||
if(audio->playing_thread)
|
||||
{
|
||||
audio->stop = true;
|
||||
threadJoin(audio->playing_thread, U64_MAX);
|
||||
threadFree(audio->playing_thread);
|
||||
}
|
||||
free(audio);
|
||||
*audio_ptr = NULL;
|
||||
}
|
||||
|
||||
int init_audio(audio_s *audio)
|
||||
{
|
||||
u32 magic = read32(audio->music_buf, &audio->cursor);
|
||||
DEBUG("Loading music, music_size: %lu, magic: 0x%08lx\n", audio->music_size, magic);
|
||||
audio->is_little_endian = read16(audio->music_buf, &audio->cursor) == 0xFEFF;
|
||||
audio->info_offset = 0;
|
||||
audio->data_offset = 0;
|
||||
|
||||
if (magic != 0x4D545343) // CSTM
|
||||
{
|
||||
free(audio->music_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
audio->cursor = 0x10;
|
||||
u16 sbc = read16(audio->music_buf, &audio->cursor);
|
||||
audio->cursor += 2;
|
||||
|
||||
for (u16 i = 0; i < sbc; ++i)
|
||||
{
|
||||
u16 sec = read16(audio->music_buf, &audio->cursor);
|
||||
audio->cursor += 2;
|
||||
u32 off = read32(audio->music_buf, &audio->cursor);
|
||||
audio->cursor += 4;
|
||||
DEBUG("Reading sbc: %04lx, %08lx\n", sec, off);
|
||||
if (sec == 0x4000) // Info block
|
||||
audio->info_offset = off;
|
||||
if (sec == 0x4002) // Data block
|
||||
audio->data_offset = off;
|
||||
}
|
||||
|
||||
DEBUG("Info offset: 0x%08lx, data offset: 0x%08lx\n", audio->info_offset, audio->data_offset);
|
||||
|
||||
if (audio->data_offset == 0 || audio->info_offset == 0)
|
||||
{
|
||||
free(audio->music_buf);
|
||||
return -2;
|
||||
}
|
||||
|
||||
audio->cursor = audio->info_offset + 0x20;
|
||||
|
||||
if (read8(audio->music_buf, &audio->cursor) != 2) // Encoding - 2 is DSP_ADPCM
|
||||
{
|
||||
free(audio->music_buf);
|
||||
return -3;
|
||||
}
|
||||
|
||||
audio->is_looping = read8(audio->music_buf, &audio->cursor);
|
||||
audio->channel_count = read8(audio->music_buf, &audio->cursor);
|
||||
|
||||
if (audio->channel_count > 2)
|
||||
{
|
||||
free(audio->music_buf);
|
||||
return -4;
|
||||
}
|
||||
|
||||
DEBUG("Channel count: %d, is looping: %d\n", audio->channel_count, audio->is_looping);
|
||||
|
||||
audio->cursor = audio->info_offset + 0x24;
|
||||
audio->sample_rate = read32(audio->music_buf, &audio->cursor);
|
||||
u32 _loop_pos = read32(audio->music_buf, &audio->cursor);
|
||||
u32 _loop_end = read32(audio->music_buf, &audio->cursor);
|
||||
audio->num_blocks = read32(audio->music_buf, &audio->cursor);
|
||||
audio->block_size = read32(audio->music_buf, &audio->cursor);
|
||||
audio->block_samples = read32(audio->music_buf, &audio->cursor);
|
||||
audio->cursor += 4;
|
||||
audio->last_block_samples = read32(audio->music_buf, &audio->cursor);
|
||||
audio->last_block_size = read32(audio->music_buf, &audio->cursor);
|
||||
|
||||
DEBUG("sample_rate: %lu, loop_start: %lu, loop_end: %lu, num_blocks: %lu, block_size: %lu, block_samples: %lu, last_block_samples: %lu, last_block_size: %lu\n",
|
||||
audio->sample_rate, _loop_pos, _loop_end, audio->num_blocks, audio->block_size, audio->block_samples, audio->last_block_samples, audio->last_block_size);
|
||||
|
||||
audio->loop_start = _loop_pos / audio->block_samples;
|
||||
audio->loop_end = (_loop_end % audio->block_samples ? audio->num_blocks : _loop_end / audio->block_samples);
|
||||
|
||||
while (read32(audio->music_buf, &audio->cursor) != 0x4102); // find channel info header
|
||||
audio->cursor += read32(audio->music_buf, &audio->cursor) + audio->channel_count * 8 - 12;
|
||||
|
||||
for (u8 i = 0; i < audio->channel_count; ++i)
|
||||
{
|
||||
memcpy(audio->adpcm_coefs[i], audio->music_buf + audio->cursor, sizeof(unsigned short) * 16);
|
||||
audio->cursor += sizeof(unsigned short) * 16;
|
||||
memcpy(&(audio->adpcm_data[i][0]), audio->music_buf + audio->cursor, sizeof(ndspAdpcmData));
|
||||
audio->cursor += sizeof(ndspAdpcmData);
|
||||
memcpy(&(audio->adpcm_data[i][1]), audio->music_buf + audio->cursor, sizeof(ndspAdpcmData));
|
||||
audio->cursor += sizeof(ndspAdpcmData);
|
||||
audio->cursor += 2; // skip padding
|
||||
}
|
||||
|
||||
audio->cursor = audio->data_offset + 0x20;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int start_play(audio_s *audio) {
|
||||
audio->current_block = 0;
|
||||
for (u8 i = 0; i < audio->channel_count; ++i)
|
||||
{
|
||||
audio->channel[i] = 0;
|
||||
while (audio->channel[i] < 24 && ((audio->active_channels >> audio->channel[i]) & 1)) {
|
||||
audio->channel[i]++;
|
||||
}
|
||||
if (audio->channel[i] == 24) {
|
||||
return -1;
|
||||
}
|
||||
audio->active_channels |= 1 << audio->channel[i];
|
||||
ndspChnWaveBufClear(audio->channel[i]);
|
||||
|
||||
static float mix[16];
|
||||
ndspChnSetFormat(audio->channel[i], NDSP_FORMAT_ADPCM | NDSP_3D_SURROUND_PREPROCESSED);
|
||||
ndspChnSetRate(audio->channel[i], audio->sample_rate);
|
||||
|
||||
if (audio->channel_count == 1)
|
||||
{
|
||||
mix[0] = mix[1] = 0.5f;
|
||||
} else if (audio->channel_count == 2)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
mix[0] = 0.8f;
|
||||
mix[1] = 0.0f;
|
||||
mix[2] = 0.2f;
|
||||
mix[3] = 0.0f;
|
||||
} else
|
||||
{
|
||||
mix[0] = 0.0f;
|
||||
mix[1] = 0.8f;
|
||||
mix[2] = 0.0f;
|
||||
mix[3] = 0.2f;
|
||||
}
|
||||
}
|
||||
ndspChnSetMix(audio->channel[i], mix);
|
||||
ndspChnSetAdpcmCoefs(audio->channel[i], audio->adpcm_coefs[i]);
|
||||
|
||||
for (u8 j = 0; j < BUFFER_COUNT; ++j)
|
||||
{
|
||||
memset(&(audio->wave_buf[i][j]), 0, sizeof(ndspWaveBuf));
|
||||
audio->wave_buf[i][j].status = NDSP_WBUF_DONE;
|
||||
audio->buffer_data[i][j] = linearAlloc(audio->block_size);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fill_buffers(audio_s *audio)
|
||||
{
|
||||
DEBUG("Filling buffers...\n");
|
||||
for (u8 bufIndex = 0; bufIndex < BUFFER_COUNT; ++bufIndex)
|
||||
{
|
||||
if (audio->wave_buf[0][bufIndex].status != NDSP_WBUF_DONE) continue;
|
||||
if (audio->channel_count == 2 && audio->wave_buf[1][bufIndex].status != NDSP_WBUF_DONE) continue;
|
||||
|
||||
if (audio->is_looping && audio->current_block == audio->loop_end)
|
||||
{
|
||||
audio->current_block = audio->loop_start;
|
||||
audio->cursor = audio->data_offset + 0x20 + audio->block_size * audio->channel_count * audio->loop_start;
|
||||
}
|
||||
|
||||
if (!audio->is_looping && audio->current_block == audio->loop_end)
|
||||
{
|
||||
// stop playing
|
||||
}
|
||||
|
||||
for (u8 channelIndex = 0; channelIndex < audio->channel_count; ++channelIndex)
|
||||
{
|
||||
ndspWaveBuf *buf = &(audio->wave_buf[channelIndex][bufIndex]);
|
||||
memset(buf, 0, sizeof(ndspWaveBuf));
|
||||
buf->data_adpcm = audio->buffer_data[channelIndex][bufIndex];
|
||||
memcpy(buf->data_adpcm, audio->music_buf + audio->cursor, (audio->current_block == audio->num_blocks - 1) ? audio->last_block_size : audio->block_size);
|
||||
audio->cursor += (audio->current_block == audio->num_blocks - 1) ? audio->last_block_size : audio->block_size;
|
||||
DSP_FlushDataCache(buf->data_adpcm, audio->block_size);
|
||||
|
||||
if (audio->current_block == 0)
|
||||
buf->adpcm_data = &(audio->adpcm_data[channelIndex][0]);
|
||||
else if (audio->current_block == audio->loop_start)
|
||||
buf->adpcm_data = &(audio->adpcm_data[channelIndex][1]);
|
||||
|
||||
if (audio->current_block == audio->num_blocks - 1)
|
||||
buf->nsamples = audio->last_block_samples;
|
||||
else
|
||||
buf->nsamples = audio->block_samples;
|
||||
|
||||
ndspChnWaveBufAdd(audio->channel[channelIndex], buf);
|
||||
}
|
||||
audio->current_block++;
|
||||
}
|
||||
}
|
||||
|
||||
// Play a given audio struct
|
||||
void update_audio(audio_s * audio)
|
||||
{
|
||||
u32 current_time = svcGetSystemTick();
|
||||
if (current_time - audio->last_time > 1e8)
|
||||
{
|
||||
fill_buffers(audio);
|
||||
audio->last_time = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
void thread_audio(void * data) {
|
||||
audio_s * audio = (audio_s *)data;
|
||||
int res = init_audio(audio);
|
||||
if (res < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
start_play(audio);
|
||||
while(!audio->stop) {
|
||||
update_audio(audio);
|
||||
}
|
||||
for (u8 i = 0; i < audio->channel_count; ++i)
|
||||
{
|
||||
ndspChnWaveBufClear(audio->channel[i]);
|
||||
ndspChnReset(audio->channel[i]);
|
||||
audio->active_channels &= ~(1 << audio->channel[i]);
|
||||
for (u8 j = 0; j < BUFFER_COUNT; ++j) {
|
||||
linearFree(audio->buffer_data[i][j]);
|
||||
}
|
||||
}
|
||||
free(audio->music_buf);
|
||||
}
|
||||
|
||||
void play_audio(audio_s * audio) {
|
||||
audio->playing_thread = threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user