use libxmp instead

This commit is contained in:
Nishi 2026-05-27 23:51:37 +09:00
commit 6f8ee4c7ae
26 changed files with 333 additions and 4039 deletions

View file

@ -12,6 +12,7 @@ static void cleanup(int sig){
int main(){
fishbone::SoundDriver* drv;
fishbone::Sound* snd;
#ifndef _WIN32
signal(SIGINT, cleanup);
@ -20,7 +21,9 @@ int main(){
drv = new fishbone::SoundDriver();
drv->mixer.open("betrayal.mod");
snd = drv->mixer.open("dubmood.xm");
snd->setLoop(true);
snd->resume();
while(!quit);

View file

@ -49,7 +49,7 @@ fb_add_dep(fishbone stb_image)
# Sound
fb_add_dep(fishbone stb_vorbis)
fb_add_dep(fishbone dr_libs)
fb_add_dep(fishbone jar)
fb_add_dep(fishbone libxmp)
# Compression
fb_add_dep(fishbone lz4)

View file

@ -1,2 +0,0 @@
add_library(jar INTERFACE)
target_include_directories(jar INTERFACE jar)

View file

@ -0,0 +1,12 @@
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CPM.cmake")
CPMAddPackage(
NAME "libxmp"
GITHUB_REPOSITORY "libxmp/libxmp"
GIT_TAG master
OPTIONS
"BUILD_SHARED OFF"
"BUILD_LITE ON"
)
add_library(deps::libxmp ALIAS xmp_lite_static)

View file

@ -14,7 +14,9 @@ function(fb_add_dep TARGET_NAME DEP_NAME)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/deps/${DEP_NAME}.cmake")
add_library(deps::${DEP_NAME} ALIAS ${DEP_NAME})
if(NOT TARGET deps::${DEP_NAME})
add_library(deps::${DEP_NAME} ALIAS ${DEP_NAME})
endif()
target_link_libraries(${TARGET_NAME} ${LINK_SCOPE} deps::${DEP_NAME})
endfunction()

View file

@ -0,0 +1,27 @@
#ifndef __FISHBONE_FILE_H__
#define __FISHBONE_FILE_H__
namespace fishbone {
class File;
};
#include <fishbone/machdep.h>
namespace fishbone {
class File {
std::ifstream ifs;
std::ofstream ofs;
public:
File(std::string path, std::string type);
~File();
size_t read(void* buffer, size_t size);
size_t write(const void* buffer, size_t size);
size_t size;
bool good;
};
}; // namespace fishbone
#endif

View file

@ -5,5 +5,6 @@
#include <fishbone/sound.h>
#include <fishbone/mixer.h>
#include <fishbone/thread.h>
#include <fishbone/file.h>
#endif

View file

@ -2,6 +2,10 @@
#define __FISHBONE_MACHDEP_H__
#include <vector>
#include <string>
#include <fstream>
#include <cstdio>
#include <fishbone/config.h>
@ -11,8 +15,7 @@
#endif
#define STB_VORBIS_HEADER_ONLY
#include <jar_xm.h>
#include <jar_mod.h>
#include <xmp.h>
#include <dr_mp3.h>
#include <dr_wav.h>
#include <dr_flac.h>

View file

@ -19,6 +19,8 @@ namespace fishbone {
void read(short* buffer, size_t frames);
fishbone::Sound* open(const char* path);
void lock();
void unlock();
static const int rate = 44100;
std::vector<fishbone::SoundLoader*> loaders;

View file

@ -1,10 +1,35 @@
#ifndef __FISHBONE_SOUND_H__
#define __FISHBONE_SOUND_H__
#define FB_SOUND_DECL(prefix) \
class prefix##SoundLoader; \
class prefix##Sound
#define FB_SOUND_DEF(prefix) \
class prefix##SoundLoader : public SoundLoader { \
public: \
Sound* open(Mixer* mixer, void* buffer, size_t size); \
int read(Sound* sound, short* buffer, size_t frames); \
void seek(Sound* sound, size_t pos); \
}; \
\
class prefix##Sound : public Sound { \
public: \
prefix##Sound(Mixer* mixer, prefix##SoundLoader* loader, struct Sound::Impl* impl) \
: Sound(mixer, loader, impl){}; \
~prefix##Sound(); \
}
#define FB_SOUND_LOADERS LOADER(XMP)
namespace fishbone {
class SoundDriver;
class SoundLoader;
class Sound;
#define LOADER(X) FB_SOUND_DECL(X);
FB_SOUND_LOADERS
#undef LOADER
}; // namespace fishbone
#include <fishbone/machdep.h>
@ -27,21 +52,43 @@ namespace fishbone {
SoundLoader();
~SoundLoader();
virtual Sound* open(void* buffer, size_t size);
virtual Sound* open(Mixer* mixer, void* buffer, size_t size);
virtual int read(Sound* sound, short* buffer, size_t frames);
virtual void seek(Sound* sound, size_t pos);
};
class Sound {
public:
struct Impl;
Impl* impl;
private:
SoundLoader* loader;
Mixer* mixer;
protected:
bool loop;
bool paused;
public:
Sound(SoundLoader* loader);
Sound(Mixer* mixer, SoundLoader* loader, struct Impl* impl);
int read(short* buffer, size_t frames);
int read(short* buffer, size_t frames);
void seek(size_t pos);
void resume();
void pause();
void pauseNoLock(); /* do NOT use this! */
bool isPaused();
void setLoop(bool v);
bool isLoop();
int rate;
int channels;
};
#define LOADER(X) FB_SOUND_DEF(X);
FB_SOUND_LOADERS
#undef LOADER
}; // namespace fishbone
#endif

View file

@ -1,14 +0,0 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,2 +0,0 @@
#define JAR_MOD_IMPLEMENTATION
#include <jar_mod.h>

View file

@ -1,2 +0,0 @@
#define JAR_XM_IMPLEMENTATION
#include <jar_xm.h>

43
engine/src/file.cc Normal file
View file

@ -0,0 +1,43 @@
#include <fishbone/file.h>
fishbone::File::File(std::string path, std::string type) {
this->good = false;
this->size = 0;
if(type == "r") {
this->ifs = std::ifstream(path, std::ios::binary);
this->ifs.seekg(0, std::ios_base::end);
this->size = this->ifs.tellg();
this->ifs.seekg(0, std::ios_base::beg);
if(!this->ifs.good()) return;
} else if(type == "w") {
this->ofs = std::ofstream(path, std::ios::binary);
if(!this->ofs.good()) return;
} else {
return;
}
this->good = true;
}
fishbone::File::~File() {
}
size_t fishbone::File::read(void* buffer, size_t size) {
if(!this->ifs.good()) return -1;
if(!this->ifs.read(reinterpret_cast<char*>(buffer), size)) return -1;
return this->ifs.gcount();
}
size_t fishbone::File::write(const void* buffer, size_t size) {
if(!this->ofs.good()) return -1;
if(!this->ofs.write(reinterpret_cast<const char*>(buffer), size)) return -1;
return size;
}

View file

@ -2,6 +2,10 @@
fishbone::Mixer::Mixer() {
this->mutex = new fishbone::Mutex();
#define LOADER(X) this->loaders.push_back(new fishbone::X##SoundLoader());
FB_SOUND_LOADERS
#undef LOADER
}
fishbone::Mixer::~Mixer() {
@ -20,18 +24,86 @@ void fishbone::Mixer::read(short* buffer, size_t frames) {
for(int i = 0; i < frames * 2; i++) tmp[i] = 0;
this->mutex->lock();
this->mutex->unlock();
this->lock();
for(int i = 0; i < this->sounds.size(); i++) {
size_t ask;
short* req;
size_t got;
if(this->sounds[i]->isPaused()) continue;
ask = frames * this->sounds[i]->rate / fishbone::Mixer::rate;
req = new short[ask * this->sounds[i]->channels];
got = this->sounds[i]->read(req, ask) * fishbone::Mixer::rate / this->sounds[i]->rate;
if(got <= 0) {
delete[] req;
this->sounds[i]->pauseNoLock();
continue;
}
for(int j = 0; j < got; j++) {
short l, r;
if(this->sounds[i]->channels == 1) {
l = r = req[j * this->sounds[i]->rate / fishbone::Mixer::rate];
} else if(this->sounds[i]->channels == 2) {
l = req[(j * this->sounds[i]->rate / fishbone::Mixer::rate) * 2 + 0];
r = req[(j * this->sounds[i]->rate / fishbone::Mixer::rate) * 2 + 1];
}
tmp[2 * j + 0] += l / 32767.0;
tmp[2 * j + 1] += r / 32767.0;
}
delete[] req;
}
this->unlock();
for(int i = 0; i < frames * 2; i++) buffer[i] = tmp[i] * 32767;
delete tmp;
delete[] tmp;
}
fishbone::Sound* fishbone::Mixer::open(const char* path) {
fishbone::File f(path, "r");
unsigned char* buffer;
fishbone::Sound* snd;
if(!f.good) return nullptr;
buffer = new unsigned char[f.size];
f.read(buffer, f.size);
for(int i = 0; i < this->loaders.size(); i++) {
// this->loaders[i]->open();
snd = this->loaders[i]->open(this, buffer, f.size);
if(snd != nullptr) break;
}
return nullptr;
delete[] buffer;
if(snd != nullptr) {
if(snd->channels != 1 && snd->channels != 2) {
delete snd;
return nullptr;
}
this->lock();
this->sounds.push_back(snd);
this->unlock();
}
return snd;
}
void fishbone::Mixer::lock() {
this->mutex->lock();
}
void fishbone::Mixer::unlock() {
this->mutex->unlock();
}

View file

@ -6,7 +6,7 @@ fishbone::SoundLoader::SoundLoader() {
fishbone::SoundLoader::~SoundLoader() {
}
fishbone::Sound* fishbone::SoundLoader::open(void* buffer, size_t size) {
fishbone::Sound* fishbone::SoundLoader::open(fishbone::Mixer* mixer, void* buffer, size_t size) {
return nullptr;
}
@ -14,15 +14,63 @@ int fishbone::SoundLoader::read(fishbone::Sound* sound, short* buffer, size_t fr
return 0;
}
fishbone::Sound::Sound(SoundLoader* loader) {
void fishbone::SoundLoader::seek(fishbone::Sound* sound, size_t pos) {
}
fishbone::Sound::Sound(fishbone::Mixer* mixer, fishbone::SoundLoader* loader, struct fishbone::Sound::Impl* impl) {
this->mixer = mixer;
this->loader = loader;
this->impl = impl;
this->paused = true;
this->loop = false;
}
int fishbone::Sound::read(short* buffer, size_t frames) {
int seek = 0;
int l;
while((l = this->loader->read(this, buffer + seek * this->channels, frames - seek)) > 0) seek += l;
while(seek < frames && (l = this->loader->read(this, buffer + seek * this->channels, frames - seek)) > 0) seek += l;
return seek;
}
void fishbone::Sound::seek(size_t pos) {
this->loader->seek(this, pos);
}
void fishbone::Sound::resume() {
this->mixer->lock();
this->paused = false;
this->mixer->unlock();
}
void fishbone::Sound::pause() {
this->mixer->lock();
this->pauseNoLock();
this->mixer->unlock();
}
void fishbone::Sound::pauseNoLock() {
this->paused = true;
}
bool fishbone::Sound::isPaused() {
bool v;
this->mixer->lock();
v = this->paused;
this->mixer->unlock();
return v;
}
void fishbone::Sound::setLoop(bool v) {
this->mixer->lock();
this->loop = v;
this->mixer->unlock();
}
bool fishbone::Sound::isLoop() {
return this->loop;
}

View file

@ -2,7 +2,7 @@
#ifdef FISHBONE_SOUND_USE_SDL2
#include <fishbone/foundation.h>
static int sdl_audio_init = 0;
static bool sdl_audio_init = false;
static void SDLCALL sound_callback(void* userdata, Uint8* stream, int len) {
fishbone::SoundDriver* sd = (fishbone::SoundDriver*)userdata;
@ -21,7 +21,7 @@ fishbone::SoundDriver::SoundDriver() {
if(!sdl_audio_init) {
SDL_InitSubSystem(SDL_INIT_AUDIO);
sdl_audio_init = 1;
sdl_audio_init = true;
}
SDL_memset(&spec, 0, sizeof(spec));

View file

@ -0,0 +1 @@
#include <fishbone/foundation.h>

View file

@ -0,0 +1 @@
#include <fishbone/foundation.h>

View file

@ -0,0 +1 @@
#include <fishbone/foundation.h>

View file

@ -0,0 +1 @@
#include <fishbone/foundation.h>

View file

@ -0,0 +1,51 @@
#include <fishbone/foundation.h>
struct fishbone::Sound::Impl {
xmp_context ctx;
unsigned char* buffer;
};
fishbone::Sound* fishbone::XMPSoundLoader::open(fishbone::Mixer* mixer, void* buffer, size_t size) {
struct fishbone::Sound::Impl* impl;
fishbone::XMPSound* snd;
impl = new struct fishbone::Sound::Impl();
impl->buffer = new unsigned char[size];
memcpy(impl->buffer, buffer, size);
impl->ctx = xmp_create_context();
if(xmp_load_module_from_memory(impl->ctx, impl->buffer, size) < 0) {
xmp_free_context(impl->ctx);
delete[] impl->buffer;
delete impl;
return nullptr;
}
xmp_start_player(impl->ctx, 44100, 0);
snd = new fishbone::XMPSound(mixer, this, impl);
snd->rate = 44100;
snd->channels = 2;
return snd;
}
int fishbone::XMPSoundLoader::read(fishbone::Sound* sound, short* buffer, size_t frames) {
if(xmp_play_buffer(sound->impl->ctx, buffer, frames * 4, 0) < 0) return -1;
return frames;
}
void fishbone::XMPSoundLoader::seek(fishbone::Sound* sound, size_t pos) {
xmp_set_position(sound->impl->ctx, pos);
}
fishbone::XMPSound::~XMPSound() {
xmp_end_player(this->impl->ctx);
xmp_release_module(this->impl->ctx);
xmp_free_context(this->impl->ctx);
delete[] this->impl->buffer;
delete this->impl;
}