diff --git a/CMakeLists.txt b/CMakeLists.txt index e0f216c63..632f92bdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,6 +387,7 @@ set( ADL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/adlmidi" ) set( OPN_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/opnmidi" ) set( TIMIDITYPP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/timidityplus" ) set( TIMIDITY_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/timidity" ) +set( WILDMIDI_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libraries/wildmidi" ) if( NOT CMAKE_CROSSCOMPILING ) if( NOT CROSS_EXPORTS ) @@ -412,6 +413,7 @@ add_subdirectory( libraries/adlmidi ) add_subdirectory( libraries/opnmidi ) add_subdirectory( libraries/timidity ) add_subdirectory( libraries/timidityplus ) +add_subdirectory( libraries/wildmidi ) add_subdirectory( wadsrc ) add_subdirectory( wadsrc_bm ) add_subdirectory( wadsrc_lights ) diff --git a/libraries/wildmidi/CMakeLists.txt b/libraries/wildmidi/CMakeLists.txt new file mode 100644 index 000000000..fbecf9f4e --- /dev/null +++ b/libraries/wildmidi/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required( VERSION 2.8.7 ) + +make_release_only() + +if( ZD_CMAKE_COMPILER_IS_GNUC_COMPATIBLE ) + set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer" ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" ) +endif() + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}") + +include_directories( wildmidi ) + +file( GLOB HEADER_FILES + wildmidi/*.h + ) +add_library( wildmidi STATIC + file_io.cpp + gus_pat.cpp + reverb.cpp + wildmidi_lib.cpp + wm_error.cpp + ) +target_link_libraries( wildmidi ) diff --git a/src/sound/wildmidi/file_io.cpp b/libraries/wildmidi/file_io.cpp similarity index 100% rename from src/sound/wildmidi/file_io.cpp rename to libraries/wildmidi/file_io.cpp diff --git a/src/sound/wildmidi/gus_pat.cpp b/libraries/wildmidi/gus_pat.cpp similarity index 100% rename from src/sound/wildmidi/gus_pat.cpp rename to libraries/wildmidi/gus_pat.cpp diff --git a/src/sound/wildmidi/reverb.cpp b/libraries/wildmidi/reverb.cpp similarity index 100% rename from src/sound/wildmidi/reverb.cpp rename to libraries/wildmidi/reverb.cpp diff --git a/src/sound/wildmidi/common.h b/libraries/wildmidi/wildmidi/common.h similarity index 100% rename from src/sound/wildmidi/common.h rename to libraries/wildmidi/wildmidi/common.h diff --git a/src/sound/wildmidi/file_io.h b/libraries/wildmidi/wildmidi/file_io.h similarity index 100% rename from src/sound/wildmidi/file_io.h rename to libraries/wildmidi/wildmidi/file_io.h diff --git a/src/sound/wildmidi/gus_pat.h b/libraries/wildmidi/wildmidi/gus_pat.h similarity index 100% rename from src/sound/wildmidi/gus_pat.h rename to libraries/wildmidi/wildmidi/gus_pat.h diff --git a/src/sound/wildmidi/reverb.h b/libraries/wildmidi/wildmidi/reverb.h similarity index 100% rename from src/sound/wildmidi/reverb.h rename to libraries/wildmidi/wildmidi/reverb.h diff --git a/src/sound/wildmidi/wildmidi_file.h b/libraries/wildmidi/wildmidi/wildmidi_file.h similarity index 100% rename from src/sound/wildmidi/wildmidi_file.h rename to libraries/wildmidi/wildmidi/wildmidi_file.h diff --git a/src/sound/wildmidi/wildmidi_lib.h b/libraries/wildmidi/wildmidi/wildmidi_lib.h similarity index 100% rename from src/sound/wildmidi/wildmidi_lib.h rename to libraries/wildmidi/wildmidi/wildmidi_lib.h diff --git a/src/sound/wildmidi/wm_error.h b/libraries/wildmidi/wildmidi/wm_error.h similarity index 100% rename from src/sound/wildmidi/wm_error.h rename to libraries/wildmidi/wildmidi/wm_error.h diff --git a/src/sound/wildmidi/wildmidi_lib.cpp b/libraries/wildmidi/wildmidi_lib.cpp similarity index 99% rename from src/sound/wildmidi/wildmidi_lib.cpp rename to libraries/wildmidi/wildmidi_lib.cpp index cf7b25ec0..4254c1f11 100644 --- a/src/sound/wildmidi/wildmidi_lib.cpp +++ b/libraries/wildmidi/wildmidi_lib.cpp @@ -37,7 +37,7 @@ #endif #include #include -#include +#include #include "common.h" #include "wm_error.h" diff --git a/src/sound/wildmidi/wm_error.cpp b/libraries/wildmidi/wm_error.cpp similarity index 100% rename from src/sound/wildmidi/wm_error.cpp rename to libraries/wildmidi/wm_error.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c6a293467..5ea292300 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -468,7 +468,7 @@ set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_L if (HAVE_VULKAN) set( ZDOOM_LIBS ${ZDOOM_LIBS} "glslang" "SPIRV" "OGLCompiler") endif() -include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" "${ADL_INCLUDE_DIR}" "${OPN_INCLUDE_DIR}" "${TIMIDITYPP_INCLUDE_DIR}" "${TIMIDITY_INCLUDE_DIR}" ) +include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" "${ADL_INCLUDE_DIR}" "${OPN_INCLUDE_DIR}" "${TIMIDITYPP_INCLUDE_DIR}" "${TIMIDITY_INCLUDE_DIR}" "${WILDMIDI_INCLUDE_DIR}" ) if( ${HAVE_VM_JIT} ) add_definitions( -DHAVE_VM_JIT ) @@ -693,7 +693,6 @@ file( GLOB HEADER_FILES sound/oplsynth/*.h sound/oplsynth/dosbox/*.h sound/thirdparty/*.h - sound/wildmidi/*.h rendering/*.h rendering/2d/*.h rendering/swrenderer/*.h @@ -1219,11 +1218,6 @@ set (PCH_SOURCES sound/oplsynth/dosbox/opl.cpp sound/oplsynth/OPL3.cpp sound/oplsynth/nukedopl3.cpp - sound/wildmidi/file_io.cpp - sound/wildmidi/gus_pat.cpp - sound/wildmidi/reverb.cpp - sound/wildmidi/wildmidi_lib.cpp - sound/wildmidi/wm_error.cpp rendering/swrenderer/textures/r_swtexture.cpp rendering/swrenderer/textures/warptexture.cpp rendering/swrenderer/textures/swcanvastexture.cpp @@ -1320,7 +1314,7 @@ if( UNIX ) endif() endif() -target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma adl opn timidity timidityplus ) +target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma adl opn timidity timidityplus wildmidi ) include_directories( . g_statusbar @@ -1338,7 +1332,6 @@ include_directories( . sound/music sound/backend sound/oplsynth - sound/wildmidi xlat utility utility/nodebuilder @@ -1450,7 +1443,6 @@ source_group("Audio Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/soun source_group("Audio Files\\Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/backend/.+") source_group("Audio Files\\OPL Synth" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/oplsynth/.+") source_group("Audio Files\\OPL Synth\\DOSBox" FILES sound/oplsynth/dosbox/opl.cpp sound/oplsynth/dosbox/opl.h) -source_group("Audio Files\\WildMidi" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/wildmidi/.+") source_group("Audio Files\\MIDI Devices" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/mididevices/.+") source_group("Audio Files\\MIDI Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/midisources/.+") source_group("Audio Files\\Music formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sound/musicformats/.+") diff --git a/src/sound/music/i_soundfont.h b/src/sound/music/i_soundfont.h index 92472f004..40d9a0b8d 100644 --- a/src/sound/music/i_soundfont.h +++ b/src/sound/music/i_soundfont.h @@ -5,7 +5,7 @@ #include "files.h" #include "timiditypp/timidity_file.h" #include "timidity/timidity_file.h" -#include "wildmidi_file.h" +#include "wildmidi/wildmidi_file.h" enum { diff --git a/src/sound/musicformats/music_xa.cpp b/src/sound/musicformats/music_xa.cpp new file mode 100644 index 000000000..463e5f9a2 --- /dev/null +++ b/src/sound/musicformats/music_xa.cpp @@ -0,0 +1,366 @@ +#include "i_musicinterns.h" +/** + * PlayStation XA (ADPCM) source support for MultiVoc + * Adapted and remixed from superxa2wav (taken from EDuke32) + */ + + +//#define NO_XA_HEADER + +#define kNumOfSamples 224 +#define kNumOfSGs 18 +#define TTYWidth 80 + +#define kBufSize (kNumOfSGs*kNumOfSamples) +#define kSamplesMono (kNumOfSGs*kNumOfSamples) +#define kSamplesStereo (kNumOfSGs*kNumOfSamples/2) + +/* ADPCM */ +#define XA_DATA_START (0x44-48) + +#define DblToPCMF(dt) ((dt) / 32768.f) + +typedef struct { + FileReader reader; + size_t committed; + bool blockIsMono; + bool blockIs18K; + bool finished; + + double t1, t2; + double t1_x, t2_x; + + float block[kBufSize]; +} xa_data; + +typedef int8_t SoundGroup[128]; + +typedef struct XASector { + int8_t sectorFiller[48]; + SoundGroup SoundGroups[18]; +} XASector; + +static double K0[4] = { + 0.0, + 0.9375, + 1.796875, + 1.53125 +}; +static double K1[4] = { + 0.0, + 0.0, + -0.8125, + -0.859375 +}; + + + +static int8_t getSoundData(int8_t *buf, int32_t unit, int32_t sample) +{ + int8_t ret; + int8_t *p; + int32_t offset, shift; + + p = buf; + shift = (unit%2) * 4; + + offset = 16 + (unit / 2) + (sample * 4); + p += offset; + + ret = (*p >> shift) & 0x0F; + + if (ret > 7) { + ret -= 16; + } + return ret; +} + +static int8_t getFilter(const int8_t *buf, int32_t unit) +{ + return (*(buf + 4 + unit) >> 4) & 0x03; +} + + +static int8_t getRange(const int8_t *buf, int32_t unit) +{ + return *(buf + 4 + unit) & 0x0F; +} + + +static void decodeSoundSectMono(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + int32_t unit, sample; + int32_t sndgrp; + double tmp2, tmp3, tmp4, tmp5; + int8_t &decodeBuf = xad->block; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit++) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + for (sample = 0; sample < 28; sample++) + { + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCM(xad->t1); + } + } + } +} + +static void decodeSoundSectStereo(XASector *ssct, xa_data * xad) +{ + size_t count = 0; + int8_t snddat, filt, range; + int8_t filt1, range1; + int16_t decoded; + int32_t unit, sample; + int32_t sndgrp; + double tmp2, tmp3, tmp4, tmp5; + int8_t &decodeBuf = xad->block; + + for (sndgrp = 0; sndgrp < kNumOfSGs; sndgrp++) + { + for (unit = 0; unit < 8; unit+= 2) + { + range = getRange(ssct->SoundGroups[sndgrp], unit); + filt = getFilter(ssct->SoundGroups[sndgrp], unit); + range1 = getRange(ssct->SoundGroups[sndgrp], unit+1); + filt1 = getFilter(ssct->SoundGroups[sndgrp], unit+1); + + for (sample = 0; sample < 28; sample++) + { + // Channel 1 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit, sample); + tmp2 = (double)(1 << (12 - range)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1 * K0[filt]; + tmp5 = xad->t2 * K1[filt]; + xad->t2 = xad->t1; + xad->t1 = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCMF(xad->t1); + + // Channel 2 + snddat = getSoundData(ssct->SoundGroups[sndgrp], unit+1, sample); + tmp2 = (double)(1 << (12 - range1)); + tmp3 = (double)snddat * tmp2; + tmp4 = xad->t1_x * K0[filt1]; + tmp5 = xad->t2_x * K1[filt1]; + xad->t2_x = xad->t1_x; + xad->t1_x = tmp3 + tmp4 + tmp5; + decodeBuf[count++] = DblToPCMF(xad->t1_x); + } + } + } +} + +//========================================================================== +// +// Get one decoded block of data +// +//========================================================================== + +static bool getNextXABlock(xa_data *xad, bool looping ) +{ + XASector ssct; + int coding; + const int SUBMODE_REAL_TIME_SECTOR = (1 << 6); + const int SUBMODE_FORM = (1 << 5); + const int SUBMODE_AUDIO_DATA = (1 << 2); + + do + { + size_t bytes = xad->reader.GetLength() - xad->reader.Tell(); + + if (sizeof(XASector) < bytes) + bytes = sizeof(XASector); + + xad->reader.Read(&ssct, bytes); + } + while (ssct.sectorFiller[46] != (SUBMODE_REAL_TIME_SECTOR | SUBMODE_FORM | SUBMODE_AUDIO_DATA)); + + coding = ssct.sectorFiller[47]; + + xa->blockIsMono = (coding & 3) == 0; + xa->blockIs18K = (((coding >> 2) & 3) == 1); + + uint32_t samples; + + if (!xa->blockIsMono) + { + decodeSoundSectStereo(&ssct, xad); + samples = kSamplesStereo; + } + else + { + decodeSoundSectMono(&ssct, xad); + samples = kSamplesMono; + } + + if (xad->GetLength() == xad->Tell()) + { + if (looping) + { + xad->pos = XA_DATA_START; + xad->t1 = xad->t2 = xad->t1_x = xad->t2_x = 0; + } + else + xa->finished = true; + } + + xa->finished = false; +} + +//========================================================================== +// +// XASong +// +//========================================================================== + +class XASong : public StreamSong +{ +public: + GMESong(FileReader & readr); + ~GMESong(); + bool SetSubsong(int subsong); + void Play(bool looping, int subsong); + +protected: + xa_data xad; + + static bool Read(SoundStream *stream, void *buff, int len, void *userdata); +}; + +//========================================================================== +// +// XASong - Constructor +// +//========================================================================== + +XASong::XASong(FileReader &reader) +{ + ChannelConfig iChannels; + SampleType Type; + + + xad.ptr = std::move(reader); + xad.pos = XA_DATA_START; + xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; + getNextXABlock(&xad, false); + auto SampleRate = xad.blockIs18K? 18900 : 37800; + + const uint32_t sampleLength = (uint32_t)decoder->getSampleLength(); + Reader = std::move(reader); + Decoder = decoder; + Channels = 2; // Since the format can theoretically switch between mono and stereo we need to output everything as stereo. + m_Stream = GSnd->CreateStream(Read, 64 * 1024, 0, SampleRate, this); +} + +//========================================================================== +// +// XASong - Destructor +// +//========================================================================== + +XASong::~XASong() +{ + Stop(); + if (m_Stream != nullptr) + { + delete m_Stream; + m_Stream = nullptr; + } +} + + +//========================================================================== +// +// XASong :: Play +// +//========================================================================== + +void XASong::Play(bool looping, int track) +{ + m_Status = STATE_Stopped; + m_Looping = looping; + if (xad.finished && looping) + { + xad.pos = XA_DATA_START; + xad.t1 = xad.t2 = xad.t1_x = xad.t2_x = 0; + xad.reader.Seek(0, FileReader::SeekSet); + xad.finished = false; + } + if (m_Stream->Play(looping, 1)) + { + m_Status = STATE_Playing; + } +} + +//========================================================================== +// +// XASong :: SetSubsong +// +//========================================================================== + +bool XASong::SetSubsong(int track) +{ + return false; +} + +//========================================================================== +// +// XASong :: Read STATIC +// +//========================================================================== + +bool XASong::Read(SoundStream *stream, void *vbuff, int ilen, void *userdata) +{ + auto self = (XASong*)userdata; + while (ilen > 0) + { + if (self->xad.committed < kBufSize) + { + // commit the data + if (self->xad.blockIsMono) + { + } + else + { + } + } + if (self->xad.finished) + { + // fill the rest with 0's. + } + if (ilen > 0) + { + // we ran out of data and need more + getNextXABlock(&self->xad, m_Looping); + // repeat until done. + } + else break; + + } + return !self->xad.finished; +} + +//========================================================================== +// +// XA_OpenSong +// +//========================================================================== + +MusInfo *XA_OpenSong(FileReader &reader) +{ + return new XASong(reader); +} +