Skip to content

Commit

Permalink
custom allocators
Browse files Browse the repository at this point in the history
ready function to manually trigger allocation
song override option
document core goals
  • Loading branch information
bbbradsmith committed Apr 26, 2024
1 parent 0073b83 commit 32e2713
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 174 deletions.
9 changes: 7 additions & 2 deletions core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,17 @@ inline const char* utf8_bom_skip(const char* text)

namespace nsf {

void* (*custom_alloc)(size_t size) = NULL;
void* (*custom_free)(void* ptr) = NULL;
void (*error_callback)(const char* msg) = NULL;
void (*debug_print_callback)(const char* msg) = NULL;
void (*fatal_callback)(const char* msg) = NULL;

void* alloc(size_t size)
{
void* a = std::malloc(size);
void* a;
if (custom_alloc) a = custom_alloc(size);
else a = std::malloc(size);
if (a == NULL) nsf::fatal("Out of memory.");
#if DEBUG_ALLOC
debug_alloc[a] = size;
Expand All @@ -71,7 +75,8 @@ void* alloc(size_t size)

void free(void* ptr)
{
std::free(ptr);
if (custom_free) custom_free(ptr);
else std::free(ptr);
#if DEBUG_ALLOC
size_t size = debug_alloc[ptr];
debug_alloc_total -= size;
Expand Down
2 changes: 2 additions & 0 deletions core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ namespace nsf {
// core.cpp

extern "C" {
extern void* (*custom_alloc)(size_t size);
extern void* (*custom_free)(void* ptr);
extern void (*error_callback)(const char* msg);
extern void (*debug_print_callback)(const char* msg);
extern void (*fatal_callback)(const char* msg);
Expand Down
258 changes: 131 additions & 127 deletions core/enums_data.h

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions core/nsfplaycore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ void nsfplay_destroy(NSFCore* core)
NSFCore::destroy(core);
}

void nsfplay_set_alloc(void* (*custom_alloc_)(size_t size),void* (*custom_free_)(void* ptr))
{
nsf::custom_alloc = custom_alloc_;
nsf::custom_free = custom_free_;
}

void nsfplay_set_error_log(void (*error_callback_)(const char* msg))
{
nsf::error_callback = error_callback_;
Expand Down Expand Up @@ -218,6 +224,12 @@ uint32_t nsfplay_render32(NSFCore* core, uint32_t samples, int32_t* stereo_outpu
return 0;
}

void nsfplay_ready(NSFCore* core)
{
NSF_UNUSED(core);
// TODO
}

uint8_t nsfplay_emu_peek(const NSFCore* core, uint16_t address)
{
NSF_UNUSED(core);
Expand Down
3 changes: 2 additions & 1 deletion enums/english.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,9 @@ LOCALSET EXPANSION_MMC5 "MMC5 Expansion" "Force enable/disable MMC5."
LOCALSET EXPANSION_VRC6 "VRC6 Expansion" "Force enable/disable VRC6."
LOCALSET EXPANSION_VRC7 "VRC7 Expansion" "Force enable/disable VRC7."
LOCALSET EXPANSION_N163 "N163 Expansion" "Force enable/disable N163."
LOCALSET EXPANSION_S5B "5B Expansion" "Force enable/disable 5B."
LOCALSET EXPANSION_S5B "5B Expansion" "Force enable/disable 5B."
LOCALSET EXPANSION_VT02 "VT02+ Expansion" "Force enable/disable VT02+."
LOCALSET OVERRIDE_NSF_SONG "NSF Song Override" "Force a specific song value, regardless of song count. -1 to disable."

#
# NSF Properties
Expand Down
1 change: 1 addition & 0 deletions enums/settings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ SETLIST ADVANCED EXPANSION_VRC7 ENABLE_AUTO AUTO
SETLIST ADVANCED EXPANSION_N163 ENABLE_AUTO AUTO
SETLIST ADVANCED EXPANSION_S5B ENABLE_AUTO AUTO
SETLIST ADVANCED EXPANSION_VT02 ENABLE_AUTO AUTO
SETINT ADVANCED OVERRIDE_NSF_SONG -1 -1 255 -1 255 INT

# for testing UI
GROUP SET TEST
Expand Down
10 changes: 10 additions & 0 deletions include/nsfplaycore.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ NSFCore* nsfplay_create(const char* ini_data);
NSFCore* nsfplay_create_init(const NSFSetInit* init);
void nsfplay_destroy(NSFCore* core);

// The default allocators (malloc/free) can be replaced if needed.
// Call with NULL pointers to restore the defaults.
void nsfplay_set_alloc(void* (*custom_alloc)(size_t size),void* (*custom_free)(void* ptr));

// logging and error handling

Expand Down Expand Up @@ -235,6 +238,13 @@ uint32_t nsfplay_render(NSFCore* core, uint32_t samples, int16_t* stereo_output)
// - ensure the output volume is low enough to prevent overflow (32-bit render is not able to clip out of range samples)
uint32_t nsfplay_render32(NSFCore* core, uint32_t samples, int32_t* stereo_output);

// manually trigger render buffer allocations if needed
// - normally this will be automatically called by render as the first song begins,
// but you can call this function to make sure it is done ahead of time if needed
// - allocation size depends on settings, and the set of active audo expansions
// - apply settings, then either load the desired NSF, or manually activate expansions
// with the EXPANSION_x settings, and at this point we can call ready() to trigger the allocation
void nsfplay_ready(NSFCore* core);

// direct emulation access
uint8_t nsfplay_emu_peek(const NSFCore* core, uint16_t address); // peek at memory, no read side effects
Expand Down
87 changes: 44 additions & 43 deletions include/nsfplayenums.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once
// generated by nsfplayenums.py
// 2024-04-25 22:50:33
// 2024-04-26 01:04:44

const int32_t NSF_LIST_COUNT = 12;
const int32_t NSF_LIST_ENABLE = 0;
Expand Down Expand Up @@ -158,7 +158,7 @@ const int32_t NSF_CHANNEL_S5B_S5B_2 = 28;
const int32_t NSF_CHANNEL_S5B_S5B_N = 29;
const int32_t NSF_CHANNEL_S5B_S5B_E = 30;

const int32_t NSF_SET_COUNT = 199;
const int32_t NSF_SET_COUNT = 200;
const int32_t NSF_SET_VOLUME = 0;
const int32_t NSF_SET_SAMPLERATE = 1;
const int32_t NSF_SET_STEREO = 2;
Expand Down Expand Up @@ -346,18 +346,19 @@ const int32_t NSF_SET_EXPANSION_VRC7 = 183;
const int32_t NSF_SET_EXPANSION_N163 = 184;
const int32_t NSF_SET_EXPANSION_S5B = 185;
const int32_t NSF_SET_EXPANSION_VT02 = 186;
const int32_t NSF_SET_TEST_INT = 187;
const int32_t NSF_SET_TEST_STR = 188;
const int32_t NSF_SET_TEST_LIST = 189;
const int32_t NSF_SET_TEST_HEX8 = 190;
const int32_t NSF_SET_TEST_HEX16 = 191;
const int32_t NSF_SET_TEST_HEX32 = 192;
const int32_t NSF_SET_TEST_COLOR = 193;
const int32_t NSF_SET_TEST_MSEC = 194;
const int32_t NSF_SET_TEST_MILL = 195;
const int32_t NSF_SET_TEST_DMILL = 196;
const int32_t NSF_SET_TEST_KEY = 197;
const int32_t NSF_SET_TEST_PRECISE = 198;
const int32_t NSF_SET_OVERRIDE_NSF_SONG = 187;
const int32_t NSF_SET_TEST_INT = 188;
const int32_t NSF_SET_TEST_STR = 189;
const int32_t NSF_SET_TEST_LIST = 190;
const int32_t NSF_SET_TEST_HEX8 = 191;
const int32_t NSF_SET_TEST_HEX16 = 192;
const int32_t NSF_SET_TEST_HEX32 = 193;
const int32_t NSF_SET_TEST_COLOR = 194;
const int32_t NSF_SET_TEST_MSEC = 195;
const int32_t NSF_SET_TEST_MILL = 196;
const int32_t NSF_SET_TEST_DMILL = 197;
const int32_t NSF_SET_TEST_KEY = 198;
const int32_t NSF_SET_TEST_PRECISE = 199;
const int32_t NSF_SETSTR_COUNT = 2;

const int32_t NSF_PROP_COUNT = 60;
Expand Down Expand Up @@ -422,35 +423,35 @@ const int32_t NSF_PROP_TEST_PRECISE = 57;
const int32_t NSF_PROP_TEST_PRDMILL = 58;
const int32_t NSF_PROP_SONG_TITLE = 59;

const int32_t NSF_TEXT_MENU_FILE = 585;
const int32_t NSF_TEXT_MENU_VIEW = 586;
const int32_t NSF_TEXT_MENU_OPTIONS = 587;
const int32_t NSF_TEXT_MENU_ABOUT = 588;
const int32_t NSF_TEXT_MENU_CHANNELS = 589;
const int32_t NSF_TEXT_MENU_MEMORY = 590;
const int32_t NSF_TEXT_MENU_LOG = 591;
const int32_t NSF_TEXT_MENU_KEYBOARD = 592;
const int32_t NSF_TEXT_MENU_DEBUGGER = 593;
const int32_t NSF_TEXT_MENU_OPEN = 594;
const int32_t NSF_TEXT_MENU_INI_LOAD = 595;
const int32_t NSF_TEXT_MENU_INI_SAVE = 596;
const int32_t NSF_ERROR_SET_INVALID = 597;
const int32_t NSF_ERROR_SET_TYPE = 598;
const int32_t NSF_ERROR_SETINT_RANGE = 599;
const int32_t NSF_ERROR_INI_NO_EQUALS = 600;
const int32_t NSF_ERROR_INI_BAD_KEY = 601;
const int32_t NSF_ERROR_INI_BAD_INT = 602;
const int32_t NSF_ERROR_INI_BAD_RANGE = 603;
const int32_t NSF_ERROR_INI_BAD_LIST_KEY = 604;
const int32_t NSF_ERROR_BIN_BAD = 605;
const int32_t NSF_ERROR_NSF_HEAD_BAD = 606;
const int32_t NSF_ERROR_NSF_VERSION_BAD = 607;
const int32_t NSF_ERROR_NSF2_META_BAD = 608;
const int32_t NSF_ERROR_NSF_HEAD_TEXT_BAD = 609;
const int32_t NSF_ERROR_NSFE_CHUNK_BAD = 610;
const int32_t NSF_ERROR_NSFE_MANDATORY = 611;
const int32_t NSF_ERROR_NSFE_NO_MANDATORY = 612;
const int32_t NSF_TEXT_COUNT = 613;
const int32_t NSF_TEXT_MENU_FILE = 587;
const int32_t NSF_TEXT_MENU_VIEW = 588;
const int32_t NSF_TEXT_MENU_OPTIONS = 589;
const int32_t NSF_TEXT_MENU_ABOUT = 590;
const int32_t NSF_TEXT_MENU_CHANNELS = 591;
const int32_t NSF_TEXT_MENU_MEMORY = 592;
const int32_t NSF_TEXT_MENU_LOG = 593;
const int32_t NSF_TEXT_MENU_KEYBOARD = 594;
const int32_t NSF_TEXT_MENU_DEBUGGER = 595;
const int32_t NSF_TEXT_MENU_OPEN = 596;
const int32_t NSF_TEXT_MENU_INI_LOAD = 597;
const int32_t NSF_TEXT_MENU_INI_SAVE = 598;
const int32_t NSF_ERROR_SET_INVALID = 599;
const int32_t NSF_ERROR_SET_TYPE = 600;
const int32_t NSF_ERROR_SETINT_RANGE = 601;
const int32_t NSF_ERROR_INI_NO_EQUALS = 602;
const int32_t NSF_ERROR_INI_BAD_KEY = 603;
const int32_t NSF_ERROR_INI_BAD_INT = 604;
const int32_t NSF_ERROR_INI_BAD_RANGE = 605;
const int32_t NSF_ERROR_INI_BAD_LIST_KEY = 606;
const int32_t NSF_ERROR_BIN_BAD = 607;
const int32_t NSF_ERROR_NSF_HEAD_BAD = 608;
const int32_t NSF_ERROR_NSF_VERSION_BAD = 609;
const int32_t NSF_ERROR_NSF2_META_BAD = 610;
const int32_t NSF_ERROR_NSF_HEAD_TEXT_BAD = 611;
const int32_t NSF_ERROR_NSFE_CHUNK_BAD = 612;
const int32_t NSF_ERROR_NSFE_MANDATORY = 613;
const int32_t NSF_ERROR_NSFE_NO_MANDATORY = 614;
const int32_t NSF_TEXT_COUNT = 615;

const int32_t NSF_LOCALE_COUNT = 1;
const int32_t NSF_LOCALE_ENGLISH = 0;
Expand Down
22 changes: 21 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ Support:
* `enum` - Definitions of all settings and text used by NSFPlay, allowing localized text in multiple languages ([python](https://www.python.org/) required).
* `wx` - [wxWidgets v3.2.4](https://github.com/wxWidgets/wxWidgets/tree/v3.2.4) cross platform GUI library.

## Core Library

The NSFPlay core library is intended to be easy to include in and project that needs NES/Famicom sound emulation. Here are some notes on its goals and useful properties:

* The code is C++11 with no external dependencies.
* Interface is `extern "C"`, to allow integration into both C and C++ projects.
* [Very permissively licensed](license.txt).
* All state is stored in an `NSFCore` [PIMPL](https://en.cppreference.com/w/cpp/language/pimpl) structure. Multiple core instances can be used simultaneously and are completely independent.
* Interaction with the core has no internal thread-safety. If `nsfplay_render` or other functions are called from different threads, you should use a mutex to prevent other interactions with that `NSFCore` instance from conflicting.
* The core makes no access to the file system. You load files and provide the data to the core.
* All strings are UTF-8 char.
* Can be used with no NSF file, and direct register writes can be used to manipulate the emulated sound devices directly. This may be suitable for use as an NES sound engine for a game.
* Allocations are kept to a minimum, and you can provide your own allocator instead of the default `malloc`/`free`.
* 1 allocation to create the core state structure.
* 1 small optional allocation for every string setting that is changed from the default.
* 1 optional allocation to make a copy of the NSF file, but you can instead tell the core to use the read only file contents you give it directly, assuming the pointer will be valid until it is unloaded.
* 1 allocation for internal rendering buffers, made when emulation begins, or can be manually triggered. These will not be reallocated unless a change in settings or selected audio expansions require a larger total allocation.
* Some features like the expansion audio, or the text data for user interfaces, can be defined out to reduce the library footprint.
* The interfaces are designed so that new settings and properties can be easily added in future versions easily through new enumerations, rather than requiring new interface members. Use the constants provided in `nsfplayenums.h`, and they should still work in future versions.

## Build

Building the GUI components `nsfplay`/`winamp` requires first setting up the wxWidgets libraries. If you only need to use the command-line `nsfplac`, then you can skip this step.
Expand All @@ -40,7 +60,7 @@ Building the GUI components `nsfplay`/`winamp` requires first setting up the wxW
* Run `wxlib.bat` to build the wxWidgets libraries.
* Use `nsfplay.sln` to build.

[Visual Studio 2019](https://visualstudio.microsoft.com/vs/older-downloads/) is the target build tool. Alternatively, [MSYS2](https://www.msys2.org/) can be used to
[Visual Studio 2019](https://visualstudio.microsoft.com/vs/older-downloads/) is the target build tool for Windows. Alternatively, [MSYS2](https://www.msys2.org/) can be used to
build with make instead. If you want to use Visual Studio 2022, see the note below about the wxWidgets Library.

### Make
Expand Down

0 comments on commit 32e2713

Please sign in to comment.