diff --git a/include/utils/sound.h b/include/utils/sound.h new file mode 100644 index 0000000..6895ec8 --- /dev/null +++ b/include/utils/sound.h @@ -0,0 +1,19 @@ +#pragma once + +#include <3ds.h> +#include + +class sound +{ +public: + sound(const std::string& path, int channel = 1, bool toloop = true); + ~sound(); + void play(); + void stop(); + +private: + u32 dataSize; + ndspWaveBuf waveBuf; + u8* data = NULL; + int chnl; +}; \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 1a121f4..31af536 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -32,6 +32,7 @@ #include "screens/screenCommon.hpp" #include "utils/config.hpp" +#include "utils/sound.h" #include "utils/structs.hpp" #include <3ds.h> @@ -39,8 +40,11 @@ #include bool exiting = false; - +bool dspFound = false; touchPosition touch; +sound *bgm = NULL; +bool songIsFound = false; + // If button Position pressed -> Do something. bool touching(touchPosition touch, Structs::ButtonPos button) { @@ -50,6 +54,28 @@ bool touching(touchPosition touch, Structs::ButtonPos button) { return false; } + +void loadSoundEffects(void) { + if (dspFound == true) { + if( access( "sdmc:/3ds/Universal-Updater/Music.wav", F_OK ) != -1 ) { + bgm = new sound("sdmc:/3ds/Universal-Updater/Music.wav", 1, true); + songIsFound = true; + } + } +} + +void playMusic(void) { + if (songIsFound == true) { + bgm->play(); + } +} + +void stopMusic(void) { + if (songIsFound == true) { + bgm->stop(); + } +} + int main() { gfxInitDefault(); @@ -72,6 +98,14 @@ int main() Gui::setScreen(std::make_unique()); osSetSpeedupEnable(true); // Enable speed-up for New 3DS users + if( access( "sdmc:/3ds/dspfirm.cdc", F_OK ) != -1 ) { + ndspInit(); + dspFound = true; + loadSoundEffects(); + playMusic(); + } + + // Loop as long as the status is not exit while (aptMainLoop() && !exiting) { @@ -87,6 +121,14 @@ int main() C3D_FrameEnd(0); gspWaitForVBlank(); } + + if (songIsFound == true) { + stopMusic(); + } + delete bgm; + if (dspFound == true) { + ndspExit(); + } Config::save(); Gui::exit(); gfxExit(); diff --git a/source/utils/sound.cpp b/source/utils/sound.cpp new file mode 100644 index 0000000..3f26ac3 --- /dev/null +++ b/source/utils/sound.cpp @@ -0,0 +1,133 @@ +#include "utils/sound.h" + +#include +#include +#include +#include + +using std::string; + +// Reference: http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/ +typedef struct _WavHeader { + char magic[4]; // "RIFF" + u32 totallength; // Total file length, minus 8. + char wavefmt[8]; // Should be "WAVEfmt " + u32 format; // 16 for PCM format + u16 pcm; // 1 for PCM format + u16 channels; // Channels + u32 frequency; // Sampling frequency + u32 bytes_per_second; + u16 bytes_by_capture; + u16 bits_per_sample; + char data[4]; // "data" + u32 bytes_in_data; +} WavHeader; +static_assert(sizeof(WavHeader) == 44, "WavHeader size is not 44 bytes."); + +sound::sound(const string& path, int channel, bool toloop) +{ + + ndspSetOutputMode(NDSP_OUTPUT_STEREO); + ndspSetOutputCount(2); // Num of buffers + + // Reading wav file + FILE* fp = fopen(path.c_str(), "rb"); + + if (!fp) { + printf("Could not open the WAV file: %s\n", path.c_str()); + return; + } + + WavHeader wavHeader; + size_t read = fread(&wavHeader, 1, sizeof(wavHeader), fp); + if (read != sizeof(wavHeader)) { + // Short read. + printf("WAV file header is too short: %s\n", path.c_str()); + fclose(fp); + return; + } + + // Verify the header. + static const char RIFF_magic[4] = {'R','I','F','F'}; + if (memcmp(wavHeader.magic, RIFF_magic, sizeof(wavHeader.magic)) != 0) { + // Incorrect magic number. + printf("Wrong file format.\n"); + fclose(fp); + return; + } + + if (wavHeader.totallength == 0 || + (wavHeader.channels != 1 && wavHeader.channels != 2) || + (wavHeader.bits_per_sample != 8 && wavHeader.bits_per_sample != 16)) + { + // Unsupported WAV file. + printf("Corrupted wav file.\n"); + fclose(fp); + return; + } + + // Get the file size. + fseek(fp, 0, SEEK_END); + dataSize = ftell(fp) - sizeof(wavHeader); + + // Allocating and reading samples + data = static_cast(linearAlloc(dataSize)); + fseek(fp, 44, SEEK_SET); + fread(data, 1, dataSize, fp); + fclose(fp); + dataSize /= 2; // FIXME: 16-bit or stereo? + + // Find the right format + u16 ndspFormat; + if (wavHeader.bits_per_sample == 8) { + ndspFormat = (wavHeader.channels == 1) ? + NDSP_FORMAT_MONO_PCM8 : + NDSP_FORMAT_STEREO_PCM8; + } else { + ndspFormat = (wavHeader.channels == 1) ? + NDSP_FORMAT_MONO_PCM16 : + NDSP_FORMAT_STEREO_PCM16; + } + + ndspChnReset(channel); + ndspChnSetInterp(channel, NDSP_INTERP_NONE); + ndspChnSetRate(channel, float(wavHeader.frequency)); + ndspChnSetFormat(channel, ndspFormat); + + // Create and play a wav buffer + memset(&waveBuf, 0, sizeof(waveBuf)); + + waveBuf.data_vaddr = reinterpret_cast(data); + waveBuf.nsamples = dataSize / (wavHeader.bits_per_sample >> 3); + waveBuf.looping = toloop; + waveBuf.status = NDSP_WBUF_FREE; + chnl = channel; +} + +sound::~sound() +{ + waveBuf.data_vaddr = 0; + waveBuf.nsamples = 0; + waveBuf.looping = false; + waveBuf.status = 0; + ndspChnWaveBufClear(chnl); + + if (data) { + linearFree(data); + } +} + +void sound::play() +{ + if (!data) + return; + DSP_FlushDataCache(data, dataSize); + ndspChnWaveBufAdd(chnl, &waveBuf); +} + +void sound::stop() +{ + if (!data) + return; + ndspChnWaveBufClear(chnl); +} \ No newline at end of file