From 7ed6507938adb0c220774e121ea303c2c0eedebf Mon Sep 17 00:00:00 2001 From: mori0091 Date: Sat, 17 May 2025 20:16:03 +0900 Subject: [PATCH] feat(NDP): add sound effects functionality --- README.md | 1 + include/NDP.h | 96 ++++++++++++++++++++++++++++++++- src/NDP/NDP__internal.h | 4 ++ src/NDP/NDP__load_data.c | 31 +++++++++++ src/NDP/NDP_load_bgm.c | 11 +--- src/NDP/NDP_load_sfx.c | 23 ++++++++ src/NDP/NDP_open_sfx_bmem.c | 22 ++++++++ src/NDP/NDP_open_sfx_mem.c | 22 ++++++++ src/NDP/NDP_open_sfx_resource.c | 26 +++++++++ src/NDP/NDP_read_sfx.c | 31 +++++++++++ src/NDP/NDP_set_sfx_ptr.c | 24 +++++++++ src/NDP/NDS__verify.c | 20 +++++++ 12 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 src/NDP/NDP__load_data.c create mode 100644 src/NDP/NDP_load_sfx.c create mode 100644 src/NDP/NDP_open_sfx_bmem.c create mode 100644 src/NDP/NDP_open_sfx_mem.c create mode 100644 src/NDP/NDP_open_sfx_resource.c create mode 100644 src/NDP/NDP_read_sfx.c create mode 100644 src/NDP/NDP_set_sfx_ptr.c create mode 100644 src/NDP/NDS__verify.c diff --git a/README.md b/README.md index b99b2c19..853e4ca9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ The below functionality is supported. - NDP - PSG Driver for MSX - NTSC (60Hz) - Stop, Start, Pause, Resume, Auto-Repeat. + - Playing sound effects (SFX) during playing background music (BGM). - NDP was originally programmed and provided by [naruto2413](https://x.com/naruto2413) and later modified for libmsx by Daishi Mori ([mori0091](https://x.com/mori0091)). diff --git a/include/NDP.h b/include/NDP.h index a13fce4c..8a7e2f4d 100644 --- a/include/NDP.h +++ b/include/NDP.h @@ -71,8 +71,8 @@ uint16_t NDP_version(void); /** * `MSX` Main routine of the NDP sound driver. * - * To play back background music, this function must be called at each VSYNC - * timing. + * To play back background music and/or sound effects, this function must be + * called at each VSYNC timing. * * The easiest way is to set this function as the VSYNC interrupt handler by * calling set_vsync_handler(). @@ -283,4 +283,96 @@ size_t NDP_read_metadata(NDPFile * ndp, uint8_t * buf, size_t buf_size); /** @} */ +// ---------------------------------------------------------------------- +/** + * \defgroup NDP_SFX Open NDS sound effects data, and set it in the driver. + * \ingroup NDP + * + * @{ + */ + +/** + * `MSX` Container of an opened NDS sound effects data. + */ +typedef struct NDSFile { + MemFile mf; +} NDSFile; + +/** + * `MSX` Open NDS sound effects data stored in ROM / RAM. + * + * \param nds Pointer to a NDSFile to be initialized. + * \param loc Location of the NDS sound effects data. + * \param size Size in bytes. + * \return Number of sound effects contained in the NDS file. + */ +int NDP_open_sfx_mem(NDSFile * nds, const uint8_t * loc, size_t size); + +/** + * `MSX` Open NDS sound effects data stored in banked memory (MegaROM). + * + * \param nds Pointer to a NDSFile to be initialized. + * \param loc Location of the NDS sound effects data. + * \param size Size in bytes. + * \return Number of sound effects contained in the NDS file. + */ +int NDP_open_sfx_bmem(NDSFile * nds, bmemptr_t loc, uint32_t size); + +/** + * `MSX` Open NDS file stored as named resources in banked memory (MegaROM). + * + * \param nds Pointer to a NDSFile to be initialized. + * \param path Path/File name (*.NDS) of the resource. + * \return Number of sound effects contained in the NDS file. + */ +int NDP_open_sfx_resource(NDSFile * nds, const char * path); + +/** + * `MSX` Setup the NDS sound effects data to NDP sound driver. + * + * \param ptr Pointer to the sound effects data. + */ +void NDP_set_sfx_ptr(void * ptr); + +/** + * `MSX` Read the NDS sound effects data. + * + * Read the specified sound effects data from the NDSFile into the specified RAM + * buffer. + * + * \param index Sound effect number. + * \param nds Pointer to the NDSFile opened by `NDP_open_sfx_*()`. + * \param buf Pointer to RAM buffer. + * \param buf_size Size of the buffer. + * + * \return the size of sound effect on success, `0` otherwise. + */ +size_t NDP_read_sfx(uint8_t index, NDSFile * nds, uint8_t * buf, size_t buf_size); + +/** + * `MSX` Load and setup the NDS sound effects data to NDP sound driver. + * + * Read the specified sound effects data from the NDSFile into the specified RAM + * buffer, and set the NDP sound driver to play the sound effects from the buffer. + * + * This is almost same as the following code: + * ``` c + * if (NDP_read_sfx(index, nds, buf, buf_size)) { + * NDP_set_sfx_ptr(buf); + * } + * ``` + * + * \param index Sound effect number. + * \param nds Pointer to the NDSFile opened by `NDP_open_sfx_*()`. + * \param buf Pointer to RAM buffer. + * \param buf_size Size of the buffer. + * + * \return `true` on success, `false` otherwise. + * + * \sa NDP_read_sfx(), NDP_set_sfx_ptr() + */ +bool NDP_load_sfx(uint8_t index, NDSFile * nds, uint8_t * buf, size_t buf_size); + +/** @} */ + #endif // NDP_H_ diff --git a/src/NDP/NDP__internal.h b/src/NDP/NDP__internal.h index 50bb1a7d..5641a40c 100644 --- a/src/NDP/NDP__internal.h +++ b/src/NDP/NDP__internal.h @@ -15,6 +15,8 @@ #ifndef NDP__INTERNAL_H_ #define NDP__INTERNAL_H_ +#include + #include #include @@ -32,4 +34,6 @@ extern void NDP_ADRSET(void * ptr); extern void NDP__set_song_ptr(uint8_t bank, void * ptr); +extern bool NDP__load_data(MemFile * mf, void * buf, size_t buf_size); + #endif // NDP__INTERNAL_H_ diff --git a/src/NDP/NDP__load_data.c b/src/NDP/NDP__load_data.c new file mode 100644 index 00000000..72a27128 --- /dev/null +++ b/src/NDP/NDP__load_data.c @@ -0,0 +1,31 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP__load_data.c + */ + +#include +#include "./NDP__internal.h" + +#include + +#include + +bool NDP__load_data(MemFile * mf, void * buf, size_t buf_size) { + assert(PAGE_ADDR(3) <= buf); + const uint32_t len = mfsize(mf); + if (buf && (len <= buf_size)) { + mfseek(mf, 0, MEM_SEEK_SET); + mfread(mf, buf, len); + return true; + } + return false; +} diff --git a/src/NDP/NDP_load_bgm.c b/src/NDP/NDP_load_bgm.c index 07ebe01f..22be6355 100644 --- a/src/NDP/NDP_load_bgm.c +++ b/src/NDP/NDP_load_bgm.c @@ -14,18 +14,9 @@ #include #include "./NDP__internal.h" -#include "memfile.h" - -#include - -#include bool NDP_load_bgm(NDPFile * ndp, uint8_t * buf, size_t buf_size) { - uint32_t len = mfsize(&ndp->mf); - if (buf && (len <= buf_size)) { - assert((uint8_t *)PAGE_ADDR(3) <= buf); - mfseek(&ndp->mf, 0, MEM_SEEK_SET); - mfread(&ndp->mf, buf, len); + if (NDP__load_data(&ndp->mf, buf, buf_size)) { NDP__set_song_ptr(0, buf); return true; } diff --git a/src/NDP/NDP_load_sfx.c b/src/NDP/NDP_load_sfx.c new file mode 100644 index 00000000..d4bf30c8 --- /dev/null +++ b/src/NDP/NDP_load_sfx.c @@ -0,0 +1,23 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_load_sfx.c + */ + +#include + +bool NDP_load_sfx(uint8_t index, NDSFile * nds, uint8_t * buf, size_t buf_size) { + if (NDP_read_sfx(index, nds, buf, buf_size)) { + NDP_set_sfx_ptr(buf); + return true; + } + return false; +} diff --git a/src/NDP/NDP_open_sfx_bmem.c b/src/NDP/NDP_open_sfx_bmem.c new file mode 100644 index 00000000..c35f41c7 --- /dev/null +++ b/src/NDP/NDP_open_sfx_bmem.c @@ -0,0 +1,22 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_open_sfx_bmem.c + */ + +#include + +extern int NDS__verify(MemFile * mf); + +int NDP_open_sfx_bmem(NDSFile * nds, bmemptr_t loc, uint32_t size) { + mfopen_bmem(&nds->mf, loc, size); + return NDS__verify(&nds->mf); +} diff --git a/src/NDP/NDP_open_sfx_mem.c b/src/NDP/NDP_open_sfx_mem.c new file mode 100644 index 00000000..127016c1 --- /dev/null +++ b/src/NDP/NDP_open_sfx_mem.c @@ -0,0 +1,22 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_open_sfx_mem.c + */ + +#include + +extern int NDS__verify(MemFile * mf); + +int NDP_open_sfx_mem(NDSFile * nds, const uint8_t * loc, size_t size) { + mfopen_mem(&nds->mf, (uint8_t *)loc, size); + return NDS__verify(&nds->mf); +} diff --git a/src/NDP/NDP_open_sfx_resource.c b/src/NDP/NDP_open_sfx_resource.c new file mode 100644 index 00000000..cb5bf99c --- /dev/null +++ b/src/NDP/NDP_open_sfx_resource.c @@ -0,0 +1,26 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_open_sfx_resource.c + */ + +#include +#include + +extern int NDS__verify(MemFile * mf); + +int NDP_open_sfx_resource(NDSFile * nds, const char * path) { + const ResourceIndex * r = resource_find(path); + if (!r || bmem_get(r->offset) != 0xfe) return 0; + const size_t size = bmem_get_u16(r->offset + 3) - bmem_get_u16(r->offset + 1) + 1; + if (size < r->size - 7) return 0; + return NDP_open_sfx_bmem(nds, r->offset+7, size); +} diff --git a/src/NDP/NDP_read_sfx.c b/src/NDP/NDP_read_sfx.c new file mode 100644 index 00000000..dc8362a8 --- /dev/null +++ b/src/NDP/NDP_read_sfx.c @@ -0,0 +1,31 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_read_sfx.c + */ + +#include +#include "./NDP__internal.h" + +size_t NDP_read_sfx(uint8_t index, NDSFile * nds, uint8_t * buf, size_t buf_size) { + MemFile * mf = &nds->mf; + mfseek(mf, 0, MEM_SEEK_SET); + const uint8_t num = mfread_u8(mf); + if (num <= index || !buf) return 0; + mfseek(mf, 2 * index, MEM_SEEK_CUR); + const uint16_t beg = mfread_u16(mf); + const uint16_t end = (index+1 == num) ? (uint16_t)mfsize(mf) : mfread_u16(mf); + if (end <= beg) return 0; + const size_t sz = end - beg; + if (buf_size < sz) return 0; + mfseek(mf, beg, MEM_SEEK_SET); + return mfread(mf, buf, sz); +} diff --git a/src/NDP/NDP_set_sfx_ptr.c b/src/NDP/NDP_set_sfx_ptr.c new file mode 100644 index 00000000..cd8b9659 --- /dev/null +++ b/src/NDP/NDP_set_sfx_ptr.c @@ -0,0 +1,24 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDP_set_sfx_ptr.c + */ + +#include +#include "./NDP__internal.h" + +void NDP_set_sfx_ptr(void * ptr) { + (void)ptr; // HL + __asm__("di"); + __asm__("ex de, hl"); + __asm__("call _NDP_SEPLAY"); + __asm__("ei"); +} diff --git a/src/NDP/NDS__verify.c b/src/NDP/NDS__verify.c new file mode 100644 index 00000000..0d844db0 --- /dev/null +++ b/src/NDP/NDS__verify.c @@ -0,0 +1,20 @@ +// -*- coding: utf-8-unix -*- +/* + * Copyright (c) 2021-2025 Daishi Mori (mori0091) + * + * This software is released under the MIT License.\n + * See https://github.com/mori0091/libmsx/blob/main/LICENSE + * + * GitHub libmsx project\n + * https://github.com/mori0091/libmsx + */ +/** + * \file NDS__verify.c + */ + +#include + +int NDS__verify(MemFile * mf) { + mfseek(mf, 0, MEM_SEEK_SET); + return (int)mfread_u8(mf); +}