diff --git a/Makefile b/Makefile index 51f31ad..60c5239 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 ASFLAGS := -g $(ARCH) LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -LIBS := -larchive -ljansson -lcitro3d -lctrud -lm -lz +LIBS := -lvorbisidec -logg -larchive -ljansson -lcitro3d -lctrud -lm -lz #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/include/music.h b/include/music.h new file mode 100644 index 0000000..077bbf7 --- /dev/null +++ b/include/music.h @@ -0,0 +1,52 @@ +/* +* This file is part of Anemone3DS +* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md +* +* This program 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 Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#ifndef MUSIC_H +#define MUSIC_H + +#include "common.h" +#include "fs.h" +#include "unicode.h" + +#include +#include + +#define BUF_TO_READ 40960 // How much data should be buffered at a time + +typedef struct { + char *filename; + OggVorbis_File vf; + ndspWaveBuf wave_buf[2]; + float mix[12]; + u8 buf_pos; + long data_read; + volatile bool stop; +} audio_s; + +Result load_audio(u16 *, audio_s *); +Result play_audio(audio_s *); + +#endif \ No newline at end of file diff --git a/source/main.c b/source/main.c index 53283e3..3bc3327 100644 --- a/source/main.c +++ b/source/main.c @@ -30,12 +30,14 @@ #include "splashes.h" #include "draw.h" #include "camera.h" +#include "music.h" #include "remote.h" #include "instructions.h" #include "pp2d/pp2d/pp2d.h" #include bool quit = false; +audio_s * audio; static bool homebrew = false; static bool installed_themes = false; @@ -75,6 +77,7 @@ static void init_services(void) cfguInit(); ptmuInit(); acInit(); + ndspInit(); APT_GetAppCpuTimeLimit(&old_time_limit); APT_SetAppCpuTimeLimit(30); httpcInit(0); @@ -95,6 +98,7 @@ static void exit_services(void) if (old_time_limit != UINT32_MAX) APT_SetAppCpuTimeLimit(old_time_limit); httpcExit(); acExit(); + ndspExit(); } static void stop_install_check(void) @@ -362,7 +366,10 @@ int main(void) } if(qr_mode) take_picture(); - else if(preview_mode) draw_preview(TEXTURE_PREVIEW, preview_offset); + else if(preview_mode) + { + draw_preview(TEXTURE_PREVIEW, preview_offset); + } else { if(!iconLoadingThread_arg.run_thread) { @@ -426,9 +433,16 @@ int main(void) { toggle_preview: if(!preview_mode) + { preview_mode = load_preview(*current_list, &preview_offset); + audio = calloc(sizeof(audio_s), 1); + load_audio(current_list->entries[current_list->selected_entry].path, audio); + } else + { preview_mode = false; + audio->stop = true; + } continue; } else if(preview_mode && kDown & (KEY_B | KEY_TOUCH)) diff --git a/source/music.c b/source/music.c new file mode 100644 index 0000000..2c92e1a --- /dev/null +++ b/source/music.c @@ -0,0 +1,121 @@ +/* +* This file is part of Anemone3DS +* Copyright (C) 2016-2018 Contributors in CONTRIBUTORS.md +* +* This program 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 Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 this program. If not, see . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#include "music.h" + +// Play a given audio struct +Result play_audio(audio_s *audio) +{ + long size = audio->wave_buf[audio->buf_pos].nsamples * 4 - audio->data_read; + char size_info[50] = {0}; + sprintf(size_info, "Audio Size: %ld\n", size); + DEBUG(size_info); + if (audio->wave_buf[audio->buf_pos].status == NDSP_WBUF_DONE) // only run if the current selected buffer has already finished playing + { + size_t read = ov_read(&audio->vf, (char*)audio->wave_buf[audio->buf_pos].data_vaddr + audio->data_read, size, NULL); // read 1 vorbis packet into wave buffer + + if (read <= 0) // EoF or error + { + ov_clear(&audio->vf); + if (read == 0) // EoF + { + ov_open(fopen(audio->filename, "rb"), &audio->vf, NULL, 0); // Reopen file. Don't need to reinit channel stuff since it's all the same as before + } else // Error :( + { + char error[100] = {0}; + sprintf(error, "Vorbis play error: %d\n", read); + DEBUG(error); + ndspChnReset(0); + return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA); + } + } else + { + audio->data_read += read; + if (read == size) { + audio->data_read = 0; + ndspChnWaveBufAdd(0, &audio->wave_buf[audio->buf_pos]); // Add buffer to ndsp + audio->buf_pos = 1 - audio->buf_pos; // switch to other buffer to load and prepare it while the current one is playing + } + } + } + return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS); +} + +void thread_audio(void* data) { + audio_s *audio = (audio_s*)data; + DEBUG(audio->filename); + while(!audio->stop) { + play_audio(audio); + } + free(audio); +} + +// Initialize the audio struct +Result load_audio(u16 *entry_path, audio_s *audio) +{ + u16 path[0x106] = {0}; + strucat(path, entry_path); + struacat(path, "/bgm.ogg"); + + ssize_t len = strulen(path, 0x106); + char *cpath = calloc(sizeof(char), len*sizeof(u16)); + utf16_to_utf8((u8*)cpath, path, len*sizeof(u16)); + audio->filename = cpath; + + audio->mix[0] = audio->mix[1] = 1.0f; // Determines volume for the 12 (?) different outputs. See http://smealum.github.io/ctrulib/channel_8h.html#a30eb26f1972cc3ec28370263796c0444 + + ndspChnSetInterp(0, NDSP_INTERP_LINEAR); + ndspChnSetRate(0, 44100); + ndspChnSetFormat(0, NDSP_FORMAT_STEREO_PCM16); // Tremor outputs ogg files in 16 bit PCM stereo + ndspChnSetMix(0, audio->mix); // See mix comment above + + FILE *file = fopen(cpath, "rb"); + if(file != NULL) + { + int e = ov_open(file, &audio->vf, NULL, 0); + if (e < 0) + { + char error[50]; + sprintf(error, "Vorbis: %d\n", e); + DEBUG(error); + return MAKERESULT(RL_FATAL, RS_INVALIDARG, RM_APPLICATION, RD_NO_DATA); + } + + vorbis_info *vi = ov_info(&audio->vf, -1); + ndspChnSetRate(0, vi->rate);// Set sample rate to what's read from the ogg file + + audio->wave_buf[0].nsamples = audio->wave_buf[1].nsamples = vi->rate / 4; // 4 bytes per sample, samples = rate (bytes) / 4 + audio->wave_buf[0].status = audio->wave_buf[1].status = NDSP_WBUF_DONE; // Used in play to stop from writing to current buffer + audio->wave_buf[0].data_vaddr = linearAlloc(BUF_TO_READ); // Most vorbis packets should only be 4 KB at most (?) Possibly dangerous assumption + audio->wave_buf[1].data_vaddr = linearAlloc(BUF_TO_READ); + DEBUG("Success!"); + threadCreate(thread_audio, audio, 0x1000, 0x3F, 1, true); + return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_APPLICATION, RD_SUCCESS); + } else { + DEBUG("File not found!\n"); + return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_APPLICATION, RD_NOT_FOUND); + } +} \ No newline at end of file diff --git a/source/unicode.c b/source/unicode.c index d8da99c..cdbe897 100644 --- a/source/unicode.c +++ b/source/unicode.c @@ -48,7 +48,9 @@ void printu(u16 *input) ssize_t buf_len = in_len + 1; // Plus 1 for proper null termination wchar_t *buf = calloc(buf_len, sizeof(wchar_t)); utf16_to_utf32((u32*)buf, input, buf_len); - printf("%ls\n", buf); + char cbuf[0x106]; + sprintf(cbuf, "%ls\n", buf); + DEBUG(cbuf); free(buf); }