diff --git a/CMakeLists.txt b/CMakeLists.txt index b5f2bff..47bc589 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ target_sources(plugin PRIVATE ${PLUGIN_SOURCES} "${CMAKE_CURRENT_BINARY_DIR}/ver add_compile_options(plugin PRIVATE /W4 /WX) target_compile_definitions(plugin PRIVATE _UNION_API_DLL __G1 __G1A __G2 __G2A) target_include_directories(plugin PRIVATE "src/" "${CMAKE_BINARY_DIR}/src/") -target_link_libraries(plugin PRIVATE union-api gothic-api bass) +target_link_libraries(plugin PRIVATE union-api gothic-api bass_all) install(FILES $ "${CMAKE_BINARY_DIR}/zBassMusic.dll" TYPE BIN) install(FILES $ "${CMAKE_BINARY_DIR}/UnionAPI.dll" TYPE BIN) @@ -72,6 +72,9 @@ if (DEFINED ENV{COPY_DLL_TARGET}) COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/zBassMusic.dll" "$ENV{COPY_DLL_TARGET}/Autorun/zBassMusic.dll" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/UnionAPI.dll" "$ENV{COPY_DLL_TARGET}/Autorun/UnionAPI.dll" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dependencies/bass/lib/bass.dll" "$ENV{COPY_DLL_TARGET}/bass.dll" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dependencies/bass/lib/bassmidi.dll" "$ENV{COPY_DLL_TARGET}/bassmidi.dll" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dependencies/bass/lib/bassopus.dll" "$ENV{COPY_DLL_TARGET}/bassopus.dll" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_SOURCE_DIR}/dependencies/bass/lib/bassflac.dll" "$ENV{COPY_DLL_TARGET}/bassflac.dll" DEPENDS plugin COMMENT "Copy plugin to target directory: $ENV{COPY_DLL_TARGET}") endif () diff --git a/cmake/bass.cmake b/cmake/bass.cmake index 9e56a80..11e96ea 100644 --- a/cmake/bass.cmake +++ b/cmake/bass.cmake @@ -2,3 +2,21 @@ add_library(bass SHARED IMPORTED) set_property(TARGET bass PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BASS_DIR}/include") set_property(TARGET bass PROPERTY IMPORTED_LOCATION "${BASS_DIR}/lib/bass.dll") set_property(TARGET bass PROPERTY IMPORTED_IMPLIB "${BASS_DIR}/lib/bass.lib") + +add_library(bassmidi SHARED IMPORTED) +set_property(TARGET bassmidi PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BASS_DIR}/include") +set_property(TARGET bassmidi PROPERTY IMPORTED_LOCATION "${BASS_DIR}/lib/bassmidi.dll") +set_property(TARGET bassmidi PROPERTY IMPORTED_IMPLIB "${BASS_DIR}/lib/bassmidi.lib") + +add_library(bassopus SHARED IMPORTED) +set_property(TARGET bassopus PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BASS_DIR}/include") +set_property(TARGET bassopus PROPERTY IMPORTED_LOCATION "${BASS_DIR}/lib/bassopus.dll") +set_property(TARGET bassopus PROPERTY IMPORTED_IMPLIB "${BASS_DIR}/lib/bassopus.lib") + +add_library(bassflac SHARED IMPORTED) +set_property(TARGET bassflac PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${BASS_DIR}/include") +set_property(TARGET bassflac PROPERTY IMPORTED_LOCATION "${BASS_DIR}/lib/bassflac.dll") +set_property(TARGET bassflac PROPERTY IMPORTED_IMPLIB "${BASS_DIR}/lib/bassflac.lib") + +add_library(bass_all INTERFACE) +target_link_libraries(bass_all INTERFACE bass bassmidi bassopus bassflac) \ No newline at end of file diff --git a/dependencies/bass/include/bassflac.h b/dependencies/bass/include/bassflac.h new file mode 100644 index 0000000..28fd5a8 --- /dev/null +++ b/dependencies/bass/include/bassflac.h @@ -0,0 +1,98 @@ +/* + BASSFLAC 2.4 C/C++ header file + Copyright (c) 2004-2017 Un4seen Developments Ltd. + + See the BASSFLAC.CHM file for more detailed documentation +*/ + +#ifndef BASSFLAC_H +#define BASSFLAC_H + +#include "bass.h" + +#if BASSVERSION!=0x204 +#error conflicting BASS and BASSFLAC versions +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BASSFLACDEF +#define BASSFLACDEF(f) WINAPI f +#endif + +// BASS_CHANNELINFO type +#define BASS_CTYPE_STREAM_FLAC 0x10900 +#define BASS_CTYPE_STREAM_FLAC_OGG 0x10901 + +// Additional tag types +#define BASS_TAG_FLAC_CUE 12 // cuesheet : TAG_FLAC_CUE structure +#define BASS_TAG_FLAC_PICTURE 0x12000 // + index #, picture : TAG_FLAC_PICTURE structure +#define BASS_TAG_FLAC_METADATA 0x12400 // + index #, application metadata : TAG_FLAC_METADATA structure + +typedef struct { + DWORD apic; // ID3v2 "APIC" picture type + const char *mime; // mime type + const char *desc; // description + DWORD width; + DWORD height; + DWORD depth; + DWORD colors; + DWORD length; // data length + const void *data; +} TAG_FLAC_PICTURE; + +typedef struct { + QWORD offset; // index offset relative to track offset (samples) + DWORD number; // index number +} TAG_FLAC_CUE_TRACK_INDEX; + +typedef struct { + QWORD offset; // track offset (samples) + DWORD number; // track number + const char *isrc; // ISRC + DWORD flags; + DWORD nindexes; // number of indexes + const TAG_FLAC_CUE_TRACK_INDEX *indexes; // the indexes +} TAG_FLAC_CUE_TRACK; + +typedef struct { + const char *catalog; // media catalog number + DWORD leadin; // lead-in (samples) + BOOL iscd; // a CD? + DWORD ntracks; // number of tracks + const TAG_FLAC_CUE_TRACK *tracks; // the tracks +} TAG_FLAC_CUE; + +// TAG_FLAC_CUE_TRACK flags +#define TAG_FLAC_CUE_TRACK_DATA 1 // data track +#define TAG_FLAC_CUE_TRACK_PRE 2 // pre-emphasis + +typedef struct { + char id[4]; + DWORD length; // data length + const void *data; +} TAG_FLAC_METADATA; + +HSTREAM BASSFLACDEF(BASS_FLAC_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags); +HSTREAM BASSFLACDEF(BASS_FLAC_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user); +HSTREAM BASSFLACDEF(BASS_FLAC_StreamCreateFileUser)(DWORD system, DWORD flags, const BASS_FILEPROCS *procs, void *user); + +#ifdef __cplusplus +} + +#ifdef _WIN32 +static inline HSTREAM BASS_FLAC_StreamCreateFile(BOOL mem, const WCHAR *file, QWORD offset, QWORD length, DWORD flags) +{ + return BASS_FLAC_StreamCreateFile(mem, (const void*)file, offset, length, flags|BASS_UNICODE); +} + +static inline HSTREAM BASS_FLAC_StreamCreateURL(const WCHAR *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user) +{ + return BASS_FLAC_StreamCreateURL((const char*)url, offset, flags|BASS_UNICODE, proc, user); +} +#endif +#endif + +#endif diff --git a/dependencies/bass/include/bassmidi.h b/dependencies/bass/include/bassmidi.h new file mode 100644 index 0000000..0c8b519 --- /dev/null +++ b/dependencies/bass/include/bassmidi.h @@ -0,0 +1,424 @@ +/* + BASSMIDI 2.4 C/C++ header file + Copyright (c) 2006-2022 Un4seen Developments Ltd. + + See the BASSMIDI.CHM file for more detailed documentation +*/ + +#ifndef BASSMIDI_H +#define BASSMIDI_H + +#include "bass.h" + +#if BASSVERSION!=0x204 +#error conflicting BASS and BASSMIDI versions +#endif + +#ifdef __OBJC__ +typedef int BOOL32; +#define BOOL BOOL32 // override objc's BOOL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BASSMIDIDEF +#define BASSMIDIDEF(f) WINAPI f +#endif + +typedef DWORD HSOUNDFONT; // soundfont handle + +// Additional error codes returned by BASS_ErrorGetCode +#define BASS_ERROR_MIDI_INCLUDE 7000 // SFZ include file could not be opened + +// Additional BASS_SetConfig options +#define BASS_CONFIG_MIDI_COMPACT 0x10400 +#define BASS_CONFIG_MIDI_VOICES 0x10401 +#define BASS_CONFIG_MIDI_AUTOFONT 0x10402 +#define BASS_CONFIG_MIDI_IN_PORTS 0x10404 +#define BASS_CONFIG_MIDI_SAMPLETHREADS 0x10406 +#define BASS_CONFIG_MIDI_SAMPLEMEM 0x10407 +#define BASS_CONFIG_MIDI_SAMPLEREAD 0x10408 +#define BASS_CONFIG_MIDI_SAMPLELOADING 0x1040a + +// Additional BASS_SetConfigPtr options +#define BASS_CONFIG_MIDI_DEFFONT 0x10403 +#define BASS_CONFIG_MIDI_SFZHEAD 0x10409 + +// Additional sync types +#define BASS_SYNC_MIDI_MARK 0x10000 +#define BASS_SYNC_MIDI_MARKER 0x10000 +#define BASS_SYNC_MIDI_CUE 0x10001 +#define BASS_SYNC_MIDI_LYRIC 0x10002 +#define BASS_SYNC_MIDI_TEXT 0x10003 +#define BASS_SYNC_MIDI_EVENT 0x10004 +#define BASS_SYNC_MIDI_TICK 0x10005 +#define BASS_SYNC_MIDI_TIMESIG 0x10006 +#define BASS_SYNC_MIDI_KEYSIG 0x10007 + +// Additional BASS_MIDI_StreamCreateFile/etc flags +#define BASS_MIDI_NODRUMPARAM 0x400 +#define BASS_MIDI_NOSYSRESET 0x800 +#define BASS_MIDI_DECAYEND 0x1000 +#define BASS_MIDI_NOFX 0x2000 +#define BASS_MIDI_DECAYSEEK 0x4000 +#define BASS_MIDI_NOCROP 0x8000 +#define BASS_MIDI_NOTEOFF1 0x10000 +#define BASS_MIDI_ASYNC 0x400000 +#define BASS_MIDI_SINCINTER 0x800000 + +// BASS_MIDI_FontInit flags +#define BASS_MIDI_FONT_MEM 0x10000 +#define BASS_MIDI_FONT_MMAP 0x20000 +#define BASS_MIDI_FONT_XGDRUMS 0x40000 +#define BASS_MIDI_FONT_NOFX 0x80000 +#define BASS_MIDI_FONT_LINATTMOD 0x100000 +#define BASS_MIDI_FONT_LINDECVOL 0x200000 +#define BASS_MIDI_FONT_NORAMPIN 0x400000 +#define BASS_MIDI_FONT_NOLIMITS 0x800000 +#define BASS_MIDI_FONT_MINFX 0x1000000 + +typedef struct { + HSOUNDFONT font; // soundfont + int preset; // preset number (-1=all) + int bank; +} BASS_MIDI_FONT; + +typedef struct { + HSOUNDFONT font; // soundfont + int spreset; // source preset number + int sbank; // source bank number + int dpreset; // destination preset/program number + int dbank; // destination bank number + int dbanklsb; // destination bank number LSB +} BASS_MIDI_FONTEX; + +typedef struct { + HSOUNDFONT font; // soundfont + int spreset; // source preset number + int sbank; // source bank number + int dpreset; // destination preset/program number + int dbank; // destination bank number + int dbanklsb; // destination bank number LSB + DWORD minchan; // minimum channel number + DWORD numchan; // number of channels from minchan +} BASS_MIDI_FONTEX2; + +// BASS_MIDI_StreamSet/GetFonts flag +#define BASS_MIDI_FONT_EX 0x1000000 // BASS_MIDI_FONTEX +#define BASS_MIDI_FONT_EX2 0x2000000 // BASS_MIDI_FONTEX2 + +typedef struct { + const char *name; + const char *copyright; + const char *comment; + DWORD presets; // number of presets/instruments + DWORD samsize; // total size (in bytes) of the sample data + DWORD samload; // amount of sample data currently loaded + DWORD samtype; // sample format (CTYPE) if packed +} BASS_MIDI_FONTINFO; + +typedef struct { + DWORD track; // track containing marker + DWORD pos; // marker position + const char *text; // marker text +} BASS_MIDI_MARK; + +// Marker types +#define BASS_MIDI_MARK_MARKER 0 // marker +#define BASS_MIDI_MARK_CUE 1 // cue point +#define BASS_MIDI_MARK_LYRIC 2 // lyric +#define BASS_MIDI_MARK_TEXT 3 // text +#define BASS_MIDI_MARK_TIMESIG 4 // time signature +#define BASS_MIDI_MARK_KEYSIG 5 // key signature +#define BASS_MIDI_MARK_COPY 6 // copyright notice +#define BASS_MIDI_MARK_TRACK 7 // track name +#define BASS_MIDI_MARK_INST 8 // instrument name +#define BASS_MIDI_MARK_TRACKSTART 9 // track start (SMF2) +#define BASS_MIDI_MARK_TICK 0x10000 // flag: get position in ticks (otherwise bytes) + +// MIDI events +#define MIDI_EVENT_NOTE 1 +#define MIDI_EVENT_PROGRAM 2 +#define MIDI_EVENT_CHANPRES 3 +#define MIDI_EVENT_PITCH 4 +#define MIDI_EVENT_PITCHRANGE 5 +#define MIDI_EVENT_DRUMS 6 +#define MIDI_EVENT_FINETUNE 7 +#define MIDI_EVENT_COARSETUNE 8 +#define MIDI_EVENT_MASTERVOL 9 +#define MIDI_EVENT_BANK 10 +#define MIDI_EVENT_MODULATION 11 +#define MIDI_EVENT_VOLUME 12 +#define MIDI_EVENT_PAN 13 +#define MIDI_EVENT_EXPRESSION 14 +#define MIDI_EVENT_SUSTAIN 15 +#define MIDI_EVENT_SOUNDOFF 16 +#define MIDI_EVENT_RESET 17 +#define MIDI_EVENT_NOTESOFF 18 +#define MIDI_EVENT_PORTAMENTO 19 +#define MIDI_EVENT_PORTATIME 20 +#define MIDI_EVENT_PORTANOTE 21 +#define MIDI_EVENT_MODE 22 +#define MIDI_EVENT_REVERB 23 +#define MIDI_EVENT_CHORUS 24 +#define MIDI_EVENT_CUTOFF 25 +#define MIDI_EVENT_RESONANCE 26 +#define MIDI_EVENT_RELEASE 27 +#define MIDI_EVENT_ATTACK 28 +#define MIDI_EVENT_DECAY 29 +#define MIDI_EVENT_REVERB_MACRO 30 +#define MIDI_EVENT_CHORUS_MACRO 31 +#define MIDI_EVENT_REVERB_TIME 32 +#define MIDI_EVENT_REVERB_DELAY 33 +#define MIDI_EVENT_REVERB_LOCUTOFF 34 +#define MIDI_EVENT_REVERB_HICUTOFF 35 +#define MIDI_EVENT_REVERB_LEVEL 36 +#define MIDI_EVENT_CHORUS_DELAY 37 +#define MIDI_EVENT_CHORUS_DEPTH 38 +#define MIDI_EVENT_CHORUS_RATE 39 +#define MIDI_EVENT_CHORUS_FEEDBACK 40 +#define MIDI_EVENT_CHORUS_LEVEL 41 +#define MIDI_EVENT_CHORUS_REVERB 42 +#define MIDI_EVENT_USERFX 43 +#define MIDI_EVENT_USERFX_LEVEL 44 +#define MIDI_EVENT_USERFX_REVERB 45 +#define MIDI_EVENT_USERFX_CHORUS 46 +#define MIDI_EVENT_DRUM_FINETUNE 50 +#define MIDI_EVENT_DRUM_COARSETUNE 51 +#define MIDI_EVENT_DRUM_PAN 52 +#define MIDI_EVENT_DRUM_REVERB 53 +#define MIDI_EVENT_DRUM_CHORUS 54 +#define MIDI_EVENT_DRUM_CUTOFF 55 +#define MIDI_EVENT_DRUM_RESONANCE 56 +#define MIDI_EVENT_DRUM_LEVEL 57 +#define MIDI_EVENT_DRUM_USERFX 58 +#define MIDI_EVENT_SOFT 60 +#define MIDI_EVENT_SYSTEM 61 +#define MIDI_EVENT_TEMPO 62 +#define MIDI_EVENT_SCALETUNING 63 +#define MIDI_EVENT_CONTROL 64 +#define MIDI_EVENT_CHANPRES_VIBRATO 65 +#define MIDI_EVENT_CHANPRES_PITCH 66 +#define MIDI_EVENT_CHANPRES_FILTER 67 +#define MIDI_EVENT_CHANPRES_VOLUME 68 +#define MIDI_EVENT_MOD_VIBRATO 69 +#define MIDI_EVENT_MODRANGE 69 +#define MIDI_EVENT_BANK_LSB 70 +#define MIDI_EVENT_KEYPRES 71 +#define MIDI_EVENT_KEYPRES_VIBRATO 72 +#define MIDI_EVENT_KEYPRES_PITCH 73 +#define MIDI_EVENT_KEYPRES_FILTER 74 +#define MIDI_EVENT_KEYPRES_VOLUME 75 +#define MIDI_EVENT_SOSTENUTO 76 +#define MIDI_EVENT_MOD_PITCH 77 +#define MIDI_EVENT_MOD_FILTER 78 +#define MIDI_EVENT_MOD_VOLUME 79 +#define MIDI_EVENT_VIBRATO_RATE 80 +#define MIDI_EVENT_VIBRATO_DEPTH 81 +#define MIDI_EVENT_VIBRATO_DELAY 82 +#define MIDI_EVENT_MASTER_FINETUNE 83 +#define MIDI_EVENT_MASTER_COARSETUNE 84 +#define MIDI_EVENT_MIXLEVEL 0x10000 +#define MIDI_EVENT_TRANSPOSE 0x10001 +#define MIDI_EVENT_SYSTEMEX 0x10002 +#define MIDI_EVENT_SPEED 0x10004 +#define MIDI_EVENT_DEFDRUMS 0x10006 + +#define MIDI_EVENT_END 0 +#define MIDI_EVENT_END_TRACK 0x10003 + +#define MIDI_EVENT_NOTES 0x20000 +#define MIDI_EVENT_VOICES 0x20001 + +#define MIDI_SYSTEM_DEFAULT 0 +#define MIDI_SYSTEM_GM1 1 +#define MIDI_SYSTEM_GM2 2 +#define MIDI_SYSTEM_XG 3 +#define MIDI_SYSTEM_GS 4 + +typedef struct { + DWORD event; // MIDI_EVENT_xxx + DWORD param; + DWORD chan; + DWORD tick; // event position (ticks) + DWORD pos; // event position (bytes) +} BASS_MIDI_EVENT; + +// BASS_MIDI_StreamEvents modes +#define BASS_MIDI_EVENTS_STRUCT 0 // BASS_MIDI_EVENT structures +#define BASS_MIDI_EVENTS_RAW 0x10000 // raw MIDI event data +#define BASS_MIDI_EVENTS_SYNC 0x1000000 // flag: trigger event syncs +#define BASS_MIDI_EVENTS_NORSTATUS 0x2000000 // flag: no running status +#define BASS_MIDI_EVENTS_CANCEL 0x4000000 // flag: cancel pending events +#define BASS_MIDI_EVENTS_TIME 0x8000000 // flag: delta-time info is present +#define BASS_MIDI_EVENTS_ABSTIME 0x10000000 // flag: absolute time info is present +#define BASS_MIDI_EVENTS_ASYNC 0x20000000 // flag: process asynchronously +#define BASS_MIDI_EVENTS_FILTER 0x40000000 // flag: apply filtering +#define BASS_MIDI_EVENTS_FLUSH 0x80000000 // flag: flush async events + +// BASS_MIDI_StreamGetChannel special channels +#define BASS_MIDI_CHAN_CHORUS (DWORD)-1 +#define BASS_MIDI_CHAN_REVERB (DWORD)-2 +#define BASS_MIDI_CHAN_USERFX (DWORD)-3 + +// BASS_CHANNELINFO type +#define BASS_CTYPE_STREAM_MIDI 0x10d00 + +// Additional attributes +#define BASS_ATTRIB_MIDI_PPQN 0x12000 +#define BASS_ATTRIB_MIDI_CPU 0x12001 +#define BASS_ATTRIB_MIDI_CHANS 0x12002 +#define BASS_ATTRIB_MIDI_VOICES 0x12003 +#define BASS_ATTRIB_MIDI_VOICES_ACTIVE 0x12004 +#define BASS_ATTRIB_MIDI_STATE 0x12005 +#define BASS_ATTRIB_MIDI_SRC 0x12006 +#define BASS_ATTRIB_MIDI_KILL 0x12007 +#define BASS_ATTRIB_MIDI_SPEED 0x12008 +#define BASS_ATTRIB_MIDI_REVERB 0x12009 +#define BASS_ATTRIB_MIDI_VOL 0x1200a +#define BASS_ATTRIB_MIDI_TRACK_VOL 0x12100 // + track # + +// Additional tag type +#define BASS_TAG_MIDI_TRACK 0x11000 // + track #, track text : array of null-terminated ANSI strings + +// BASS_ChannelGetLength/GetPosition/SetPosition mode +#define BASS_POS_MIDI_TICK 2 // tick position + +typedef BOOL (CALLBACK MIDIFILTERPROC)(HSTREAM handle, int track, BASS_MIDI_EVENT *event, BOOL seeking, void *user); +/* Event filtering callback function. +handle : MIDI stream handle +track : Track containing the event +event : The event +seeking: TRUE = the event is being processed while seeking, FALSE = it is being played +user : The 'user' parameter value given when calling BASS_MIDI_StreamSetFilter +RETURN : TRUE = process the event, FALSE = drop the event */ + +// BASS_MIDI_FontLoadEx flags +#define BASS_MIDI_FONTLOAD_NOWAIT 1 // don't want for the samples to load +#define BASS_MIDI_FONTLOAD_COMPACT 2 // compact samples +#define BASS_MIDI_FONTLOAD_NOLOAD 4 // don't load (only compact) +#define BASS_MIDI_FONTLOAD_TIME 8 // length is in milliseconds +#define BASS_MIDI_FONTLOAD_KEEPDEC 16 // keep decoders + +// BASS_MIDI_FontPack flags +#define BASS_MIDI_PACK_NOHEAD 1 // don't send a WAV header to the encoder +#define BASS_MIDI_PACK_16BIT 2 // discard low 8 bits of 24-bit sample data +#define BASS_MIDI_PACK_48KHZ 4 // set encoding rate to 48000 Hz (else 44100 Hz) + +typedef struct { + const char *name; // description + DWORD id; + DWORD flags; +} BASS_MIDI_DEVICEINFO; + +typedef void (CALLBACK MIDIINPROC)(DWORD device, double time, const BYTE *buffer, DWORD length, void *user); +/* MIDI input callback function. +device : MIDI input device +time : Timestamp +buffer : Buffer containing MIDI data +length : Number of bytes of data +user : The 'user' parameter value given when calling BASS_MIDI_InInit */ + +DWORD BASSMIDIDEF(BASS_MIDI_GetVersion)(void); + +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreate)(DWORD channels, DWORD flags, DWORD freq); +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags, DWORD freq); +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user, DWORD freq); +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateFileUser)(DWORD system, DWORD flags, const BASS_FILEPROCS *procs, void *user, DWORD freq); +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamCreateEvents)(const BASS_MIDI_EVENT *events, DWORD ppqn, DWORD flags, DWORD freq); +BOOL BASSMIDIDEF(BASS_MIDI_StreamGetMark)(HSTREAM handle, DWORD type, DWORD index, BASS_MIDI_MARK *mark); +DWORD BASSMIDIDEF(BASS_MIDI_StreamGetMarks)(HSTREAM handle, int track, DWORD type, BASS_MIDI_MARK *marks); +BOOL BASSMIDIDEF(BASS_MIDI_StreamSetFonts)(HSTREAM handle, const void *fonts, DWORD count); +DWORD BASSMIDIDEF(BASS_MIDI_StreamGetFonts)(HSTREAM handle, void *fonts, DWORD count); +BOOL BASSMIDIDEF(BASS_MIDI_StreamLoadSamples)(HSTREAM handle); +BOOL BASSMIDIDEF(BASS_MIDI_StreamEvent)(HSTREAM handle, DWORD chan, DWORD event, DWORD param); +DWORD BASSMIDIDEF(BASS_MIDI_StreamEvents)(HSTREAM handle, DWORD mode, const void *events, DWORD length); +DWORD BASSMIDIDEF(BASS_MIDI_StreamGetEvent)(HSTREAM handle, DWORD chan, DWORD event); +DWORD BASSMIDIDEF(BASS_MIDI_StreamGetEvents)(HSTREAM handle, int track, DWORD filter, BASS_MIDI_EVENT *events); +DWORD BASSMIDIDEF(BASS_MIDI_StreamGetEventsEx)(HSTREAM handle, int track, DWORD filter, BASS_MIDI_EVENT *events, DWORD start, DWORD count); +BOOL BASSMIDIDEF(BASS_MIDI_StreamGetPreset)(HSTREAM handle, DWORD chan, BASS_MIDI_FONT *font); +HSTREAM BASSMIDIDEF(BASS_MIDI_StreamGetChannel)(HSTREAM handle, DWORD chan); +BOOL BASSMIDIDEF(BASS_MIDI_StreamSetFilter)(HSTREAM handle, BOOL seeking, MIDIFILTERPROC *proc, void *user); + +HSOUNDFONT BASSMIDIDEF(BASS_MIDI_FontInit)(const void *file, DWORD flags); +HSOUNDFONT BASSMIDIDEF(BASS_MIDI_FontInitUser)(const BASS_FILEPROCS *procs, void *user, DWORD flags); +BOOL BASSMIDIDEF(BASS_MIDI_FontFree)(HSOUNDFONT handle); +BOOL BASSMIDIDEF(BASS_MIDI_FontGetInfo)(HSOUNDFONT handle, BASS_MIDI_FONTINFO *info); +BOOL BASSMIDIDEF(BASS_MIDI_FontGetPresets)(HSOUNDFONT handle, DWORD *presets); +const char *BASSMIDIDEF(BASS_MIDI_FontGetPreset)(HSOUNDFONT handle, int preset, int bank); +BOOL BASSMIDIDEF(BASS_MIDI_FontLoad)(HSOUNDFONT handle, int preset, int bank); +BOOL BASSMIDIDEF(BASS_MIDI_FontLoadEx)(HSOUNDFONT handle, int preset, int bank, DWORD length, DWORD flags); +BOOL BASSMIDIDEF(BASS_MIDI_FontUnload)(HSOUNDFONT handle, int preset, int bank); +BOOL BASSMIDIDEF(BASS_MIDI_FontCompact)(HSOUNDFONT handle); +BOOL BASSMIDIDEF(BASS_MIDI_FontPack)(HSOUNDFONT handle, const void *outfile, const void *encoder, DWORD flags); +BOOL BASSMIDIDEF(BASS_MIDI_FontUnpack)(HSOUNDFONT handle, const void *outfile, DWORD flags); +DWORD BASSMIDIDEF(BASS_MIDI_FontFlags)(HSOUNDFONT handle, DWORD flags, DWORD mask); +BOOL BASSMIDIDEF(BASS_MIDI_FontSetVolume)(HSOUNDFONT handle, float volume); +float BASSMIDIDEF(BASS_MIDI_FontGetVolume)(HSOUNDFONT handle); + +DWORD BASSMIDIDEF(BASS_MIDI_ConvertEvents)(const BYTE *data, DWORD length, BASS_MIDI_EVENT *events, DWORD count, DWORD flags); + +BOOL BASSMIDIDEF(BASS_MIDI_InGetDeviceInfo)(DWORD device, BASS_MIDI_DEVICEINFO *info); +BOOL BASSMIDIDEF(BASS_MIDI_InInit)(DWORD device, MIDIINPROC *proc, void *user); +BOOL BASSMIDIDEF(BASS_MIDI_InFree)(DWORD device); +BOOL BASSMIDIDEF(BASS_MIDI_InStart)(DWORD device); +BOOL BASSMIDIDEF(BASS_MIDI_InStop)(DWORD device); + +#ifdef __cplusplus +} + +static inline BOOL BASS_MIDI_StreamSetFonts(HSTREAM handle, const BASS_MIDI_FONTEX *fonts, DWORD count) +{ + return BASS_MIDI_StreamSetFonts(handle, (const void*)fonts, count | BASS_MIDI_FONT_EX); +} + +static inline BOOL BASS_MIDI_StreamSetFonts(HSTREAM handle, const BASS_MIDI_FONTEX2 *fonts, DWORD count) +{ + return BASS_MIDI_StreamSetFonts(handle, (const void*)fonts, count | BASS_MIDI_FONT_EX2); +} + +static inline DWORD BASS_MIDI_StreamGetFonts(HSTREAM handle, BASS_MIDI_FONTEX *fonts, DWORD count) +{ + return BASS_MIDI_StreamGetFonts(handle, (void*)fonts, count | BASS_MIDI_FONT_EX); +} + +static inline DWORD BASS_MIDI_StreamGetFonts(HSTREAM handle, BASS_MIDI_FONTEX2 *fonts, DWORD count) +{ + return BASS_MIDI_StreamGetFonts(handle, (void*)fonts, count | BASS_MIDI_FONT_EX2); +} + +#ifdef _WIN32 +static inline HSTREAM BASS_MIDI_StreamCreateFile(BOOL mem, const WCHAR *file, QWORD offset, QWORD length, DWORD flags, DWORD freq) +{ + return BASS_MIDI_StreamCreateFile(mem, (const void*)file, offset, length, flags | BASS_UNICODE, freq); +} + +static inline HSTREAM BASS_MIDI_StreamCreateURL(const WCHAR *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user, DWORD freq) +{ + return BASS_MIDI_StreamCreateURL((const char*)url, offset, flags | BASS_UNICODE, proc, user, freq); +} + +static inline HSOUNDFONT BASS_MIDI_FontInit(const WCHAR *file, DWORD flags) +{ + return BASS_MIDI_FontInit((const void*)file, flags|BASS_UNICODE); +} + +static inline BOOL BASS_MIDI_FontPack(HSOUNDFONT handle, const WCHAR *outfile, const WCHAR *encoder, DWORD flags) +{ + return BASS_MIDI_FontPack(handle, (const void*)outfile, (const void*)encoder, flags | BASS_UNICODE); +} + +static inline BOOL BASS_MIDI_FontUnpack(HSOUNDFONT handle, const WCHAR *outfile, DWORD flags) +{ + return BASS_MIDI_FontUnpack(handle, (const void*)outfile, flags | BASS_UNICODE); +} +#endif +#endif + +#ifdef __OBJC__ +#undef BOOL +#endif + +#endif diff --git a/dependencies/bass/include/bassopus.h b/dependencies/bass/include/bassopus.h new file mode 100644 index 0000000..7a535ae --- /dev/null +++ b/dependencies/bass/include/bassopus.h @@ -0,0 +1,61 @@ +/* + BASSOPUS 2.4 C/C++ header file + Copyright (c) 2012-2015 Un4seen Developments Ltd. + + See the BASSOPUS.CHM file for more detailed documentation +*/ + +#ifndef BASSOPUS_H +#define BASSOPUS_H + +#include "bass.h" + +#if BASSVERSION!=0x204 +#error conflicting BASS and BASSOPUS versions +#endif + +#ifdef __OBJC__ +typedef int BOOL32; +#define BOOL BOOL32 // override objc's BOOL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BASSOPUSDEF +#define BASSOPUSDEF(f) WINAPI f +#endif + +// BASS_CHANNELINFO type +#define BASS_CTYPE_STREAM_OPUS 0x11200 + +// Additional attributes +#define BASS_ATTRIB_OPUS_ORIGFREQ 0x13000 +#define BASS_ATTRIB_OPUS_GAIN 0x13001 + +HSTREAM BASSOPUSDEF(BASS_OPUS_StreamCreateFile)(BOOL mem, const void *file, QWORD offset, QWORD length, DWORD flags); +HSTREAM BASSOPUSDEF(BASS_OPUS_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user); +HSTREAM BASSOPUSDEF(BASS_OPUS_StreamCreateFileUser)(DWORD system, DWORD flags, const BASS_FILEPROCS *procs, void *user); + +#ifdef __cplusplus +} + +#if defined(_WIN32) && !defined(NOBASSOVERLOADS) +static inline HSTREAM BASS_OPUS_StreamCreateFile(BOOL mem, const WCHAR *file, QWORD offset, QWORD length, DWORD flags) +{ + return BASS_OPUS_StreamCreateFile(mem, (const void*)file, offset, length, flags|BASS_UNICODE); +} + +static inline HSTREAM BASS_OPUS_StreamCreateURL(const WCHAR *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user) +{ + return BASS_OPUS_StreamCreateURL((const char*)url, offset, flags|BASS_UNICODE, proc, user); +} +#endif +#endif + +#ifdef __OBJC__ +#undef BOOL +#endif + +#endif diff --git a/dependencies/bass/lib/bassflac.dll b/dependencies/bass/lib/bassflac.dll new file mode 100644 index 0000000..d0684c0 Binary files /dev/null and b/dependencies/bass/lib/bassflac.dll differ diff --git a/dependencies/bass/lib/bassflac.lib b/dependencies/bass/lib/bassflac.lib new file mode 100644 index 0000000..e26020e Binary files /dev/null and b/dependencies/bass/lib/bassflac.lib differ diff --git a/dependencies/bass/lib/bassmidi.dll b/dependencies/bass/lib/bassmidi.dll new file mode 100644 index 0000000..2722419 Binary files /dev/null and b/dependencies/bass/lib/bassmidi.dll differ diff --git a/dependencies/bass/lib/bassmidi.lib b/dependencies/bass/lib/bassmidi.lib new file mode 100644 index 0000000..9e4947c Binary files /dev/null and b/dependencies/bass/lib/bassmidi.lib differ diff --git a/dependencies/bass/lib/bassopus.dll b/dependencies/bass/lib/bassopus.dll new file mode 100644 index 0000000..8059f03 Binary files /dev/null and b/dependencies/bass/lib/bassopus.dll differ diff --git a/dependencies/bass/lib/bassopus.lib b/dependencies/bass/lib/bassopus.lib new file mode 100644 index 0000000..5ba778d Binary files /dev/null and b/dependencies/bass/lib/bassopus.lib differ diff --git a/src/Gothic/BassLoader.hpp b/src/Gothic/BassLoader.hpp index 125250d..841583d 100644 --- a/src/Gothic/BassLoader.hpp +++ b/src/Gothic/BassLoader.hpp @@ -1,4 +1,5 @@ #include +#include namespace GOTHIC_NAMESPACE { @@ -23,6 +24,7 @@ namespace GOTHIC_NAMESPACE { zSTRING Type; zSTRING Filename; + zSTRING MidiFile; }; class BassLoader diff --git a/src/NH/Bass/Engine.cpp b/src/NH/Bass/Engine.cpp index a2749b5..b6dfde2 100644 --- a/src/NH/Bass/Engine.cpp +++ b/src/NH/Bass/Engine.cpp @@ -1,5 +1,10 @@ #include "Options.h" #include "Engine.h" + +#include +#include +#include + #include namespace NH::Bass @@ -100,6 +105,21 @@ namespace NH::Bass Engine::Engine() { + HPLUGIN midiPlugin = BASS_PluginLoad("bassmidi.dll", 0); + if (midiPlugin) { BASS_PluginEnable(midiPlugin, true); } + else { + log->Warning("Could not load BASSMIDI plugin. Make sure that bassmidi.dll is in the working directory or Autorun."); + log->Warning("BASSMIDI plugin is required for MIDI metadata. Engine may crash without it."); + } + + HPLUGIN opusPlugin = BASS_PluginLoad("bassopus.dll", 0); + if (opusPlugin) { BASS_PluginEnable(opusPlugin, true); } + else { log->Warning("Could not load BASSOPUS plugin (bassopus.dll). Opus files won't play."); } + + HPLUGIN flacPlugin = BASS_PluginLoad("bassflac.dll", 0); + if (flacPlugin) { BASS_PluginEnable(flacPlugin, true); } + else { log->Warning("Could not load BASSFLAC plugin (bassflac.dll). FLAC files won't play."); } + size_t deviceIndex = 0; BASS_DEVICEINFO deviceInfo; for (size_t i = 1; BASS_GetDeviceInfo(i, &deviceInfo); i++) diff --git a/src/NH/Bass/MidiFile.cpp b/src/NH/Bass/MidiFile.cpp new file mode 100644 index 0000000..18a6656 --- /dev/null +++ b/src/NH/Bass/MidiFile.cpp @@ -0,0 +1,101 @@ +#include "MidiFile.h" + +#include +#include +#include + +namespace NH::Bass +{ + Logger* MidiFile::log = CreateLogger("zBassMusic::MidiFile"); + + void MidiFile::LoadMidiFile(Executor& executor) + { + if (m_LoadingStatus != LoadingStatusType::NOT_LOADED) + { + log->Warning("MidiFile::LoadMidiFile() called on a MidiFile that is not in NOT_LOADED state."); + return; + } + m_LoadingStatus = LoadingStatusType::LOADING; + executor.AddTask([this]() + { + int systems = VDF_VIRTUAL | VDF_PHYSICAL; + const Union::VDFS::File* file = Union::VDFS::GetDefaultInstance().GetFile(m_Filename, systems); + if (!file) + { + m_LoadingStatus = LoadingStatusType::FAILED; + m_Error = "File not found"; + return; + } + + Union::Stream* stream = file->Open(); + m_Buffer.resize(stream->GetSize()); + stream->Read(m_Buffer.data(), m_Buffer.size()); + stream->Close(); + m_LoadingStatus = LoadingStatusType::READY; + ParseMidiFile(); + }); + } + + void MidiFile::ParseMidiFile() + { + m_Status = StatusType::PARSING; + m_Tones.clear(); + m_MidiEvents.clear(); + HSTREAM stream = BASS_MIDI_StreamCreateFile(true, m_Buffer.data(), 0, m_Buffer.size(), BASS_SAMPLE_FLOAT, 0); + uint32_t count = BASS_MIDI_StreamGetEvents(stream, -1, 0, nullptr); + m_MidiEvents.resize(count); + BASS_MIDI_StreamGetEvents(stream, -1, 0, m_MidiEvents.data()); + + uint32_t tempo = 0; + float ppqn = 0.0f; // Ticks per quarter note + BASS_ChannelGetAttribute(stream, BASS_ATTRIB_MIDI_PPQN, &ppqn); + std::unordered_map tonesInProgress; + double ticksToSeconds = 0.0; + + for (const auto& event: m_MidiEvents) + { + switch (event.event) + { + case MIDI_EVENT_TEMPO: + tempo = event.param; + ticksToSeconds = (double) tempo / (double) ppqn / 1000000.0; + log->Trace("TempoEvent: tempo={0}, ticksToSeconds={1}", tempo, ticksToSeconds); + break; + case MIDI_EVENT_NOTE: + { + uint8_t key = event.param & 0xFF; + uint8_t velocity = (event.param >> 8) & 0xFF; + uint32_t tick = event.tick; + double time = tick * ticksToSeconds; + log->Trace("NoteEvent: key={0} action={1} time={2}", key, velocity == 0 ? "release" : (velocity == 255 ? "stop" : "press"), time); + if (velocity == 0 || velocity == 255) + { + // release || stop + if (!tonesInProgress.contains(key)) + { + log->Error("Error: key {0} released without being pressed at {1}", key, time); + continue; + } + Tone tone = tonesInProgress[key]; + tone.end = time; + m_Tones.emplace_back(tone); + tonesInProgress.erase(key); + } + else + { + // press + tonesInProgress[key] = { key, time, 0}; + } + break; + } + } + } + + for (const auto& tone : m_Tones) + { + log->Debug("Tone{ key={0}, start={1}, end={2} }", static_cast(tone.key), tone.start, tone.end); + } + + m_Status = StatusType::READY; + } +} \ No newline at end of file diff --git a/src/NH/Bass/MidiFile.h b/src/NH/Bass/MidiFile.h new file mode 100644 index 0000000..fa6f2b4 --- /dev/null +++ b/src/NH/Bass/MidiFile.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace NH::Bass +{ + /** + * @brief Represents a MIDI file with metadata for the music transitions. + * + * The MIDI file is parser and the notes are extracted and stored in the `m_Tones` vector. + * Specific note keys mean: + * - A4 (key=69): Fade out transition + * Fade-out starts at the START time and decreases the volume to 0 at the END time. + * By default, the next song is fading-in for the same period. + * - A#4 (key=70): Fade in transition for next song (optional) + * If present alongside A4, defines the fade-in period of the next song. + */ + class MidiFile + { + public: + struct Tone { + uint32_t key; + double start; + double end; + }; + + enum class StatusType : size_t { NOT_LOADED = 0, PARSING, READY, FAILED }; + enum class LoadingStatusType : size_t { NOT_LOADED = 0, LOADING, READY, FAILED }; + + private: + static Logger* log; + HashString m_ThemeId; + HashString m_AudioId; + String m_Filename; + std::vector m_Buffer{}; + StatusType m_Status = StatusType::NOT_LOADED; + LoadingStatusType m_LoadingStatus = LoadingStatusType::NOT_LOADED; + String m_Error{}; + std::vector m_MidiEvents{}; + std::vector m_Tones{}; + + public: + MidiFile(HashString themeId, HashString audioId, const String& filename) + : m_ThemeId(themeId), m_AudioId(audioId), m_Filename(filename) {} + + void LoadMidiFile(Executor& executor); + + [[nodiscard]] HashString GetThemeId() const { return m_ThemeId; } + + [[nodiscard]] HashString GetAudioId() const { return m_AudioId; } + + [[nodiscard]] const String& GetFilename() const { return m_Filename; } + + [[nodiscard]] StatusType GetStatus() const { return m_Status; } + + [[nodiscard]] LoadingStatusType GetLoadingStatus() const { return m_LoadingStatus; } + + [[nodiscard]] const String& GetError() const { return m_Error; } + + [[nodiscard]] const std::vector& GetTones() const { return m_Tones; } + + private: + void ParseMidiFile(); + }; +} diff --git a/src/NH/Bass/MusicTheme.cpp b/src/NH/Bass/MusicTheme.cpp index 48ae819..215a3a4 100644 --- a/src/NH/Bass/MusicTheme.cpp +++ b/src/NH/Bass/MusicTheme.cpp @@ -33,6 +33,11 @@ namespace NH::Bass m_Zones.emplace_back(zone); } + void MusicTheme::AddMidiFile(HashString type, const std::shared_ptr& midiFile) + { + m_MidiFiles.emplace(std::make_pair(type, midiFile)); + } + void MusicTheme::LoadAudioFiles(Executor& executor) { for (auto& [type, audioFile]: m_AudioFiles) @@ -68,6 +73,12 @@ namespace NH::Bass return AudioEffects::None; } + const std::shared_ptr& MusicTheme::GetMidiFile(HashString type) const + { + if (m_MidiFiles.contains(type)) { return m_MidiFiles.at(type); } + return {}; + } + bool MusicTheme::HasZone(HashString zone) const { return std::find(m_Zones.begin(), m_Zones.end(), zone) != m_Zones.end(); diff --git a/src/NH/Bass/MusicTheme.h b/src/NH/Bass/MusicTheme.h index b5dcaa0..a36c078 100644 --- a/src/NH/Bass/MusicTheme.h +++ b/src/NH/Bass/MusicTheme.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,7 @@ namespace NH::Bass String m_Name; std::unordered_map m_AudioFiles; std::unordered_map m_AudioEffects; + std::unordered_map> m_MidiFiles; std::vector m_Zones; public: @@ -70,6 +72,8 @@ namespace NH::Bass void AddZone(HashString zone); + void AddMidiFile(HashString type, const std::shared_ptr& midiFile); + void LoadAudioFiles(Executor& executor); [[nodiscard]] const String& GetName() const { return m_Name; } @@ -82,6 +86,10 @@ namespace NH::Bass [[nodiscard]] const AudioEffects& GetAudioEffects(HashString type) const; + [[nodiscard]] const std::shared_ptr& GetMidiFile(HashString type) const; + + [[nodiscard]] const std::vector& GetZones() const { return m_Zones; } + [[nodiscard]] bool HasZone(HashString zone) const; [[nodiscard]] String ToString() const override diff --git a/vdf/vdf.cmake b/vdf/vdf.cmake index ad401b1..c0059ff 100644 --- a/vdf/vdf.cmake +++ b/vdf/vdf.cmake @@ -2,3 +2,6 @@ file(MAKE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") file(COPY "${CMAKE_INSTALL_PREFIX}/bin/zBassMusic.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") file(COPY "${CMAKE_INSTALL_PREFIX}/bin/UnionAPI.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") file(COPY "${CMAKE_INSTALL_PREFIX}/bin/bass.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") +file(COPY "${CMAKE_INSTALL_PREFIX}/bin/bassmidi.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") +file(COPY "${CMAKE_INSTALL_PREFIX}/bin/bassflac.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun") +file(COPY "${CMAKE_INSTALL_PREFIX}/bin/bassopus.dll" DESTINATION "${CMAKE_INSTALL_PREFIX}/vdf/System/Autorun")