Add externals

This commit is contained in:
Phantop 2020-12-06 12:11:14 -05:00
parent 0194ae1315
commit bd301929e6
608 changed files with 393362 additions and 0 deletions

1
external/clownaudio/.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
PKGBUILD text eol=lf

9
external/clownaudio/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
/build
/example/build
/example/obj
/example/test
/example/test.exe
/test/build
/test/obj
/test/test
/test/test.exe

509
external/clownaudio/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,509 @@
cmake_minimum_required(VERSION 3.12)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(CLOWNAUDIO_LIBVORBIS "Enable the libvorbis decoder backend" OFF)
option(CLOWNAUDIO_STB_VORBIS "Enable the stb_vorbis decoder backend" ON)
option(CLOWNAUDIO_DR_MP3 "Enable the dr_mp3 decoder backend" ON)
option(CLOWNAUDIO_LIBOPUS "Enable the libopus decoder backend" OFF)
option(CLOWNAUDIO_LIBFLAC "Enable the libFLAC decoder backend" OFF)
option(CLOWNAUDIO_DR_FLAC "Enable the dr_flac decoder backend" ON)
option(CLOWNAUDIO_DR_WAV "Enable the dr_wav decoder backend" ON)
option(CLOWNAUDIO_LIBSNDFILE "Enable the libsndfile decoder backend" OFF)
option(CLOWNAUDIO_LIBOPENMPT "Enable the libopenmpt decoder backend" OFF)
option(CLOWNAUDIO_LIBXMPLITE "Enable the libxmp-lite decoder backend" OFF)
option(CLOWNAUDIO_PXTONE "Enable the PxTone decoder backend" OFF)
option(CLOWNAUDIO_SNES_SPC "Enable the snes_spc decoder backend" OFF)
option(CLOWNAUDIO_MIXER_ONLY "Disables playback capabilities" OFF)
if(NOT CLOWNAUDIO_MIXER_ONLY)
set(CLOWNAUDIO_BACKEND "miniaudio" CACHE STRING "Which playback backend to use: supported options are 'miniaudio', 'SDL1', 'SDL2', 'Cubeb', and 'PortAudio'")
endif()
project(clownaudio VERSION 0.2 DESCRIPTION "Stereo audio engine supporting various formats" LANGUAGES C CXX)
add_library(clownaudio
"include/clownaudio/mixer.h"
"src/miniaudio.cpp"
"src/miniaudio.h"
"src/mixer.cpp"
"src/decoding/decoder_selector.cpp"
"src/decoding/decoder_selector.h"
"src/decoding/predecoder.cpp"
"src/decoding/predecoder.h"
"src/decoding/resampled_decoder.cpp"
"src/decoding/resampled_decoder.h"
"src/decoding/split_decoder.cpp"
"src/decoding/split_decoder.h"
"src/decoding/decoders/common.h"
"src/decoding/decoders/memory_stream.cpp"
"src/decoding/decoders/memory_stream.h"
)
if(NOT CLOWNAUDIO_MIXER_ONLY)
target_sources(clownaudio PRIVATE
"include/clownaudio/clownaudio.h"
"include/clownaudio/playback.h"
"src/clownaudio.cpp"
)
endif()
####################
# Decoding backends
####################
if(CLOWNAUDIO_LIBVORBIS)
target_compile_definitions(clownaudio PRIVATE USE_LIBVORBIS)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libvorbis.cpp"
"src/decoding/decoders/libvorbis.h"
)
endif()
if(CLOWNAUDIO_STB_VORBIS)
target_compile_definitions(clownaudio PRIVATE USE_STB_VORBIS)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/stb_vorbis.cpp"
"src/decoding/decoders/stb_vorbis.h"
)
include(CheckLibraryExists)
check_library_exists(m exp "" LIBM)
if(LIBM)
target_link_libraries(clownaudio PRIVATE m)
list(APPEND STATIC_LIBS m)
endif()
endif()
if(CLOWNAUDIO_DR_MP3)
target_compile_definitions(clownaudio PRIVATE USE_DR_MP3)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/dr_mp3.cpp"
"src/decoding/decoders/dr_mp3.h"
)
endif()
if(CLOWNAUDIO_LIBOPUS)
target_compile_definitions(clownaudio PRIVATE USE_LIBOPUS)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libopus.cpp"
"src/decoding/decoders/libopus.h"
)
endif()
if(CLOWNAUDIO_LIBFLAC)
target_compile_definitions(clownaudio PRIVATE USE_LIBFLAC)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libflac.cpp"
"src/decoding/decoders/libflac.h"
)
endif()
if(CLOWNAUDIO_DR_FLAC)
target_compile_definitions(clownaudio PRIVATE USE_DR_FLAC)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/dr_flac.cpp"
"src/decoding/decoders/dr_flac.h"
"src/decoding/decoders/libs/dr_flac.h"
)
endif()
if(CLOWNAUDIO_DR_WAV)
target_compile_definitions(clownaudio PRIVATE USE_DR_WAV)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/dr_wav.cpp"
"src/decoding/decoders/dr_wav.h"
"src/decoding/decoders/libs/dr_wav.h"
)
endif()
if(CLOWNAUDIO_LIBSNDFILE)
target_compile_definitions(clownaudio PRIVATE USE_LIBSNDFILE)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libsndfile.cpp"
"src/decoding/decoders/libsndfile.h"
)
endif()
if(CLOWNAUDIO_LIBOPENMPT)
target_compile_definitions(clownaudio PRIVATE USE_LIBOPENMPT)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libopenmpt.cpp"
"src/decoding/decoders/libopenmpt.h"
)
endif()
if(CLOWNAUDIO_LIBXMPLITE)
target_compile_definitions(clownaudio PRIVATE USE_LIBXMPLITE)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libxmp-lite.cpp"
"src/decoding/decoders/libxmp-lite.h"
)
endif()
if(CLOWNAUDIO_PXTONE)
target_compile_definitions(clownaudio PRIVATE USE_PXTONE)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/pxtone.cpp"
"src/decoding/decoders/pxtone.h"
"src/decoding/decoders/pxtone_noise.cpp"
"src/decoding/decoders/pxtone_noise.h"
"src/decoding/decoders/libs/pxtone/pxtn.h"
"src/decoding/decoders/libs/pxtone/pxtnDelay.cpp"
"src/decoding/decoders/libs/pxtone/pxtnDelay.h"
"src/decoding/decoders/libs/pxtone/pxtnDescriptor.cpp"
"src/decoding/decoders/libs/pxtone/pxtnDescriptor.h"
"src/decoding/decoders/libs/pxtone/pxtnError.cpp"
"src/decoding/decoders/libs/pxtone/pxtnError.h"
"src/decoding/decoders/libs/pxtone/pxtnEvelist.cpp"
"src/decoding/decoders/libs/pxtone/pxtnEvelist.h"
"src/decoding/decoders/libs/pxtone/pxtnMaster.cpp"
"src/decoding/decoders/libs/pxtone/pxtnMaster.h"
"src/decoding/decoders/libs/pxtone/pxtnMax.h"
"src/decoding/decoders/libs/pxtone/pxtnMem.cpp"
"src/decoding/decoders/libs/pxtone/pxtnMem.h"
"src/decoding/decoders/libs/pxtone/pxtnOverDrive.cpp"
"src/decoding/decoders/libs/pxtone/pxtnOverDrive.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Frequency.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Frequency.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Noise.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Noise.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_NoiseBuilder.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_NoiseBuilder.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Oggv.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Oggv.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Oscillator.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_Oscillator.h"
"src/decoding/decoders/libs/pxtone/pxtnPulse_PCM.cpp"
"src/decoding/decoders/libs/pxtone/pxtnPulse_PCM.h"
"src/decoding/decoders/libs/pxtone/pxtnService.cpp"
"src/decoding/decoders/libs/pxtone/pxtnService.h"
"src/decoding/decoders/libs/pxtone/pxtnService_moo.cpp"
"src/decoding/decoders/libs/pxtone/pxtnText.cpp"
"src/decoding/decoders/libs/pxtone/pxtnText.h"
"src/decoding/decoders/libs/pxtone/pxtnUnit.cpp"
"src/decoding/decoders/libs/pxtone/pxtnUnit.h"
"src/decoding/decoders/libs/pxtone/pxtnWoice.cpp"
"src/decoding/decoders/libs/pxtone/pxtnWoice.h"
"src/decoding/decoders/libs/pxtone/pxtnWoice_io.cpp"
"src/decoding/decoders/libs/pxtone/pxtnWoicePTV.cpp"
"src/decoding/decoders/libs/pxtone/pxtoneNoise.cpp"
"src/decoding/decoders/libs/pxtone/pxtoneNoise.h"
)
list(APPEND STATIC_LIBS stdc++)
endif()
if(CLOWNAUDIO_SNES_SPC)
target_compile_definitions(clownaudio PRIVATE USE_SNES_SPC)
target_sources(clownaudio PRIVATE
"src/decoding/decoders/snes_spc.cpp"
"src/decoding/decoders/snes_spc.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/blargg_common.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/blargg_config.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/blargg_endian.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/blargg_source.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/dsp.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/dsp.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SNES_SPC.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SNES_SPC.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SNES_SPC_misc.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SNES_SPC_state.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/spc.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/spc.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SPC_CPU.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SPC_DSP.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SPC_DSP.h"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SPC_Filter.cpp"
"src/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/SPC_Filter.h"
)
endif()
####################
# Playback backends
####################
if(NOT CLOWNAUDIO_MIXER_ONLY)
if(CLOWNAUDIO_BACKEND STREQUAL "miniaudio")
target_sources(clownaudio PRIVATE "src/playback/miniaudio.cpp")
target_compile_definitions(clownaudio PRIVATE MINIAUDIO_ENABLE_DEVICE_IO)
include(CheckLibraryExists)
check_library_exists(m pow "" LIBM)
if(LIBM)
target_link_libraries(clownaudio PRIVATE m)
list(APPEND STATIC_LIBS m)
endif()
check_library_exists(pthread pthread_create "" LIBPTHREAD)
if(LIBPTHREAD)
target_link_libraries(clownaudio PRIVATE pthread)
list(APPEND STATIC_LIBS pthread)
endif()
target_link_libraries(clownaudio PRIVATE ${CMAKE_DL_LIBS})
list(APPEND STATIC_LIBS ${CMAKE_DL_LIBS})
elseif(CLOWNAUDIO_BACKEND STREQUAL "SDL1")
target_sources(clownaudio PRIVATE "src/playback/sdl1.cpp")
elseif(CLOWNAUDIO_BACKEND STREQUAL "SDL2")
target_sources(clownaudio PRIVATE "src/playback/sdl2.cpp")
elseif(CLOWNAUDIO_BACKEND STREQUAL "Cubeb")
target_sources(clownaudio PRIVATE "src/playback/cubeb.cpp")
elseif(CLOWNAUDIO_BACKEND STREQUAL "PortAudio")
target_sources(clownaudio PRIVATE "src/playback/portaudio.cpp")
else()
message(FATAL_ERROR "Invalid BACKEND selected")
endif()
endif()
####################
# Find dependencies
####################
# CMake
if(NOT CLOWNAUDIO_MIXER_ONLY)
if(CLOWNAUDIO_BACKEND STREQUAL "Cubeb")
find_package(cubeb REQUIRED)
target_link_libraries(clownaudio PRIVATE cubeb::cubeb)
list(APPEND STATIC_LIBS cubeb)
endif()
endif()
# pkg-config
find_package(PkgConfig QUIET)
if(NOT CLOWNAUDIO_MIXER_ONLY)
if(CLOWNAUDIO_BACKEND STREQUAL "SDL1")
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by SDL1, but it couldn't be found")
endif()
pkg_check_modules(sdl REQUIRED IMPORTED_TARGET sdl)
target_link_libraries(clownaudio PRIVATE PkgConfig::sdl)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} sdl")
endif()
if(CLOWNAUDIO_BACKEND STREQUAL "SDL2")
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by SDL2, but it couldn't be found")
endif()
pkg_check_modules(sdl2 REQUIRED IMPORTED_TARGET sdl2)
target_link_libraries(clownaudio PRIVATE PkgConfig::sdl2)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} sdl2")
endif()
if(CLOWNAUDIO_BACKEND STREQUAL "PortAudio")
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by PortAudio, but it couldn't be found")
endif()
pkg_check_modules(portaudio REQUIRED IMPORTED_TARGET portaudio-2.0)
target_link_libraries(clownaudio PRIVATE PkgConfig::portaudio)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} portaudio-2.0")
endif()
endif()
if(CLOWNAUDIO_LIBVORBIS)
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by libvorbis, but it couldn't be found")
endif()
pkg_check_modules(vorbisfile REQUIRED IMPORTED_TARGET vorbisfile)
target_link_libraries(clownaudio PRIVATE PkgConfig::vorbisfile)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} vorbisfile")
endif()
if(CLOWNAUDIO_LIBOPUS)
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by libopus, but it couldn't be found")
endif()
pkg_check_modules(opusfile REQUIRED IMPORTED_TARGET opusfile)
target_link_libraries(clownaudio PRIVATE PkgConfig::opusfile)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} opusfile")
endif()
if(CLOWNAUDIO_LIBFLAC)
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by libflac, but it couldn't be found")
endif()
pkg_check_modules(flac REQUIRED IMPORTED_TARGET flac)
target_link_libraries(clownaudio PRIVATE PkgConfig::flac)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} flac")
endif()
if(CLOWNAUDIO_LIBSNDFILE)
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by libsndfile, but it couldn't be found")
endif()
pkg_check_modules(sndfile REQUIRED IMPORTED_TARGET sndfile)
target_link_libraries(clownaudio PRIVATE PkgConfig::sndfile)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} sndfile")
endif()
if(CLOWNAUDIO_LIBOPENMPT)
if (NOT PkgConfig_FOUND)
message(FATAL_ERROR "pkg-config is needed by libopenmpt, but it couldn't be found")
endif()
pkg_check_modules(libopenmpt REQUIRED IMPORTED_TARGET libopenmpt)
target_link_libraries(clownaudio PRIVATE PkgConfig::libopenmpt)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} libopenmpt")
endif()
if(CLOWNAUDIO_LIBXMPLITE)
if (PkgConfig_FOUND)
pkg_check_modules(libxmp-lite IMPORTED_TARGET libxmp-lite)
endif()
if(libxmp-lite_FOUND)
message(STATUS "Using system libxmp-lite")
target_link_libraries(clownaudio PRIVATE PkgConfig::libxmp-lite)
set(PKG_CONFIG_REQUIRES "${PKG_CONFIG_REQUIRES} libxmp-lite")
else()
# Compile it ourselves
message(STATUS "Using local libxmp-lite")
target_sources(clownaudio PRIVATE
"src/decoding/decoders/libs/libxmp-lite/include/libxmp-lite/xmp.h"
"src/decoding/decoders/libs/libxmp-lite/src/common.h"
"src/decoding/decoders/libs/libxmp-lite/src/control.c"
"src/decoding/decoders/libs/libxmp-lite/src/dataio.c"
"src/decoding/decoders/libs/libxmp-lite/src/effects.c"
"src/decoding/decoders/libs/libxmp-lite/src/effects.h"
"src/decoding/decoders/libs/libxmp-lite/src/filter.c"
"src/decoding/decoders/libs/libxmp-lite/src/format.c"
"src/decoding/decoders/libs/libxmp-lite/src/format.h"
"src/decoding/decoders/libs/libxmp-lite/src/hio.c"
"src/decoding/decoders/libs/libxmp-lite/src/hio.h"
"src/decoding/decoders/libs/libxmp-lite/src/lfo.c"
"src/decoding/decoders/libs/libxmp-lite/src/lfo.h"
"src/decoding/decoders/libs/libxmp-lite/src/list.h"
"src/decoding/decoders/libs/libxmp-lite/src/load.c"
"src/decoding/decoders/libs/libxmp-lite/src/load_helpers.c"
"src/decoding/decoders/libs/libxmp-lite/src/mdataio.h"
"src/decoding/decoders/libs/libxmp-lite/src/memio.c"
"src/decoding/decoders/libs/libxmp-lite/src/memio.h"
"src/decoding/decoders/libs/libxmp-lite/src/mix_all.c"
"src/decoding/decoders/libs/libxmp-lite/src/mixer.c"
"src/decoding/decoders/libs/libxmp-lite/src/mixer.h"
"src/decoding/decoders/libs/libxmp-lite/src/period.c"
"src/decoding/decoders/libs/libxmp-lite/src/period.h"
"src/decoding/decoders/libs/libxmp-lite/src/player.c"
"src/decoding/decoders/libs/libxmp-lite/src/player.h"
"src/decoding/decoders/libs/libxmp-lite/src/precomp_lut.h"
"src/decoding/decoders/libs/libxmp-lite/src/read_event.c"
"src/decoding/decoders/libs/libxmp-lite/src/scan.c"
"src/decoding/decoders/libs/libxmp-lite/src/smix.c"
"src/decoding/decoders/libs/libxmp-lite/src/tempfile.h"
"src/decoding/decoders/libs/libxmp-lite/src/virtual.c"
"src/decoding/decoders/libs/libxmp-lite/src/virtual.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/common.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/it.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/it_load.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/itsex.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/loader.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/mod.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/mod_load.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/s3m.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/s3m_load.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/sample.c"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/xm.h"
"src/decoding/decoders/libs/libxmp-lite/src/loaders/xm_load.c"
)
target_include_directories(clownaudio PRIVATE "src/decoding/decoders/libs/libxmp-lite/include/libxmp-lite")
target_compile_definitions(clownaudio PRIVATE LIBXMP_CORE_PLAYER=1)
endif()
endif()
####################
# Misc. settings
####################
# Include the public headers
target_include_directories(clownaudio PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
# Export symbols
include(GenerateExportHeader)
GENERATE_EXPORT_HEADER(clownaudio EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/export/clownaudio_export.h")
target_include_directories(clownaudio PUBLIC "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/export>")
set_target_properties(clownaudio PROPERTIES C_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)
# Define public header
if(CLOWNAUDIO_MIXER_ONLY)
set_target_properties(clownaudio PROPERTIES PUBLIC_HEADER "include/clownaudio/mixer.h;${CMAKE_BINARY_DIR}/export/clownaudio_export.h")
else()
set_target_properties(clownaudio PROPERTIES PUBLIC_HEADER "include/clownaudio/clownaudio.h;include/clownaudio/playback.h;include/clownaudio/mixer.h;${CMAKE_BINARY_DIR}/export/clownaudio_export.h")
endif()
# Set language standards
# Extensions may be used by dependencies (miniaudio), so enable them
set_target_properties(clownaudio PROPERTIES
C_STANDARD 99
C_EXTENSIONS ON
CXX_STANDARD 11
CXX_EXTENSIONS ON
)
# Shut up those stupid MSVC warnings
if(MSVC)
target_compile_definitions(clownaudio PRIVATE _CRT_SECURE_NO_WARNINGS)
endif()
# Group files by directory when using an IDE
get_target_property(CLOWNAUDIO_SOURCES clownaudio SOURCES)
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES CLOWNAUDIO_SOURCES)
####################
# Install
####################
include(GNUInstallDirs)
# Install directories
install(TARGETS clownaudio
EXPORT clownaudioTargets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/clownaudio"
)
# Install `clownaudioConfig.cmake` and `clownaudioTargets.cmake`
install(EXPORT clownaudioTargets DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/clownaudio")
export(TARGETS clownaudio FILE "clownaudioTargets.cmake")
include(CMakePackageConfigHelpers)
configure_package_config_file(
"clownaudioConfig.cmake.in"
"clownaudioConfig.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/clownaudio"
)
install(FILES "${CMAKE_BINARY_DIR}/clownaudioConfig.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/clownaudio")
# pkg-config
if(DEFINED STATIC_LIBS)
list(REMOVE_DUPLICATES STATIC_LIBS)
foreach(ITEM ${STATIC_LIBS})
set(PKG_CONFIG_STATIC_LIBS "${PKG_CONFIG_STATIC_LIBS} -l${ITEM}")
endforeach()
endif()
configure_file("clownaudio.pc.in" "clownaudio.pc" @ONLY)
install(FILES "${CMAKE_BINARY_DIR}/clownaudio.pc" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig")

19
external/clownaudio/LICENCE.txt vendored Normal file
View file

@ -0,0 +1,19 @@
zlib License
(C) 2018-2020 Clownacy
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

88
external/clownaudio/README.md vendored Normal file
View file

@ -0,0 +1,88 @@
## About
clownaudio is a stereo sound engine, capable of playing and mixing sounds in a
variety of formats.
Supported formats include...
* Ogg Vorbis
* MP3
* Opus
* FLAC
* WAV
* Various tracker formats ('.it', '.xm', '.mod', etc.)
* PxTone Music
* PxTone Noise
* SNES SPC
clownaudio is a 'full stack' sound engine, meaning that it handles everything
from decoding to playback - the user merely has to provide it with sound data to
process.
That said, clownaudio's internals are modular, meaning you can easily extract
its mixer and use it as part of your own audio system if needed.
## Decoding backends
In order to support a range of audio formats, clownaudio leverages numerous
open-source libraries, dubbed 'decoding backends'. These libraries include...
| Library | Format | Licence | Included in-tree |
|-------------|------------|---------------------|------------------|
| libvorbis | Ogg Vorbis | BSD | No |
| stb_vorbis | Ogg Vorbis | Public-domain/MIT-0 | Yes |
| dr_mp3 | MP3 | Public-domain/MIT-0 | Yes |
| libopus | Opus | BSD | No |
| libFLAC | FLAC | BSD | No |
| dr_flac | FLAC | Public-domain/MIT-0 | Yes |
| dr_wav | WAV | Public-domain/MIT-0 | Yes |
| libsndfile | Various (includes Ogg Vorbis, FLAC, WAV, AIFF, and others) | LGPL 2.1 | No |
| libopenmpt | Various (includes .mod, .it, .xm, .s3m, .mptm, and **many** others) | BSD | No |
| libxmp-lite | .mod, .it, .xm, .s3m | MIT | Yes |
| PxTone | PxTone Music/PxTone Noise | Custom (appears to be permissive) | Yes |
| snes_spc | SNES SPC | LGPL 2.1 | Yes |
clownaudio aims to be bloat-free and dependency-free: each decoding backend can
be toggled at compile-time, and an effort is made to provide multiple backends
for individual formats, to allow the user a choice between standard libraries
(such as libvorbis), and lighter in-tree libraries (such as stb_vorbis).
## Playback backends
In addition, clownaudio utilises one of the following libraries to provide
audio-playback:
| Library | Licence | Included in-tree |
|-----------|---------------------|------------------|
| Cubeb | ISC | No |
| miniaudio | Public-domain/MIT-0 | Yes |
| PortAudio | MIT | No |
| SDL1.2 | LGPL 2.1 | No |
| SDL2 | zlib | No |
## Building
clownaudio is built with CMake - both shared and static libraries can be
produced. Built libraries can be accessed via `pkg-config` and CMake's
`find_package`.
As an alternative, CMake projects can embed clownaudio directly, using
`add_subdirectory`.
Example `Makefile` and `CMakeLists.txt` files for a demo tool are provided: the
`Makefile` demonstrates linking clownaudio with `pkg-config`, and the
`CMakeLists.txt` file demonstrates linking with `find_package` and embedding
with `add_subdirectory`.
`PKGBUILD` files for MSYS2 and Arch Linux are provided in the `packages`
directory.
## Licensing
clownaudio itself is under the zlib licence.
Be aware that libraries used by the various backends are subject to
their own licences.

13
external/clownaudio/clownaudio.pc vendored Normal file
View file

@ -0,0 +1,13 @@
prefix=C:/Program Files (x86)/CSE2
exec_prefix=C:/Program Files (x86)/CSE2
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: clownaudio
Description: Stereo audio engine supporting various formats
Version: 0.2
Requires.private:
Libs: -L${libdir} -lclownaudio
Libs.private: OFF
Cflags: -I${includedir}

13
external/clownaudio/clownaudio.pc.in vendored Normal file
View file

@ -0,0 +1,13 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
Version: @PROJECT_VERSION@
Requires.private: @PKG_CONFIG_REQUIRES@
Libs: -L${libdir} -lclownaudio
Libs.private: @PKG_CONFIG_STATIC_LIBS@
Cflags: -I${includedir}

View file

@ -0,0 +1,60 @@
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
####### Any changes to this file will be overwritten by the next CMake run ####
####### The input file was clownaudioConfig.cmake.in ########
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
macro(set_and_check _var _file)
set(${_var} "${_file}")
if(NOT EXISTS "${_file}")
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
endif()
endmacro()
macro(check_required_components _NAME)
foreach(comp ${${_NAME}_FIND_COMPONENTS})
if(NOT ${_NAME}_${comp}_FOUND)
if(${_NAME}_FIND_REQUIRED_${comp})
set(${_NAME}_FOUND FALSE)
endif()
endif()
endforeach()
endmacro()
####################################################################################
if(NOT OFF)
if(OFF)
find_package(PkgConfig REQUIRED)
pkg_check_modules(vorbisfile REQUIRED IMPORTED_TARGET vorbisfile)
endif()
if(OFF)
find_package(PkgConfig REQUIRED)
pkg_check_modules(flac REQUIRED IMPORTED_TARGET flac)
endif()
if(OFF)
find_package(PkgConfig REQUIRED)
pkg_check_modules(opusfile REQUIRED IMPORTED_TARGET opusfile)
endif()
if(OFF)
find_package(PkgConfig REQUIRED)
pkg_check_modules(sndfile REQUIRED IMPORTED_TARGET sndfile)
endif()
if(OFF)
find_package(PkgConfig REQUIRED)
pkg_check_modules(libopenmpt REQUIRED IMPORTED_TARGET libopenmpt)
endif()
if()
find_package(PkgConfig REQUIRED)
pkg_check_modules(libxmp-lite REQUIRED IMPORTED_TARGET libxmp-lite)
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/clownaudioTargets.cmake")
check_required_components(clownaudio)

View file

@ -0,0 +1,36 @@
@PACKAGE_INIT@
if(NOT @BUILD_SHARED_LIBS@)
if(@CLOWNAUDIO_LIBVORBIS@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(vorbisfile REQUIRED IMPORTED_TARGET vorbisfile)
endif()
if(@CLOWNAUDIO_LIBFLAC@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(flac REQUIRED IMPORTED_TARGET flac)
endif()
if(@CLOWNAUDIO_LIBOPUS@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(opusfile REQUIRED IMPORTED_TARGET opusfile)
endif()
if(@CLOWNAUDIO_LIBSNDFILE@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(sndfile REQUIRED IMPORTED_TARGET sndfile)
endif()
if(@CLOWNAUDIO_LIBOPENMPT@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(libopenmpt REQUIRED IMPORTED_TARGET libopenmpt)
endif()
if(@libxmp-lite_FOUND@)
find_package(PkgConfig REQUIRED)
pkg_check_modules(libxmp-lite REQUIRED IMPORTED_TARGET libxmp-lite)
endif()
endif()
include("${CMAKE_CURRENT_LIST_DIR}/clownaudioTargets.cmake")
check_required_components(clownaudio)

View file

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.12)
option(SUBDIRECTORY "Use clownaudio as a subdirectory, instead of an external package" OFF)
project(clownaudio_test LANGUAGES CXX)
add_executable(test "test.cpp")
if(SUBDIRECTORY)
add_subdirectory(".." "clownaudio" EXCLUDE_FROM_ALL)
else()
find_package(clownaudio REQUIRED)
endif()
target_link_libraries(test PRIVATE clownaudio)
set_target_properties(test PROPERTIES
CXX_STANDARD 98
CXX_EXTENSIONS OFF
)
if(MSVC)
target_compile_definitions(test PRIVATE _CRT_SECURE_NO_WARNINGS) # Shut up those stupid warnings
endif()

13
external/clownaudio/example/Makefile vendored Normal file
View file

@ -0,0 +1,13 @@
ALL_CXXFLAGS = $(CXXFLAGS)
ALL_LIBS = $(LIBS)
ifeq ($(STATIC), 1)
ALL_CXXFLAGS += $(shell pkg-config clownaudio --cflags --static) -static -DCLOWNAUDIO_STATIC_DEFINE
ALL_LIBS += $(shell pkg-config clownaudio --libs --static)
else
ALL_CXXFLAGS += $(shell pkg-config clownaudio --cflags)
ALL_LIBS += $(shell pkg-config clownaudio --libs)
endif
test: test.cpp
$(CXX) $(ALL_CXXFLAGS) -o $@ $^ $(LDFLAGS) $(ALL_LIBS)

273
external/clownaudio/example/Makefile.old vendored Normal file
View file

@ -0,0 +1,273 @@
USE_LIBVORBIS = false
USE_STB_VORBIS = true
USE_DR_MP3 = true
USE_LIBOPUS = false
USE_LIBFLAC = false
USE_DR_FLAC = true
USE_DR_WAV = true
USE_LIBSNDFILE = false
USE_LIBOPENMPT = false
USE_LIBXMPLITE = true
USE_PXTONE = true
USE_SNES_SPC = true
# Can be 'miniaudio', 'SDL1', 'SDL2', 'Cubeb', or 'PortAudio'
BACKEND = miniaudio
CLOWNAUDIO_DIR = ../src
ifneq ($(RELEASE),)
CFLAGS = -O2 -flto
else
CFLAGS = -Og -ggdb3
endif
ALL_CFLAGS = -std=c99 -MMD -MP -MF $@.d -DCLOWNAUDIO_EXPORT= -DCLOWNAUDIO_NO_EXPORT= -I $(CFLAGS)
ifneq ($(RELEASE),)
CXXFLAGS = -O2 -flto
else
CXXFLAGS = -Og -ggdb3
endif
ALL_CXXFLAGS = -std=c++98 -MMD -MP -MF $@.d -DCLOWNAUDIO_EXPORT= -DCLOWNAUDIO_NO_EXPORT= $(CXXFLAGS)
ifneq ($(RELEASE),)
LDFLAGS = -s
else
LDFLAGS =
endif
ALL_LDFLAGS = $(LDFLAGS)
LIBS =
ALL_LIBS = $(LIBS)
SDL1_CFLAGS = $(shell pkg-config sdl --cflags)
SDL1_LIBS = $(shell pkg-config sdl --libs --static)
SDL2_CFLAGS = $(shell pkg-config sdl2 --cflags)
SDL2_LIBS = $(shell pkg-config sdl2 --libs --static)
CLOWNAUDIO_SOURCES = \
miniaudio.cpp \
mixer.cpp \
decoding/decoder_selector.cpp \
decoding/predecoder.cpp \
decoding/resampled_decoder.cpp \
decoding/split_decoder.cpp \
decoding/decoders/memory_stream.cpp
ifeq ($(USE_LIBVORBIS), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libvorbis.cpp
ALL_CXXFLAGS += -DUSE_LIBVORBIS $(shell pkg-config vorbisfile --cflags)
ALL_LIBS += $(shell pkg-config vorbisfile --libs --static)
endif
ifeq ($(USE_STB_VORBIS), true)
CLOWNAUDIO_SOURCES += decoding/decoders/stb_vorbis.cpp
ALL_CXXFLAGS += -DUSE_STB_VORBIS
ALL_LIBS += -lm
endif
ifeq ($(USE_DR_MP3), true)
CLOWNAUDIO_SOURCES += decoding/decoders/dr_mp3.cpp
ALL_CXXFLAGS += -DUSE_DR_MP3
endif
ifeq ($(USE_LIBOPUS), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libopus.cpp
ALL_CXXFLAGS += -DUSE_LIBOPUS $(shell pkg-config opusfile --cflags)
ALL_LIBS += $(shell pkg-config opusfile --libs --static)
endif
ifeq ($(USE_LIBFLAC), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libflac.cpp
ALL_CXXFLAGS += -DUSE_LIBFLAC $(shell pkg-config flac --cflags)
ALL_LIBS += $(shell pkg-config flac --libs --static)
endif
ifeq ($(USE_DR_FLAC), true)
CLOWNAUDIO_SOURCES += decoding/decoders/dr_flac.cpp
ALL_CXXFLAGS += -DUSE_DR_FLAC
endif
ifeq ($(USE_DR_WAV), true)
CLOWNAUDIO_SOURCES += decoding/decoders/dr_wav.cpp
ALL_CXXFLAGS += -DUSE_DR_WAV
endif
ifeq ($(USE_LIBSNDFILE), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libsndfile.cpp
ALL_CXXFLAGS += -DUSE_LIBSNDFILE $(shell pkg-config sndfile --cflags)
ALL_LIBS += $(shell pkg-config sndfile --libs --static)
endif
ifeq ($(USE_LIBOPENMPT), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libopenmpt.cpp
ALL_CXXFLAGS += -DUSE_LIBOPENMPT $(shell pkg-config libopenmpt --cflags)
ALL_LIBS += $(shell pkg-config libopenmpt --libs --static)
endif
ifeq ($(USE_LIBXMPLITE), true)
CLOWNAUDIO_SOURCES += decoding/decoders/libxmp-lite.cpp
ALL_CXXFLAGS += -DUSE_LIBXMPLITE
ifeq ($(shell pkg-config libxmp-lite --exists && echo 1), 1)
ALL_CXXFLAGS += $(shell pkg-config libxmp-lite --cflags)
ALL_LIBS += $(shell pkg-config libxmp-lite --libs --static)
else
ALL_CFLAGS += -I$(CLOWNAUDIO_DIR)/decoding/decoders/libs/libxmp-lite/include/libxmp-lite
ALL_CXXFLAGS += -I$(CLOWNAUDIO_DIR)/decoding/decoders/libs/libxmp-lite/include/libxmp-lite
endif
endif
ifeq ($(USE_PXTONE), true)
CLOWNAUDIO_SOURCES += decoding/decoders/pxtone.cpp decoding/decoders/pxtone_noise.cpp
ALL_CXXFLAGS += -DUSE_PXTONE
ALL_LIBS += -lstdc++
# Apparently PxTone supports Vorbis-encoded samples
ifeq ($(USE_LIBVORBIS), true)
ALL_CXXFLAGS += -DpxINCLUDE_OGGVORBIS
endif
endif
ifeq ($(USE_SNES_SPC), true)
CLOWNAUDIO_SOURCES += decoding/decoders/snes_spc.cpp
ALL_CXXFLAGS += -DUSE_SNES_SPC
ALL_LIBS += -lstdc++
endif
ifeq ($(BACKEND), miniaudio)
CLOWNAUDIO_SOURCES += playback/miniaudio.cpp
ALL_CXXFLAGS += -DMINIAUDIO_ENABLE_DEVICE_IO
ALL_LIBS += -lpthread -lm
ifneq ($(WINDOWS), 1)
ALL_LIBS += -ldl
endif
else ifeq ($(BACKEND), SDL1)
CLOWNAUDIO_SOURCES += playback/sdl1.cpp
ALL_CXXFLAGS += $(SDL1_CFLAGS)
ALL_LIBS += $(SDL1_LIBS)
else ifeq ($(BACKEND), SDL2)
CLOWNAUDIO_SOURCES += playback/sdl2.cpp
ALL_CXXFLAGS += $(SDL2_CFLAGS)
ALL_LIBS += $(SDL2_LIBS)
else ifeq ($(BACKEND), Cubeb)
CLOWNAUDIO_SOURCES += playback/cubeb.cpp
ALL_LIBS += -lcubeb
else ifeq ($(BACKEND), PortAudio)
CLOWNAUDIO_SOURCES += playback/portaudio.cpp
ALL_CXXFLAGS += $(shell pkg-config portaudio-2.0 --cflags)
ALL_LIBS += $(shell pkg-config portaudio-2.0 --libs --static)
endif
LIBXMPLITE_SOURCES = \
src/virtual.c \
src/format.c \
src/period.c \
src/player.c \
src/read_event.c \
src/dataio.c \
src/lfo.c \
src/scan.c \
src/control.c \
src/filter.c \
src/effects.c \
src/mixer.c \
src/mix_all.c \
src/load_helpers.c \
src/load.c \
src/hio.c \
src/smix.c \
src/memio.c \
src/loaders/common.c \
src/loaders/itsex.c \
src/loaders/sample.c \
src/loaders/xm_load.c \
src/loaders/mod_load.c \
src/loaders/s3m_load.c \
src/loaders/it_load.c
PXTONE_SOURCES = \
pxtnDelay.cpp \
pxtnDescriptor.cpp \
pxtnError.cpp \
pxtnEvelist.cpp \
pxtnMaster.cpp \
pxtnMem.cpp \
pxtnOverDrive.cpp \
pxtnPulse_Frequency.cpp \
pxtnPulse_Noise.cpp \
pxtnPulse_NoiseBuilder.cpp \
pxtnPulse_Oggv.cpp \
pxtnPulse_Oscillator.cpp \
pxtnPulse_PCM.cpp \
pxtnService.cpp \
pxtnService_moo.cpp \
pxtnText.cpp \
pxtnUnit.cpp \
pxtnWoice.cpp \
pxtnWoice_io.cpp \
pxtnWoicePTV.cpp \
pxtoneNoise.cpp
SPC_SOURCES = \
dsp.cpp \
SNES_SPC.cpp \
SNES_SPC_misc.cpp \
SNES_SPC_state.cpp \
spc.cpp \
SPC_DSP.cpp \
SPC_Filter.cpp
OBJECTS += obj/test.o
OBJECTS += $(addprefix obj/clownaudio/, $(addsuffix .o, $(CLOWNAUDIO_SOURCES)))
ifeq ($(USE_LIBXMPLITE), true)
ifneq ($(shell pkg-config libxmp-lite --exists && echo 1), 1)
OBJECTS += $(addprefix obj/libxmp-lite/, $(addsuffix .o, $(LIBXMPLITE_SOURCES)))
endif
endif
ifeq ($(USE_PXTONE), true)
OBJECTS += $(addprefix obj/pxtone/, $(addsuffix .o, $(PXTONE_SOURCES)))
endif
ifeq ($(USE_SNES_SPC), true)
OBJECTS += $(addprefix obj/spc/, $(addsuffix .o, $(SPC_SOURCES)))
endif
DEPENDENCIES = $(addsuffix .d, $(OBJECTS))
all: test
obj/test.o: test.cpp
$(info Compiling $<)
@mkdir -p $(@D)
@$(CXX) $(ALL_CXXFLAGS) -Wall -Wextra -pedantic -I. -I../include $< -o $@ -c
obj/clownaudio/%.c.o: $(CLOWNAUDIO_DIR)/%.c
$(info Compiling $<)
@mkdir -p $(@D)
@$(CC) $(ALL_CFLAGS) -Wall -Wextra -pedantic -I. -I../include $< -o $@ -c
obj/clownaudio/%.cpp.o: $(CLOWNAUDIO_DIR)/%.cpp
$(info Compiling $<)
@mkdir -p $(@D)
@$(CXX) $(ALL_CXXFLAGS) -Wall -Wextra -pedantic -I. -I../include $< -o $@ -c
obj/libxmp-lite/%.o: $(CLOWNAUDIO_DIR)/decoding/decoders/libs/libxmp-lite/%
$(info Compiling $<)
@mkdir -p $(@D)
@$(CC) $(ALL_CFLAGS) -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-sign-compare -Wno-maybe-uninitialized -I$(CLOWNAUDIO_DIR)/decoding/decoders/libs/libxmp-lite/src -DLIBXMP_CORE_PLAYER=1 $< -o $@ -c
obj/pxtone/%.o: $(CLOWNAUDIO_DIR)/decoding/decoders/libs/pxtone/%
$(info Compiling $<)
@mkdir -p $(@D)
@$(CXX) $(ALL_CXXFLAGS) -std=c++11 -Wall -Wextra -pedantic $< -o $@ -c
obj/spc/%.o: $(CLOWNAUDIO_DIR)/decoding/decoders/libs/snes_spc-0.9.0/snes_spc/%
$(info Compiling $<)
@mkdir -p $(@D)
@$(CXX) $(ALL_CXXFLAGS) $< -o $@ -c
test: $(OBJECTS)
$(info Linking $@)
@$(CC) $(ALL_CFLAGS) -Wall -Wextra -pedantic $^ -o $@ $(ALL_LDFLAGS) $(ALL_LIBS)
include $(wildcard $(DEPENDENCIES))

228
external/clownaudio/example/test.cpp vendored Normal file
View file

@ -0,0 +1,228 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
//#define STB_LEAKCHECK_IMPLEMENTATION
//#include "stb_leakcheck.h"
#include "clownaudio/clownaudio.h"
int main(int argc, char *argv[])
{
if (argc != 2 && argc != 3)
{
printf("clownaudio test program\n\nUsage: %s [intro file] [loop file (optional)]\n\n", argv[0]);
return 0;
}
printf("Initialising clownaudio\n");
fflush(stdout);
if (ClownAudio_Init())
{
const char *file_paths[2];
if (argc == 3)
{
file_paths[0] = argv[1];
file_paths[1] = argv[2];
}
else if (argc == 2)
{
file_paths[0] = argv[1];
file_paths[1] = NULL;
}
ClownAudio_SoundDataConfig config;
ClownAudio_InitSoundDataConfig(&config);
ClownAudio_SoundData *sound_data = ClownAudio_LoadSoundDataFromFiles(file_paths[0], file_paths[1], &config);
if (sound_data != NULL)
{
printf("Creating sound\n");
fflush(stdout);
ClownAudio_SoundConfig config2;
ClownAudio_InitSoundConfig(&config2);
config2.loop = true;
ClownAudio_SoundID instance = ClownAudio_CreateSound(sound_data, &config2);
ClownAudio_UnpauseSound(instance);
if (instance != 0)
{
printf("\n"
"Controls:\n"
" q - Quit\n"
" r - Rewind sound\n"
" o [duration] - Fade-out sound (milliseconds)\n"
" i [duration] - Fade-in sound (milliseconds)\n"
" c - Cancel fade\n"
" u [rate] - Set sample-rate (Hz)\n"
" p - Pause/unpause sound\n"
" v [left] [right] - Set sound volume (0.0-1.0)\n"
);
fflush(stdout);
bool pause = false;
bool exit = false;
while (!exit)
{
char buffer[128];
fgets(buffer, sizeof(buffer), stdin);
char mode;
while (sscanf(buffer, "%c", &mode) != 1);
switch (mode)
{
case 'q':
printf("Quitting\n");
fflush(stdout);
exit = true;
break;
case 'r':
printf("Rewinding sound\n");
fflush(stdout);
ClownAudio_RewindSound(instance);
break;
case 'o':
{
unsigned int param;
if (sscanf(buffer, "%c %u", &mode, &param) != 2)
param = 1000 * 2;
printf("Fading-out sound over %u milliseconds\n", param);
fflush(stdout);
ClownAudio_FadeOutSound(instance, param);
break;
}
case 'i':
{
unsigned int param;
if (sscanf(buffer, "%c %u", &mode, &param) != 2)
param = 1000 * 2;
printf("Fading-in sound over %u milliseconds\n", param);
fflush(stdout);
ClownAudio_FadeInSound(instance, param);
break;
}
case 'c':
printf("Cancelling fade\n");
fflush(stdout);
ClownAudio_CancelFade(instance);
break;
case 'u':
{
unsigned int param;
if (sscanf(buffer, "%c %u", &mode, &param) != 2)
param = 8000;
printf("Setting sample-rate to %uHz\n", param);
fflush(stdout);
ClownAudio_SetSoundSampleRate(instance, param, param);
break;
}
case 'p':
if (pause)
{
printf("Unpausing sound\n");
fflush(stdout);
ClownAudio_UnpauseSound(instance);
}
else
{
printf("Pausing sound\n");
fflush(stdout);
ClownAudio_PauseSound(instance);
}
pause = !pause;
break;
case 'v':
{
float volume_left, volume_right;
int values_read = sscanf(buffer, "%c %f %f", &mode, &volume_left, &volume_right);
if (values_read == 1)
volume_left = volume_right = 1.0f;
else if (values_read == 2)
volume_right = volume_left;
printf("Setting volume to %f left, %f right\n", volume_left, volume_right);
fflush(stdout);
ClownAudio_SetSoundVolume(instance, volume_left * 0x100, volume_right * 0x100);
break;
}
}
}
printf("Destroying sound\n");
fflush(stdout);
ClownAudio_DestroySound(instance);
}
else
{
printf("Couldn't create sound\n");
}
printf("Unloading sound data\n");
fflush(stdout);
ClownAudio_UnloadSoundData(sound_data);
}
else
{
printf("Couldn't load sound data\n");
}
printf("Deinitialising clownaudio\n");
fflush(stdout);
ClownAudio_Deinit();
}
else
{
printf("Couldn't initialise clownaudio\n");
}
// stb_leakcheck_dumpmem();
return 0;
}

View file

@ -0,0 +1,152 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stddef.h>
#include "mixer.h"
#if !defined(CLOWNAUDIO_EXPORT) && !defined(CLOWNAUDIO_NO_EXPORT)
#include "clownaudio_export.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Defined elsewhere, but included here for documentation
typedef struct ClownAudio_SoundDataConfig
{
bool predecode; // If true, the sound *may* be predecoded if possible. If not, the sound will still be loaded, albeit not predecoded.
bool must_predecode; // If true, the sound *must* be predecoded if possible. If not, the function will fail.
bool dynamic_sample_rate; // If sound is predecoded, then this needs to be true for `ClownAudio_SetSoundSampleRate` to work
} ClownAudio_SoundDataConfig;
typedef struct ClownAudio_SoundConfig
{
bool loop; // If true, the sound will loop indefinitely
bool do_not_free_when_done; // If true, the sound will not be automatically destroyed once it finishes playing
bool dynamic_sample_rate; // If sound is not predecoded, then this needs to be true for `ClownAudio_SetSoundSampleRate` to work
} ClownAudio_SoundConfig;
//////////////////////////////////
// Configuration initialisation //
//////////////////////////////////
// Initialises a `ClownAudio_SoundDataConfig` struct with sane default values
CLOWNAUDIO_EXPORT void ClownAudio_InitSoundDataConfig(ClownAudio_SoundDataConfig *config);
// Initialises a `ClownAudio_SoundConfig` struct with sane default values
CLOWNAUDIO_EXPORT void ClownAudio_InitSoundConfig(ClownAudio_SoundConfig *config);
*/
////////////////////
// Initialisation //
////////////////////
// Initialises clownaudio
CLOWNAUDIO_EXPORT bool ClownAudio_Init(void);
// Deinitialises clownaudio
CLOWNAUDIO_EXPORT void ClownAudio_Deinit(void);
//////////////////////////////////
// Sound-data loading/unloading //
//////////////////////////////////
// Loads data from up to two memory buffers - either buffer pointer can be NULL.
// If two buffers are specified and looping is enabled, the sound will loop at the point where the first buffer ends, and the second one begins.
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_LoadSoundDataFromMemory(const unsigned char *file_buffer1, size_t file_size1, const unsigned char *file_buffer2, size_t file_size2, ClownAudio_SoundDataConfig *config);
// Loads data from up to two files - either file path can be NULL.
// If two files are specified and looping is enabled, the sound will loop at the point where the first file ends, and the second one begins.
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_LoadSoundDataFromFiles(const char *intro_path, const char *loop_path, ClownAudio_SoundDataConfig *config);
// Unloads data. All sounds using the specified data must be destroyed manually before this function is called.
CLOWNAUDIO_EXPORT void ClownAudio_UnloadSoundData(ClownAudio_SoundData *sound_data);
////////////////////////////////
// Sound creation/destruction //
////////////////////////////////
// Creates a sound from sound-data. The sound will be paused by default.
CLOWNAUDIO_EXPORT ClownAudio_SoundID ClownAudio_CreateSound(ClownAudio_SoundData *sound_data, ClownAudio_SoundConfig *config);
// Destroys sound.
CLOWNAUDIO_EXPORT void ClownAudio_DestroySound(ClownAudio_SoundID sound_id);
/////////////////////////////
// Assorted sound controls //
/////////////////////////////
// Playback
// Rewinds sound to the very beginning.
CLOWNAUDIO_EXPORT void ClownAudio_RewindSound(ClownAudio_SoundID sound_id);
// Pauses sound.
CLOWNAUDIO_EXPORT void ClownAudio_PauseSound(ClownAudio_SoundID sound_id);
// Unpauses sound.
CLOWNAUDIO_EXPORT void ClownAudio_UnpauseSound(ClownAudio_SoundID sound_id);
// Fading
// Make sound fade-out over the specified duration, measured in milliseconds.
// If the sound is currently fading-in, this function will override it, and cause the sound to fade-out from the volume is was currently at.
CLOWNAUDIO_EXPORT void ClownAudio_FadeOutSound(ClownAudio_SoundID sound_id, unsigned int duration);
// Make sound fade-in over the specified duration, measured in milliseconds.
// If the sound is currently fading-out, this function will override it, and cause the sound to fade-in from the volume is was currently at.
CLOWNAUDIO_EXPORT void ClownAudio_FadeInSound(ClownAudio_SoundID sound_id, unsigned int duration);
// Aborts fading, and instantly restores the sound to full volume.
// If you want to smoothly-undo an in-progress fade, use one of the above functions instead.
CLOWNAUDIO_EXPORT void ClownAudio_CancelFade(ClownAudio_SoundID sound_id);
// Miscellaneous
// Returns -1 if the sound doesn't exist, 0 if it's unpaused, or 1 if it is paused.
CLOWNAUDIO_EXPORT int ClownAudio_GetSoundStatus(ClownAudio_SoundID sound_id);
// Sets stereo volume - volume is linear, ranging from 0 (silence) to 0x100 (full volume). Exceeding 0x100 will amplify the volume.
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundVolume(ClownAudio_SoundID sound_id, unsigned short volume_left, unsigned short volume_right);
// Change whether the sound should loop or not. Only certain file formats support this (for example - Ogg Vorbis does, and PxTone doesn't).
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundLoop(ClownAudio_SoundID sound_id, bool loop);
// Override the sound's sample-rate. Note - the sound must have been created with `dynamic_sample_rate` enabled in the configuration struct,
// otherwise this function will silently fail.
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundSampleRate(ClownAudio_SoundID sound_id, unsigned long sample_rate1, unsigned long sample_rate2);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,183 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stddef.h>
#if !defined(CLOWNAUDIO_EXPORT) && !defined(CLOWNAUDIO_NO_EXPORT)
#include "clownaudio_export.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ClownAudio_Mixer ClownAudio_Mixer;
typedef struct ClownAudio_Sound ClownAudio_Sound;
typedef struct ClownAudio_SoundData ClownAudio_SoundData;
typedef unsigned int ClownAudio_SoundID;
typedef struct ClownAudio_SoundDataConfig
{
bool predecode; // If true, the sound *may* be predecoded if possible. If not, the sound will still be loaded, albeit not predecoded.
bool must_predecode; // If true, the sound *must* be predecoded if possible. If not, the function will fail.
bool dynamic_sample_rate; // If sound is predecoded, then this needs to be true for `ClownAudio_SetSoundSampleRate` to work
} ClownAudio_SoundDataConfig;
typedef struct ClownAudio_SoundConfig
{
bool loop; // If true, the sound will loop indefinitely
bool do_not_free_when_done; // If true, the sound will not be automatically destroyed once it finishes playing
bool dynamic_sample_rate; // If sound is not predecoded, then this needs to be true for `ClownAudio_SetSoundSampleRate` to work
} ClownAudio_SoundConfig;
//////////////////////////////////
// Configuration initialisation //
//////////////////////////////////
// Initialises a `ClownAudio_SoundDataConfig` struct with sane default values
CLOWNAUDIO_EXPORT void ClownAudio_InitSoundDataConfig(ClownAudio_SoundDataConfig *config);
// Initialises a `ClownAudio_SoundConfig` struct with sane default values
CLOWNAUDIO_EXPORT void ClownAudio_InitSoundConfig(ClownAudio_SoundConfig *config);
////////////////////////////////
// Mixer creation/destruction //
////////////////////////////////
// Creates a mixer. Will return NULL if it fails.
CLOWNAUDIO_EXPORT ClownAudio_Mixer* ClownAudio_CreateMixer(unsigned long sample_rate);
// Destroys a mixer. All sounds playing through the specified mixer must be destroyed manually before this function is called.
CLOWNAUDIO_EXPORT void ClownAudio_DestroyMixer(ClownAudio_Mixer *mixer);
//////////////////////////////////
// Sound-data loading/unloading //
//////////////////////////////////
// Loads data from up to two memory buffers - either buffer pointer can be NULL.
// If two buffers are specified and looping is enabled, the sound will loop at the point where the first buffer ends, and the second one begins.
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_Mixer_LoadSoundDataFromMemory(ClownAudio_Mixer *mixer, const unsigned char *file_buffer1, size_t file_size1, const unsigned char *file_buffer2, size_t file_size2, ClownAudio_SoundDataConfig *config);
// Loads data from up to two files - either file path can be NULL.
// If two files are specified and looping is enabled, the sound will loop at the point where the first file ends, and the second one begins.
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_Mixer_LoadSoundDataFromFiles(ClownAudio_Mixer *mixer, const char *intro_path, const char *loop_path, ClownAudio_SoundDataConfig *config);
// Unloads data. All sounds using the specified data must be destroyed manually before this function is called.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_UnloadSoundData(ClownAudio_SoundData *sound_data);
////////////////////////////////
// Sound creation/destruction //
////////////////////////////////
// Creates a sound from sound-data. The sound will be paused by default.
CLOWNAUDIO_EXPORT ClownAudio_Sound* ClownAudio_Mixer_CreateSound(ClownAudio_Mixer *mixer, ClownAudio_SoundData *sound_data, ClownAudio_SoundConfig *config);
// Used to create a sound ID from a sound. Must be done only once.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT ClownAudio_SoundID ClownAudio_Mixer_RegisterSound(ClownAudio_Mixer *mixer, ClownAudio_Sound *sound);
// Destroys sound.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_DestroySound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
/////////////////////////////
// Assorted sound controls //
/////////////////////////////
// Playback
// Rewinds sound to the very beginning.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_RewindSound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
// Pauses sound.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_PauseSound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
// Unpauses sound.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_UnpauseSound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
// Fading
// Make sound fade-out over the specified duration, measured in milliseconds.
// If the sound is currently fading-in, this function will override it, and cause the sound to fade-out from the volume is was currently at.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_FadeOutSound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id, unsigned int duration);
// Make sound fade-in over the specified duration, measured in milliseconds.
// If the sound is currently fading-out, this function will override it, and cause the sound to fade-in from the volume is was currently at.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_FadeInSound(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id, unsigned int duration);
// Aborts fading, and instantly restores the sound to full volume.
// If you want to smoothly-undo an in-progress fade, use one of the above functions instead.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_CancelFade(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
// Miscellaneous
// Returns -1 if the sound doesn't exist, 0 if it's unpaused, or 1 if it is paused.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT int ClownAudio_Mixer_GetSoundStatus(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id);
// Sets stereo volume - volume is linear, ranging from 0 (silence) to 0x100 (full volume). Exceeding 0x100 will amplify the volume.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_SetSoundVolume(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id, unsigned short volume_left, unsigned short volume_right);
// Change whether the sound should loop or not. Only certain file formats support this (for example - Ogg Vorbis does, and PxTone doesn't).
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_SetSoundLoop(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id, bool loop);
// Override the sound's sample-rate. Note - the sound must have been created with `dynamic_sample_rate` enabled in the configuration struct,
// otherwise this function will silently fail.
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_SetSoundSampleRate(ClownAudio_Mixer *mixer, ClownAudio_SoundID sound_id, unsigned long sample_rate1, unsigned long sample_rate2);
////////////
// Output //
////////////
// Mix interlaced (L,R ordering) S16 PCM samples into specified S32 buffer (not clamped).
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_MixSamples(ClownAudio_Mixer *mixer, long *output_buffer, size_t frames_to_do);
// Output interlaced (L,R ordering) S16 PCM samples into specified S16 buffer (clamped).
// Must be guarded with mutex.
CLOWNAUDIO_EXPORT void ClownAudio_Mixer_OutputSamples(ClownAudio_Mixer *mixer, short *output_buffer, size_t frames_to_do);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,52 @@
/*
* (C) 2018-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <stddef.h>
#if !defined(CLOWNAUDIO_EXPORT) && !defined(CLOWNAUDIO_NO_EXPORT)
#include "clownaudio_export.h"
#endif
#define CLOWNAUDIO_STREAM_CHANNEL_COUNT 2
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ClownAudio_Stream ClownAudio_Stream;
CLOWNAUDIO_EXPORT bool ClownAudio_InitPlayback(void);
CLOWNAUDIO_EXPORT void ClownAudio_DeinitPlayback(void);
CLOWNAUDIO_EXPORT ClownAudio_Stream* ClownAudio_CreateStream(unsigned long *sample_rate, void (*user_callback)(void*, short*, size_t));
CLOWNAUDIO_EXPORT bool ClownAudio_DestroyStream(ClownAudio_Stream *stream);
CLOWNAUDIO_EXPORT void ClownAudio_SetStreamCallbackData(ClownAudio_Stream *stream, void *user_data);
CLOWNAUDIO_EXPORT bool ClownAudio_PauseStream(ClownAudio_Stream *stream);
CLOWNAUDIO_EXPORT bool ClownAudio_ResumeStream(ClownAudio_Stream *stream);
CLOWNAUDIO_EXPORT void ClownAudio_LockStream(ClownAudio_Stream *stream);
CLOWNAUDIO_EXPORT void ClownAudio_UnlockStream(ClownAudio_Stream *stream);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,52 @@
pkgname=clownaudio-git
pkgver=r430.4a508d0
pkgrel=1
pkgdesc='Stereo audio engine supporting various formats'
arch=('any')
url='https://github.com/Clownacy/clownaudio'
license=('zlib', 'MIT', 'LGPL2', 'custom')
depends=(
'libvorbis'
'flac'
'opus'
)
makedepends=('cmake')
source=("git+https://github.com/Clownacy/clownaudio.git")
sha256sums=('SKIP')
pkgver() {
cd "${srcdir}/clownaudio"
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
prepare() {
mkdir -p "${srcdir}/clownaudio/build"
}
build() {
cd "${srcdir}/clownaudio/build"
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DCLOWNAUDIO_LIBVORBIS=ON \
-DCLOWNAUDIO_STB_VORBIS=OFF \
-DCLOWNAUDIO_DR_MP3=ON \
-DCLOWNAUDIO_LIBOPUS=ON \
-DCLOWNAUDIO_LIBFLAC=ON \
-DCLOWNAUDIO_DR_FLAC=OFF \
-DCLOWNAUDIO_DR_WAV=ON \
-DCLOWNAUDIO_LIBSNDFILE=OFF \
-DCLOWNAUDIO_LIBOPENMPT=OFF \
-DCLOWNAUDIO_LIBXMPLITE=ON \
-DCLOWNAUDIO_PXTONE=ON \
-DCLOWNAUDIO_SNES_SPC=ON \
-DCLOWNAUDIO_BACKEND=miniaudio
make
}
package() {
make -C "${srcdir}/clownaudio/build" DESTDIR="$pkgdir" install
}

View file

@ -0,0 +1,82 @@
pkgbase=mingw-w64-clownaudio-git
pkgname=${MINGW_PACKAGE_PREFIX}-clownaudio-git
pkgver=r231.79004cd
pkgrel=1
pkgdesc='Stereo audio engine supporting various formats'
arch=('any')
url='https://github.com/Clownacy/clownaudio'
license=('zlib', 'MIT', 'LGPL2', 'custom')
depends=(
"${MINGW_PACKAGE_PREFIX}-libvorbis"
"${MINGW_PACKAGE_PREFIX}-flac"
"${MINGW_PACKAGE_PREFIX}-opusfile"
)
makedepends=("${MINGW_PACKAGE_PREFIX}-cmake")
options=('strip' 'staticlibs')
source=("git+https://github.com/Clownacy/clownaudio.git")
sha256sums=('SKIP')
pkgver() {
cd "${srcdir}/clownaudio"
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
prepare() {
mkdir -p "${srcdir}/clownaudio/build-static-${MINGW_CHOST}"
mkdir -p "${srcdir}/clownaudio/build-shared-${MINGW_CHOST}"
}
build() {
cd "${srcdir}/clownaudio/build-static-${MINGW_CHOST}"
MSYS2_ARG_CONV_EXCL="-DCMAKE_INSTALL_PREFIX=" \
${MINGW_PREFIX}/bin/cmake .. \
-G"MSYS Makefiles" \
-DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX} \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DCLOWNAUDIO_LIBVORBIS=ON \
-DCLOWNAUDIO_STB_VORBIS=OFF \
-DCLOWNAUDIO_DR_MP3=ON \
-DCLOWNAUDIO_LIBOPUS=ON \
-DCLOWNAUDIO_LIBFLAC=ON \
-DCLOWNAUDIO_DR_FLAC=OFF \
-DCLOWNAUDIO_DR_WAV=ON \
-DCLOWNAUDIO_LIBSNDFILE=OFF \
-DCLOWNAUDIO_LIBOPENMPT=OFF \
-DCLOWNAUDIO_LIBXMPLITE=ON \
-DCLOWNAUDIO_PXTONE=ON \
-DCLOWNAUDIO_SNES_SPC=ON \
-DCLOWNAUDIO_BACKEND=miniaudio
make
cd "${srcdir}/clownaudio/build-shared-${MINGW_CHOST}"
MSYS2_ARG_CONV_EXCL="-DCMAKE_INSTALL_PREFIX=" \
${MINGW_PREFIX}/bin/cmake .. \
-G"MSYS Makefiles" \
-DCMAKE_INSTALL_PREFIX=${MINGW_PREFIX} \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=ON \
-DCLOWNAUDIO_LIBVORBIS=ON \
-DCLOWNAUDIO_STB_VORBIS=OFF \
-DCLOWNAUDIO_DR_MP3=ON \
-DCLOWNAUDIO_LIBOPUS=ON \
-DCLOWNAUDIO_LIBFLAC=ON \
-DCLOWNAUDIO_DR_FLAC=OFF \
-DCLOWNAUDIO_DR_WAV=ON \
-DCLOWNAUDIO_LIBSNDFILE=OFF \
-DCLOWNAUDIO_LIBOPENMPT=OFF \
-DCLOWNAUDIO_LIBXMPLITE=ON \
-DCLOWNAUDIO_PXTONE=ON \
-DCLOWNAUDIO_SNES_SPC=ON \
-DCLOWNAUDIO_BACKEND=miniaudio
make
}
package() {
make -C "${srcdir}/clownaudio/build-static-${MINGW_CHOST}" DESTDIR="$pkgdir" install
make -C "${srcdir}/clownaudio/build-shared-${MINGW_CHOST}" DESTDIR="$pkgdir" install
}

179
external/clownaudio/src/clownaudio.cpp vendored Normal file
View file

@ -0,0 +1,179 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "clownaudio/clownaudio.h"
#include <stddef.h>
#include <string.h>
#include "clownaudio/mixer.h"
#include "clownaudio/playback.h"
static ClownAudio_Stream *stream;
static ClownAudio_Mixer *mixer;
static void StreamCallback(void *user_data, short *output_buffer, size_t frames_to_do)
{
(void)user_data;
ClownAudio_LockStream(stream);
ClownAudio_Mixer_OutputSamples(mixer, output_buffer, frames_to_do);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT bool ClownAudio_Init(void)
{
if (ClownAudio_InitPlayback())
{
unsigned long sample_rate = 48000; // This default value is a fallback - it will be overwritten if the backend has a preferred rate
stream = ClownAudio_CreateStream(&sample_rate, StreamCallback);
if (stream != NULL)
{
mixer = ClownAudio_CreateMixer(sample_rate);
if (mixer != NULL)
{
ClownAudio_ResumeStream(stream);
return true;
}
ClownAudio_DestroyStream(stream);
}
ClownAudio_DeinitPlayback();
}
return false;
}
CLOWNAUDIO_EXPORT void ClownAudio_Deinit(void)
{
ClownAudio_PauseStream(stream);
ClownAudio_DestroyMixer(mixer);
ClownAudio_DestroyStream(stream);
ClownAudio_DeinitPlayback();
}
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_LoadSoundDataFromMemory(const unsigned char *file_buffer1, size_t file_size1, const unsigned char *file_buffer2, size_t file_size2, ClownAudio_SoundDataConfig *config)
{
return ClownAudio_Mixer_LoadSoundDataFromMemory(mixer, file_buffer1, file_size1, file_buffer2, file_size2, config);
}
CLOWNAUDIO_EXPORT ClownAudio_SoundData* ClownAudio_LoadSoundDataFromFiles(const char *intro_path, const char *loop_path, ClownAudio_SoundDataConfig *config)
{
return ClownAudio_Mixer_LoadSoundDataFromFiles(mixer, intro_path, loop_path, config);
}
CLOWNAUDIO_EXPORT void ClownAudio_UnloadSoundData(ClownAudio_SoundData *sound_data)
{
ClownAudio_Mixer_UnloadSoundData(sound_data);
}
CLOWNAUDIO_EXPORT ClownAudio_SoundID ClownAudio_CreateSound(ClownAudio_SoundData *sound_data, ClownAudio_SoundConfig *config)
{
ClownAudio_Sound *sound = ClownAudio_Mixer_CreateSound(mixer, sound_data, config);
ClownAudio_LockStream(stream);
ClownAudio_SoundID sound_id = ClownAudio_Mixer_RegisterSound(mixer, sound);
ClownAudio_UnlockStream(stream);
return sound_id;
}
CLOWNAUDIO_EXPORT void ClownAudio_DestroySound(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_DestroySound(mixer, sound_id);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_RewindSound(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_RewindSound(mixer, sound_id);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_PauseSound(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_PauseSound(mixer, sound_id);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_UnpauseSound(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_UnpauseSound(mixer, sound_id);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_FadeOutSound(ClownAudio_SoundID sound_id, unsigned int duration)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_FadeOutSound(mixer, sound_id, duration);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_FadeInSound(ClownAudio_SoundID sound_id, unsigned int duration)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_FadeInSound(mixer, sound_id, duration);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_CancelFade(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_CancelFade(mixer, sound_id);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT int ClownAudio_GetSoundStatus(ClownAudio_SoundID sound_id)
{
ClownAudio_LockStream(stream);
int status = ClownAudio_Mixer_GetSoundStatus(mixer, sound_id);
ClownAudio_UnlockStream(stream);
return status;
}
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundVolume(ClownAudio_SoundID sound_id, unsigned short volume_left, unsigned short volume_right)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_SetSoundVolume(mixer, sound_id, volume_left, volume_right);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundLoop(ClownAudio_SoundID sound_id, bool loop)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_SetSoundLoop(mixer, sound_id, loop);
ClownAudio_UnlockStream(stream);
}
CLOWNAUDIO_EXPORT void ClownAudio_SetSoundSampleRate(ClownAudio_SoundID sound_id, unsigned long sample_rate1, unsigned long sample_rate2)
{
ClownAudio_LockStream(stream);
ClownAudio_Mixer_SetSoundSampleRate(mixer, sound_id, sample_rate1, sample_rate2);
ClownAudio_UnlockStream(stream);
}

View file

@ -0,0 +1,325 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "decoder_selector.h"
#include <stddef.h>
#include <stdlib.h>
#include "decoders/common.h"
#include "predecoder.h"
#ifdef USE_LIBVORBIS
#include "decoders/libvorbis.h"
#endif
#ifdef USE_STB_VORBIS
#include "decoders/stb_vorbis.h"
#endif
#ifdef USE_DR_MP3
#include "decoders/dr_mp3.h"
#endif
#ifdef USE_LIBOPUS
#include "decoders/libopus.h"
#endif
#ifdef USE_LIBFLAC
#include "decoders/libflac.h"
#endif
#ifdef USE_DR_FLAC
#include "decoders/dr_flac.h"
#endif
#ifdef USE_DR_WAV
#include "decoders/dr_wav.h"
#endif
#ifdef USE_LIBSNDFILE
#include "decoders/libsndfile.h"
#endif
#ifdef USE_LIBOPENMPT
#include "decoders/libopenmpt.h"
#endif
#ifdef USE_LIBXMPLITE
#include "decoders/libxmp-lite.h"
#endif
#ifdef USE_PXTONE
#include "decoders/pxtone.h"
#include "decoders/pxtone_noise.h"
#endif
#ifdef USE_SNES_SPC
#include "decoders/snes_spc.h"
#endif
#define DECODER_FUNCTIONS(name) \
{ \
Decoder_##name##_Create, \
Decoder_##name##_Destroy, \
Decoder_##name##_Rewind, \
Decoder_##name##_GetSamples \
}
typedef enum DecoderType
{
DECODER_TYPE_PREDECODER,
DECODER_TYPE_COMPLEX,
DECODER_TYPE_SIMPLE
} DecoderType;
typedef struct DecoderFunctions
{
void* (*Create)(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void (*Destroy)(void *decoder);
void (*Rewind)(void *decoder);
size_t (*GetSamples)(void *decoder, short *buffer, size_t frames_to_do);
} DecoderFunctions;
typedef struct DecoderSelector
{
void *decoder;
DecoderSelectorData *data;
bool loop;
} DecoderSelector;
struct DecoderSelectorData
{
const unsigned char *file_buffer;
size_t file_size;
DecoderType decoder_type;
const DecoderFunctions *decoder_functions;
PredecoderData *predecoder_data;
unsigned int channel_count;
};
static const DecoderFunctions decoder_function_list[] = {
#ifdef USE_LIBVORBIS
DECODER_FUNCTIONS(libVorbis),
#endif
#ifdef USE_STB_VORBIS
DECODER_FUNCTIONS(STB_Vorbis),
#endif
#ifdef USE_DR_MP3
DECODER_FUNCTIONS(DR_MP3),
#endif
#ifdef USE_LIBOPUS
DECODER_FUNCTIONS(libOpus),
#endif
#ifdef USE_LIBFLAC
DECODER_FUNCTIONS(libFLAC),
#endif
#ifdef USE_DR_FLAC
DECODER_FUNCTIONS(DR_FLAC),
#endif
#ifdef USE_DR_WAV
DECODER_FUNCTIONS(DR_WAV),
#endif
#ifdef USE_LIBSNDFILE
DECODER_FUNCTIONS(libSndfile),
#endif
#ifdef USE_LIBOPENMPT
DECODER_FUNCTIONS(libOpenMPT),
#endif
#ifdef USE_LIBXMPLITE
DECODER_FUNCTIONS(libXMPLite),
#endif
#ifdef USE_PXTONE
DECODER_FUNCTIONS(PxTone),
#endif
#ifdef USE_PXTONE
DECODER_FUNCTIONS(PxToneNoise),
#endif
#ifdef USE_SNES_SPC
DECODER_FUNCTIONS(SNES_SPC),
#endif
};
static const DecoderFunctions predecoder_functions = {
NULL,
Predecoder_Destroy,
Predecoder_Rewind,
Predecoder_GetSamples
};
DecoderSelectorData* DecoderSelector_LoadData(const unsigned char *file_buffer, size_t file_size, bool predecode, bool must_predecode, const DecoderSpec *wanted_spec)
{
DecoderType decoder_type;
const DecoderFunctions *decoder_functions = NULL;
PredecoderData *predecoder_data = NULL;
DecoderSpec spec;
// Figure out what format this sound is
for (size_t i = 0; i < sizeof(decoder_function_list) / sizeof(decoder_function_list[0]); ++i)
{
void *decoder = decoder_function_list[i].Create(file_buffer, file_size, false, wanted_spec, &spec);
if (decoder != NULL)
{
decoder_type = spec.is_complex ? DECODER_TYPE_COMPLEX : DECODER_TYPE_SIMPLE;
decoder_functions = &decoder_function_list[i];
DecoderStage stage;
stage.decoder = decoder;
stage.Destroy = decoder_functions->Destroy;
stage.Rewind = decoder_functions->Rewind;
stage.GetSamples = decoder_functions->GetSamples;
stage.SetLoop = NULL;
if (decoder_type == DECODER_TYPE_SIMPLE && (predecode || must_predecode))
{
predecoder_data = Predecoder_DecodeData(&spec, wanted_spec, &stage);
if (predecoder_data != NULL)
{
decoder_type = DECODER_TYPE_PREDECODER;
decoder_functions = &predecoder_functions;
break;
}
}
decoder_function_list[i].Destroy(decoder);
break;
}
}
if (decoder_functions != NULL && (!must_predecode || decoder_type == DECODER_TYPE_PREDECODER))
{
DecoderSelectorData *data = (DecoderSelectorData*)malloc(sizeof(DecoderSelectorData));
if (data != NULL)
{
data->file_buffer = file_buffer;
data->file_size = file_size;
data->decoder_type = decoder_type;
data->decoder_functions = decoder_functions;
data->predecoder_data = predecoder_data;
data->channel_count = spec.channel_count;
return data;
}
}
if (predecoder_data != NULL)
Predecoder_UnloadData(predecoder_data);
return NULL;
}
void DecoderSelector_UnloadData(DecoderSelectorData *data)
{
if (data->predecoder_data != NULL)
Predecoder_UnloadData(data->predecoder_data);
free(data);
}
void* DecoderSelector_Create(DecoderSelectorData *data, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
DecoderSelector *selector = (DecoderSelector*)malloc(sizeof(DecoderSelector));
if (selector != NULL)
{
if (data->decoder_type == DECODER_TYPE_PREDECODER)
selector->decoder = Predecoder_Create(data->predecoder_data, loop, wanted_spec, spec);
else
selector->decoder = data->decoder_functions->Create(data->file_buffer, data->file_size, loop, wanted_spec, spec);
if (selector->decoder != NULL)
{
selector->data = data;
selector->loop = loop;
return selector;
}
free(selector);
}
return NULL;
}
void DecoderSelector_Destroy(void *selector_void)
{
DecoderSelector *selector = (DecoderSelector*)selector_void;
selector->data->decoder_functions->Destroy(selector->decoder);
free(selector);
}
void DecoderSelector_Rewind(void *selector_void)
{
DecoderSelector *selector = (DecoderSelector*)selector_void;
selector->data->decoder_functions->Rewind(selector->decoder);
}
size_t DecoderSelector_GetSamples(void *selector_void, short *buffer, size_t frames_to_do)
{
DecoderSelector *selector = (DecoderSelector*)selector_void;
size_t frames_done = 0;
switch (selector->data->decoder_type)
{
default:
return 0;
case DECODER_TYPE_PREDECODER:
return Predecoder_GetSamples(selector->decoder, buffer, frames_to_do);
case DECODER_TYPE_COMPLEX:
return selector->data->decoder_functions->GetSamples(selector->decoder, buffer, frames_to_do);
case DECODER_TYPE_SIMPLE:
// Handle looping here, since the simple decoders don't do it by themselves
while (frames_done != frames_to_do)
{
const size_t frames = selector->data->decoder_functions->GetSamples(selector->decoder, &buffer[frames_done * selector->data->channel_count], frames_to_do - frames_done);
if (frames == 0)
{
if (selector->loop)
selector->data->decoder_functions->Rewind(selector->decoder);
else
break;
}
frames_done += frames;
}
return frames_done;
}
}
void DecoderSelector_SetLoop(void *selector_void, bool loop)
{
DecoderSelector *selector = (DecoderSelector*)selector_void;
switch (selector->data->decoder_type)
{
case DECODER_TYPE_PREDECODER:
Predecoder_SetLoop(selector->decoder, loop);
break;
case DECODER_TYPE_SIMPLE:
selector->loop = loop;
break;
case DECODER_TYPE_COMPLEX:
// TODO - This is impossible to implement
break;
}
}

View file

@ -0,0 +1,35 @@
/*
* (C) 2019 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "decoders/common.h"
typedef struct DecoderSelectorData DecoderSelectorData;
DecoderSelectorData* DecoderSelector_LoadData(const unsigned char *data, size_t data_size, bool predecode, bool must_predecode, const DecoderSpec *wanted_spec);
void DecoderSelector_UnloadData(DecoderSelectorData *data);
void* DecoderSelector_Create(DecoderSelectorData *data, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void DecoderSelector_Destroy(void *selector);
void DecoderSelector_Rewind(void *selector);
size_t DecoderSelector_GetSamples(void *selector, short *buffer, size_t frames_to_do);
void DecoderSelector_SetLoop(void *selector, bool loop);

View file

@ -0,0 +1,40 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
typedef struct DecoderSpec
{
unsigned long sample_rate;
unsigned int channel_count;
bool is_complex;
} DecoderSpec;
typedef struct DecoderStage
{
void *decoder;
void (*Destroy)(void *decoder);
void (*Rewind)(void *decoder);
size_t (*GetSamples)(void *decoder, short *buffer, size_t frames_to_do);
void (*SetLoop)(void *decoder, bool loop);
} DecoderStage;

View file

@ -0,0 +1,61 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "dr_flac.h"
#include <stddef.h>
#define DR_FLAC_IMPLEMENTATION
#define DR_FLAC_NO_STDIO
#include "libs/dr_flac.h"
#include "common.h"
void* Decoder_DR_FLAC_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)loop; // This is ignored in simple decoders
(void)wanted_spec;
drflac *backend = drflac_open_memory(data, data_size, NULL);
if (backend != NULL)
{
spec->sample_rate = backend->sampleRate;
spec->channel_count = backend->channels;
spec->is_complex = false;
}
return backend;
}
void Decoder_DR_FLAC_Destroy(void *decoder)
{
drflac_close((drflac*)decoder);
}
void Decoder_DR_FLAC_Rewind(void *decoder)
{
drflac_seek_to_pcm_frame((drflac*)decoder, 0);
}
size_t Decoder_DR_FLAC_GetSamples(void *decoder, short *buffer, size_t frames_to_do)
{
return drflac_read_pcm_frames_s16((drflac*)decoder, frames_to_do, buffer);
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_DR_FLAC_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_DR_FLAC_Destroy(void *decoder);
void Decoder_DR_FLAC_Rewind(void *decoder);
size_t Decoder_DR_FLAC_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

View file

@ -0,0 +1,75 @@
/*
* (C) 2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "dr_mp3.h"
#include <stddef.h>
#include <stdlib.h>
#define DR_MP3_IMPLEMENTATION
#define DR_MP3_NO_STDIO
#include "libs/dr_mp3.h"
#include "common.h"
void* Decoder_DR_MP3_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)loop; // This is ignored in simple decoders
(void)wanted_spec;
if ((data[0] == 0xFF && data[1] == 0xFB) || (data[0] == 0x49 && data[1] == 0x44 && data[2] == 0x33))
{
drmp3 *instance = (drmp3*)malloc(sizeof(drmp3));
if (instance != NULL)
{
if (drmp3_init_memory(instance, data, data_size, NULL))
{
spec->sample_rate = instance->sampleRate;
spec->channel_count = instance->channels;
spec->is_complex = false;
return instance;
}
free(instance);
}
}
return NULL;
}
void Decoder_DR_MP3_Destroy(void *decoder)
{
drmp3 *instance = (drmp3*)decoder;
drmp3_uninit(instance);
free(instance);
}
void Decoder_DR_MP3_Rewind(void *decoder)
{
drmp3_seek_to_pcm_frame((drmp3*)decoder, 0);
}
size_t Decoder_DR_MP3_GetSamples(void *decoder, short *buffer, size_t frames_to_do)
{
return drmp3_read_pcm_frames_s16((drmp3*)decoder, frames_to_do, buffer);
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_DR_MP3_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_DR_MP3_Destroy(void *decoder);
void Decoder_DR_MP3_Rewind(void *decoder);
size_t Decoder_DR_MP3_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

View file

@ -0,0 +1,72 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "dr_wav.h"
#include <stddef.h>
#include <stdlib.h>
#define DR_WAV_IMPLEMENTATION
#define DR_WAV_NO_STDIO
#include "libs/dr_wav.h"
#include "common.h"
void* Decoder_DR_WAV_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)loop; // This is ignored in simple decoders
(void)wanted_spec;
drwav *instance = (drwav*)malloc(sizeof(drwav));
if (instance != NULL)
{
if (drwav_init_memory(instance, data, data_size, NULL))
{
spec->sample_rate = instance->sampleRate;
spec->channel_count = instance->channels;
spec->is_complex = false;
return instance;
}
free(instance);
}
return NULL;
}
void Decoder_DR_WAV_Destroy(void *decoder)
{
drwav *instance = (drwav*)decoder;
drwav_uninit(instance);
free(instance);
}
void Decoder_DR_WAV_Rewind(void *decoder)
{
drwav_seek_to_pcm_frame((drwav*)decoder, 0);
}
size_t Decoder_DR_WAV_GetSamples(void *decoder, short *buffer, size_t frames_to_do)
{
return drwav_read_pcm_frames_s16((drwav*)decoder, frames_to_do, buffer);
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_DR_WAV_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_DR_WAV_Destroy(void *decoder);
void Decoder_DR_WAV_Rewind(void *decoder);
size_t Decoder_DR_WAV_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

View file

@ -0,0 +1,257 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "libflac.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <FLAC/stream_decoder.h>
#include "common.h"
#include "memory_stream.h"
#undef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
typedef struct Decoder_libFLAC
{
ROMemoryStream *memory_stream;
FLAC__StreamDecoder *flac_stream_decoder;
DecoderSpec *spec;
unsigned int channel_count;
bool error;
unsigned int bits_per_sample;
unsigned char *block_buffer;
unsigned long block_buffer_index;
unsigned long block_buffer_size;
} Decoder_libFLAC;
static FLAC__StreamDecoderReadStatus fread_wrapper(const FLAC__StreamDecoder *flac_stream_decoder, FLAC__byte *output, size_t *count, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
FLAC__StreamDecoderReadStatus status = FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
*count = ROMemoryStream_Read(decoder->memory_stream, output, sizeof(FLAC__byte), *count);
if (*count == 0)
status = FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
return status;
}
static FLAC__StreamDecoderSeekStatus fseek_wrapper(const FLAC__StreamDecoder *flac_stream_decoder, FLAC__uint64 offset, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
return ROMemoryStream_SetPosition(decoder->memory_stream, offset, MEMORYSTREAM_START) ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
}
static FLAC__StreamDecoderTellStatus ftell_wrapper(const FLAC__StreamDecoder *flac_stream_decoder, FLAC__uint64 *offset, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
*offset = ROMemoryStream_GetPosition(decoder->memory_stream);
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
static FLAC__StreamDecoderLengthStatus GetSize(const FLAC__StreamDecoder *flac_stream_decoder, FLAC__uint64 *length, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
const size_t old_offset = ROMemoryStream_GetPosition(decoder->memory_stream);
ROMemoryStream_SetPosition(decoder->memory_stream, 0, MEMORYSTREAM_END);
*length = ROMemoryStream_GetPosition(decoder->memory_stream);
ROMemoryStream_SetPosition(decoder->memory_stream, old_offset, MEMORYSTREAM_START);
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static FLAC__bool CheckEOF(const FLAC__StreamDecoder *flac_stream_decoder, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
const size_t offset = ROMemoryStream_GetPosition(decoder->memory_stream);
ROMemoryStream_SetPosition(decoder->memory_stream, 0, MEMORYSTREAM_END);
const size_t size = ROMemoryStream_GetPosition(decoder->memory_stream);
ROMemoryStream_SetPosition(decoder->memory_stream, offset, MEMORYSTREAM_START);
return (offset == size);
}
static FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *flac_stream_decoder, const FLAC__Frame *frame, const FLAC__int32* const buffer[], void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
FLAC__int16 *block_buffer_pointer = (FLAC__int16*)decoder->block_buffer;
for (unsigned int i = 0; i < frame->header.blocksize; ++i)
{
for (unsigned int j = 0; j < frame->header.channels; ++j)
{
const FLAC__int32 sample = buffer[j][i];
// Downscale/upscale to 16-bit
if (decoder->bits_per_sample < 16)
*block_buffer_pointer++ = (FLAC__int16)(sample << (16 - decoder->bits_per_sample));
else if (decoder->bits_per_sample > 16)
*block_buffer_pointer++ = (FLAC__int16)(sample >> (decoder->bits_per_sample - 16));
else
*block_buffer_pointer++ = (FLAC__int16)sample;
}
}
decoder->block_buffer_index = 0;
decoder->block_buffer_size = (block_buffer_pointer - (FLAC__int16*)decoder->block_buffer) / decoder->channel_count;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static void MetadataCallback(const FLAC__StreamDecoder *flac_stream_decoder, const FLAC__StreamMetadata *metadata, void *user)
{
(void)flac_stream_decoder;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
decoder->spec->sample_rate = metadata->data.stream_info.sample_rate;
decoder->spec->channel_count = decoder->channel_count = metadata->data.stream_info.channels;
decoder->spec->is_complex = false;
decoder->bits_per_sample = metadata->data.stream_info.bits_per_sample;
// Init block buffer
decoder->block_buffer = (unsigned char*)malloc(metadata->data.stream_info.max_blocksize * sizeof(FLAC__int16) * metadata->data.stream_info.channels);
decoder->block_buffer_index = 0;
decoder->block_buffer_size = 0;
}
static void ErrorCallback(const FLAC__StreamDecoder *flac_stream_decoder, FLAC__StreamDecoderErrorStatus status, void *user)
{
(void)flac_stream_decoder;
(void)status;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)user;
decoder->error = true;
}
void* Decoder_libFLAC_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)loop; // This is ignored in simple decoders
(void)wanted_spec;
Decoder_libFLAC *decoder = (Decoder_libFLAC*)malloc(sizeof(Decoder_libFLAC));
if (decoder != NULL)
{
decoder->flac_stream_decoder = FLAC__stream_decoder_new();
if (decoder->flac_stream_decoder != NULL)
{
decoder->memory_stream = ROMemoryStream_Create(data, data_size);
if (decoder->memory_stream != NULL)
{
if (FLAC__stream_decoder_init_stream(decoder->flac_stream_decoder, fread_wrapper, fseek_wrapper, ftell_wrapper, GetSize, CheckEOF, WriteCallback, MetadataCallback, ErrorCallback, decoder) == FLAC__STREAM_DECODER_INIT_STATUS_OK)
{
decoder->error = false;
decoder->spec = spec;
FLAC__stream_decoder_process_until_end_of_metadata(decoder->flac_stream_decoder);
if (!decoder->error)
return decoder;
FLAC__stream_decoder_finish(decoder->flac_stream_decoder);
}
ROMemoryStream_Destroy(decoder->memory_stream);
}
FLAC__stream_decoder_delete(decoder->flac_stream_decoder);
}
free(decoder);
}
return NULL;
}
void Decoder_libFLAC_Destroy(void *decoder_void)
{
Decoder_libFLAC *decoder = (Decoder_libFLAC*)decoder_void;
FLAC__stream_decoder_finish(decoder->flac_stream_decoder);
FLAC__stream_decoder_delete(decoder->flac_stream_decoder);
ROMemoryStream_Destroy(decoder->memory_stream);
free(decoder->block_buffer);
free(decoder);
}
void Decoder_libFLAC_Rewind(void *decoder_void)
{
Decoder_libFLAC *decoder = (Decoder_libFLAC*)decoder_void;
FLAC__stream_decoder_seek_absolute(decoder->flac_stream_decoder, 0);
}
size_t Decoder_libFLAC_GetSamples(void *decoder_void, short *buffer, size_t frames_to_do)
{
Decoder_libFLAC *decoder = (Decoder_libFLAC*)decoder_void;
if (decoder->block_buffer_index == decoder->block_buffer_size)
{
FLAC__stream_decoder_process_single(decoder->flac_stream_decoder);
if (FLAC__stream_decoder_get_state(decoder->flac_stream_decoder) == FLAC__STREAM_DECODER_END_OF_STREAM)
return 0;
}
const unsigned int SIZE_OF_FRAME = sizeof(FLAC__int16) * decoder->channel_count;
const unsigned long block_frames_to_do = MIN(frames_to_do, decoder->block_buffer_size - decoder->block_buffer_index);
memcpy(buffer, &decoder->block_buffer[decoder->block_buffer_index * SIZE_OF_FRAME], block_frames_to_do * SIZE_OF_FRAME);
decoder->block_buffer_index += block_frames_to_do;
return block_frames_to_do;
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_libFLAC_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_libFLAC_Destroy(void *decoder);
void Decoder_libFLAC_Rewind(void *decoder);
size_t Decoder_libFLAC_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

View file

@ -0,0 +1,102 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "libopenmpt.h"
#include <stddef.h>
#include <stdlib.h>
#include <libopenmpt/libopenmpt.h>
#include "common.h"
#define SAMPLE_RATE 48000
#define CHANNEL_COUNT 2
typedef struct Decoder_libOpenMPT
{
openmpt_module *module;
unsigned long sample_rate;
} Decoder_libOpenMPT;
void* Decoder_libOpenMPT_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)wanted_spec;
Decoder_libOpenMPT *decoder = (Decoder_libOpenMPT*)malloc(sizeof(Decoder_libOpenMPT));
if (decoder != NULL)
{
unsigned long sample_rate = wanted_spec->sample_rate == 0 ? SAMPLE_RATE : wanted_spec->sample_rate;
decoder->module = openmpt_module_create_from_memory2(data, data_size, openmpt_log_func_silent, NULL, openmpt_error_func_ignore, NULL, NULL, NULL, NULL);
decoder->sample_rate = sample_rate;
if (decoder->module != NULL)
{
spec->sample_rate = sample_rate;
spec->channel_count = CHANNEL_COUNT;
spec->is_complex = true;
if (loop)
openmpt_module_set_repeat_count(decoder->module, -1);
return decoder;
}
free(decoder);
}
return NULL;
}
void Decoder_libOpenMPT_Destroy(void *decoder_void)
{
Decoder_libOpenMPT *decoder = (Decoder_libOpenMPT*)decoder_void;
openmpt_module_destroy(decoder->module);
free(decoder);
}
void Decoder_libOpenMPT_Rewind(void *decoder_void)
{
Decoder_libOpenMPT *decoder = (Decoder_libOpenMPT*)decoder_void;
openmpt_module_set_position_seconds(decoder->module, 0);
}
size_t Decoder_libOpenMPT_GetSamples(void *decoder_void, short *buffer, size_t frames_to_do)
{
Decoder_libOpenMPT *decoder = (Decoder_libOpenMPT*)decoder_void;
size_t frames_done = 0;
while (frames_done != frames_to_do)
{
size_t frames = openmpt_module_read_interleaved_stereo(decoder->module, decoder->sample_rate, frames_to_do - frames_done, &buffer[frames_done * CHANNEL_COUNT]);
if (frames == 0)
break;
frames_done += frames;
}
return frames_done;
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_libOpenMPT_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_libOpenMPT_Destroy(void *decoder);
void Decoder_libOpenMPT_Rewind(void *decoder);
size_t Decoder_libOpenMPT_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

View file

@ -0,0 +1,59 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "libopus.h"
#include <stddef.h>
#include <opus/opusfile.h>
#include "common.h"
void* Decoder_libOpus_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec)
{
(void)loop; // This is ignored in simple decoders
(void)wanted_spec;
OggOpusFile *backend = op_open_memory(data, data_size, NULL);
if (backend != NULL)
{
spec->sample_rate = 48000; // libopusfile always outputs at 48kHz (https://opus-codec.org/docs/opusfile_api-0.7/structOpusHead.html#a73b80a913eca33d829f1667caee80d9e)
spec->channel_count = 2; // We use op_read_float_stereo, so libopusfile will handle conversion if it needs to
spec->is_complex = false;
}
return backend;
}
void Decoder_libOpus_Destroy(void *decoder)
{
op_free((OggOpusFile*)decoder);
}
void Decoder_libOpus_Rewind(void *decoder)
{
op_pcm_seek((OggOpusFile*)decoder, 0);
}
size_t Decoder_libOpus_GetSamples(void *decoder, short *buffer, size_t frames_to_do)
{
return op_read_stereo((OggOpusFile*)decoder, buffer, frames_to_do * 2); // You tell *me* why that last parameter is in samples and not frames
}

View file

@ -0,0 +1,30 @@
/*
* (C) 2019-2020 Clownacy
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#pragma once
#include <stddef.h>
#include "common.h"
void* Decoder_libOpus_Create(const unsigned char *data, size_t data_size, bool loop, const DecoderSpec *wanted_spec, DecoderSpec *spec);
void Decoder_libOpus_Destroy(void *decoder);
void Decoder_libOpus_Rewind(void *decoder);
size_t Decoder_libOpus_GetSamples(void *decoder, short *buffer, size_t frames_to_do);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
Stable versions
---------------
4.4.1 (20161012):
- fix MacOS Tiger build issues (reported by Misty De Meo)
- fix sample loop corner case (reported by knight-ryu12)
- fix set pan effect in multichannel MODs (reported by Leilei)
- fix global volume on module loop (reported by Travis Evans)
- fix IT pan right value (by NoSuck)
- fix memory leak in XMs with 256 patterns
- fix anticlick when rendering only one sample
4.4.0 (20160719):
Fix bugs caught in the OpenMPT test cases:
- fix XM arpeggio in FastTracker 2 compatible mode
- fix IT bidirectional loop sample length
- fix MOD vibrato and tremolo in Protracker compatible mode
Fix multichannel MOD issues reported by Leilei:
- fix XM replayer note delay and retrig quirk
- fix XM replayer channel pan
- fix MOD loader period to note conversion
Fix issues reported by Lionel Debroux:
- fix virtual channel deallocation error handling
- fix S3M global volume effect
- fix IT envelope reset on tone portamento
- fix IT voice leak caused by disabled envelope
- fix IT volume column tone portamento
- fix XM envelope position setting
- fix FT2 arpeggio+portamento quirk with finetunes
- fix mixer anticlick routines
- accept S3M modules with invalid effects
Other changes:
- fix S3M channel reset on sample end (reported by Alexander Null)
- fix Noisetracker MOD speed setting (reported by Tero Auvinen)
- fix IT loader DCA sanity check (reported by Paul Gomez Givera)
- fix IT envelope reset after offset with portamento
- fix bidirectional sample interpolation
- fix mixer resampling and tuning issues
- add flags to configure player mode
- add option to set the maximum number of virtual channels
- add support to IT sample sustain loop
- code refactoring and cleanup
4.3.13 (20160417):
Fix bugs caught in the OpenMPT test cases:
- fix IT volume column fine volume slide with row delay
Other changes:
- fix MOD vs XM set finetune effect
- fix IT old instrument volume
- fix IT panbrello speed
- fix IT random pan variation left bias
- fix IT default pan in sample mode (reported by Hai Shalom)
- fix S3M set pan effect (reported by Hai Shalom and Johannes Schultz)
- code refactoring and cleanup
4.3.12 (20150305):
Fix bugs caught in the OpenMPT test cases:
- fix IT note off with instrument
- fix IT note recover after cut
- fix IT instrument without note after note cut event
- fix IT pan reset on new note instead of new instrument
- fix IT volume swing problems
- fix XM glissando effect
- fix Scream Tracker 3 period limits
- fix Scream Tracker 3 tremolo memory
Other changes:
- fix IT pattern break in hexadecimal (reported by StarFox008)
- fix S3M subsong detection (reported by knight-ryu12)
- fix S3M/IT special marker handling (reported by knight-ryu12)
- fix tone portamento memory without note (reported by NoSuck)
- fix IT pan swing limits
4.3.11 (20160212):
Fix bugs caught in the OpenMPT test cases:
- fix FT2 XM arpeggio clamp
- fix FT2 XM arpeggio + pitch slide
- fix XM tremor effect handling
- fix XM tremor recover after volume setting
- fix IT instrument after keyoff
- fix S3M first frame test in pattern delay
- fix Protracker tone portamento target setting
- fix Protracker arpeggio wraparound
- fix Protracker finetune setting
Other changes:
- fix Visual C++ build (reported by Jochen Goernitz)
- fix invalid sample offset handling in Skale Tracker XM (reported by
Vladislav Suschikh)
- fix Protracker sample loop to use full repeat only if start is 0
- fix lite build with IT support disabled
4.3.10 (20151231):
Fix bugs reported by Coverity Scan:
- fix out of bounds access in IT/XM envelopes
- fix negative array index read when setting position
- fix resource leak in module load error handling
- add sanity check to smix sample loading
- add error handling to many I/O operations
- remove dead code in virtual channel manager reset
- remove unnecessary seeks in format loaders
- prevent division by zero in memory I/O
Other changes:
- fix IT envelope release + fadeout (reported by NoSuck)
- fix IT autovibrato depth (reported by Travis Evans)
- fix tone portamento target setting (reported by Georgy Lomsadze)
- disable ST3 sample size limit (reported by Jochen Goernitz)
4.3.9 (20150623):
Fix bugs caught in the OpenMPT test cases:
- fix IT tone portamento on sample change and NNA
- fix IT tone portamento with offset
Fix problems caused by fuzz files (reported by Jonathan Neuschäfer):
- add sanity check to IT instrument name loader
- add sanity check to IT loader instrument mapping
- initialize IT loader last event data
Other changes:
- detect Amiga frequency limits in MOD (reported by Mirko Buffoni)
- fix global volume on restart to invalid row (reported by Adam Purkrt)
- fix external sample mixer for IT files (reported by honguito98)
- allow short sample reads (reported by Adam Purkrt)
- address problems reported by clang sanitizer
4.3.8 (20150404):
- fix pre-increment of envelope indexes
- fix IT note release at end of envelope sustain loop
- reset channel flags in case of delay effect
- refactor XM envelopes
- refactor IT envelopes
4.3.7 (20150329):
- fix IT sample mode note cut on invalid sample
- fix IT sample mode note end detection
- fix IT envelope handling with carry and fadeout
- fix IT tone portamento with sample changes
- fix IT initial global volume setting
- fix IT keyoff with instrument in old effects mode
- fix IT filter maximum values with resonance
- fix IT random volume variation
- fix pattern initialization sanity check
- fix ++ pattern handling in IT loader (reported by honguito98)
- add IT high offset command (SAx)
- add IT surround command (S9x)
- add IT surround channel support
- add IT sample pan setting support
4.3.6 (20150322):
- fix IT volume column volume slide effect memory
- fix IT default filter cutoff on new note
- fix IT filter envelope memory
- add sanity check for IT old instrument loading
- fix instrument number in channel initialization
- fix sample size limit (reported by Jochen Goernitz)
- fix loading of OpenMPT 1.17 IT modules (reported by Dane Bush)
- fix XM loading for MED2XM modules (reported by Lorence Lombardo)
- add IT random volume variation
- add IT random pan variation
4.3.5 (20150207):
- add sanity check for ST3 S3M maximum sample size
- add sanity check for sample loop start
- add sanity check for speed 0
- add sanity check for invalid XM effects
- add sanity check for maximum number of channels
- add sanity check for number of points in IT envelope
- add sanity check for S3M file format information
- add sanity check for maximum sample size
- add sanity check for invalid envelope points
- add sanity check for basic module parameters
- add sanity check for instrument release after load error
- add sanity check for XM header size
- add sanity check for XM/IT/S3M parameters and sample size
- fix mixer index overflow with large samples
- fix crash on attempt to play invalid sample
- fix infinite loop in break+delay quirk
- reset module data before loading module
- fix loop processing error in scan (reported by Lionel Debroux)
- fix sample loop adjustment (by Emmanuel Julien)
4.3.4 (20150111):
- fix XM keyoff+delay combinations
- fix XM fine pitch slide with pattern delay
- fix XM vibrato rampdown waveform
- fix XM volume column pan with keyoff and delay
- fix XM pan envelope position setting
- fix channel volume and instrument initialization
- fix end of module detection inside a loop
- fix overflow in linear interpolator (reported by Jochen Goernitz)
- fix big-endian detection in configuration (by Andreas Schwab)
4.3.3 (20141231):
- fix XM note delay volume with no note or instrument set
- fix XM out-of-range note delays with pattern delays
- fix XM envelope loop length (reported by Per Törner)
4.3.2 (20141130):
- fix IT invalid instrument number recovery
- fix IT note retrig on portamento with same sample
- fix XM portamento target reset on new instrument
- fix XM portamento with offset
- fix XM pan slide memory
- fix XM tremolo and vibrato waveforms
- fix MOD pattern break with pattern delay
- fix MOD Protracker offset bug emulation
- fix tremolo rate
- fix IT portamento after keyoff and note end
- fix IT fadeout reset on new note
- fix IT pattern row delay scan
- fix MOD/XM volume up+down priority (reported by Jason Gibson)
- fix MOD fine volume slide memory (reported by Dennis Lindroos)
- fix set sample offset effect (by Dennis Lindroos)
- add emulation of the FT2 pattern loop bug (by Eugene Toder)
- code cleanup
4.3.1 (20141111):
- fix IT filter envelope range
- fix IT envelope carry after envelope end
- fix IT tone portamento in first note (reported by Jan Engelhardt)
- fix XM note off with volume command
- fix XM K00 effect handling
- fix XM portamento with volume column portamento
- fix XM keyoff with instrument
- fix XM note limits
- fix XM invalid memory access in event reader
- fix MOD period range enforcing (reported by Jason Gibson)
- fix corner case memory leak in S3M loader
- fix retrig of single-shot samples after the end of the sample
- fix crash in envelope reset with invalid instrument
- fix module titles and instrument names in Mac OS X
- fix row delay initialization on new module
4.3.0 (20140927):
- rebranded as libxmp-lite
- build from the same source tree as the full libxmp
- fix fine volume slide memory
- fix IT portamento after note end in sample mode
- fix S3M portamento after note end
- add XM and IT envelope loop and sustain point quirk
- fix Amiga limits for notes with finetune
- fix XM invalid offset handling
- fix XM note release reset on new volume
- fix XM pattern loader to honor header size
- fix XM fine volume slide effect memory
- fix XM fine pitch slide effect memory
- fix XM finetune effect
- fix IT portamento if offset effect is used
- fix IT NNA on invalid sample mapping
- fix IT filter envelope index reset
- fix IT envelope carry on note cut events
- fix IT envelope reset on new instrument
- fix IT instrument change on portamento in compatible GXX mode
- fix IT unmapped sample parsing
- fix IT filter cutoff reset
- add API call to load a module from a file handle
- add API call to set default pan separation value
- refactor memory I/O calls
- fix segfault in mixer caused by sample position overflow
- fix XM, S3M, IT and MED offset effect handling
- fix IT fadeout and envelope reset on new virtual channel
- fix S3M shared effect parameter memory
- fix S3M default pan positions
- fix S3M set BPM effect with values < 32 (reported by Kyu S.)
- fix loop counter reset on play buffer reset
- fix finetune effect
- fix sequence number reset on player start
- fix stray notes in XM (reported by Andreas Argirakis)
- limit note number to avoid crash (reported by Bastian Pflieger)
4.2.7 (20140412):
- fix library name in pkg-config file (by Chris Spiegel)
- fix XM set pan effect
- fix IT disabled instrument pan
4.2.6 (20140407):
- add configure option to disable IT support
- minor Visual C++ port fixes
- add Visual C++ nmake Makefile
- fix double free in module loaded from memory (by Arnaud Troël)
- fix Win64 portability issues (reported by Ozkan Sezer)
- fix IT tempo slide effect
- generate Android NDK static libraries
4.2.0 (20140302):
- forked from libxmp 4.2.5 and removed unnecessary features

View file

@ -0,0 +1,36 @@
Requirements
------------
- This package needs to be built using GNU make
(https://www.gnu.org/software/make/). On BSD or SysV systems, you may
need to use "gmake" instead of "make".
Building the library
--------------------
In most systems just execute:
$ ./configure
$ make
We'll build the shared library by default. Use ./configure --enable-static
to build the static library. To check if the library was correctly built,
run:
$ make check
Use ./configure --help to see more options.
Installation
------------
To install the library and development components, just run:
# make install
as the superuser. This will install the shared and static libraries,
header file and pkg-config file into directories under /usr/local or a
different location selected with the --prefix option in configure.

View file

@ -0,0 +1,207 @@
VERSION_MAJOR = 4
VERSION_MINOR = 4
VERSION_RELEASE = 1
VERSION = $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_RELEASE)
# The dylib will support anything linked against COMPAT_VERSION through VERSION
COMPAT_VERSION = $(VERSION_MAJOR)
CC = @CC@
CFLAGS = -c @CFLAGS@ @DEFS@ -D_REENTRANT -DLIBXMP_CORE_PLAYER
LD = @CC@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
RANLIB = @RANLIB@
INSTALL = @INSTALL@
DESTDIR =
prefix = @prefix@
exec_prefix = @prefix@
datarootdir = @datarootdir@
BINDIR = @bindir@
LIBDIR = @libdir@
MANDIR = @mandir@/man3
INCLUDEDIR = @includedir@/libxmp-lite
LD_VERSCRIPT = @LD_VERSCRIPT@
SHELL = /bin/sh
DIST = libxmp-lite-$(VERSION)
DFILES = README INSTALL Changelog install-sh configure configure.ac \
config.sub config.guess Makefile.in libxmp-lite.pc.in \
Makefile.vc libxmp.map
DDIRS = include src loaders test
V = 0
LIB = libxmp-lite.a
SOLIB = libxmp-lite.so
SHLIB = $(SOLIB).$(VERSION)
SONAME = $(SOLIB).$(VERSION_MAJOR)
DLL = libxmp-lite.dll
DYLIB = libxmp-lite.$(VERSION_MAJOR).dylib
GCLIB = libxmp-lite-gc.a
all: @STATIC@ @SHARED@
include include/libxmp-lite/Makefile
include src/Makefile
include src/loaders/Makefile
include test/Makefile
LOBJS = $(OBJS:.o=.lo)
GCOBJS = $(OBJS:.o=.gco)
CFLAGS += -Iinclude/libxmp-lite -Isrc
.SUFFIXES: .c .o .lo .a .so .dll .in .gco .gcda .gcno
.c.o:
@CMD='$(CC) $(CPPFLAGS) $(CFLAGS) -o $*.o $<'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo CC $*.o ; fi; \
eval $$CMD
.c.lo:
@CMD='$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC -o $*.lo $<'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo CC $*.lo ; fi; \
eval $$CMD
.c.gco:
@CMD='$(CC) $(CPPFLAGS) $(CFLAGS) -O0 -fno-inline -fprofile-arcs -ftest-coverage -o $*.gco $<'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo CC $*.gco ; fi; \
eval $$CMD
static: lib/$(LIB)
shared: lib/$(SHLIB)
coverage: lib/$(GCLIB)
dll: lib/$(DLL)
dylib: lib/$(DYLIB)
lib/$(LIB): $(OBJS)
@mkdir -p lib
@CMD='$(AR) r $@ $(OBJS)'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo AR $@ ; fi; \
eval $$CMD
$(RANLIB) $@
lib/$(SHLIB): $(LOBJS)
@mkdir -p lib
@CMD='$(LD) $(LDFLAGS) -shared -Wl,-soname,$(SONAME) $(LD_VERSCRIPT) -o $@ $(LOBJS) $(LIBS)'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo LD $@ ; fi; \
eval $$CMD
ln -sf $(SHLIB) lib/$(SONAME)
ln -sf $(SONAME) lib/$(SOLIB)
lib/$(DLL): $(OBJS)
@mkdir -p lib
@CMD='$(LD) $(LDFLAGS) -shared -Wl,--output-def,lib/libxmp-lite.def,--out-implib,lib/libxmp-lite_dll.a -o $@ $(OBJS) $(LIBS)'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo LD $@ ; fi; \
eval $$CMD
# From http://stackoverflow.com/questions/15905310:
# The version number checks that dyld performs are limited to ensuring that
# the compatibility version of the library being loaded is higher than the
# compatibility version of the library that was used at build time.
lib/$(DYLIB): $(LOBJS)
@mkdir -p lib
@CMD='$(LD) $(LDFLAGS) -dynamiclib -Wl,-headerpad_max_install_names,-undefined,dynamic_lookup,-compatibility_version,$(COMPAT_VERSION),-current_version,$(VERSION),-install_name,$(prefix)/lib/$(DYLIB) -o $@ $(LOBJS) $(LIBS)'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo LD $@ ; fi; \
eval $$CMD
ln -sf $(DYLIB) lib/libxmp-lite.dylib
lib/$(GCLIB): $(GCOBJS)
@mkdir -p lib
@CMD='$(AR) r $@ $(GCOBJS)'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo AR $@ ; fi; \
eval $$CMD
$(RANLIB) $@
clean:
@rm -f $(OBJS) $(LOBJS) lib/lib*
@rm -f $(GCOBJS) $(OBJS:.o=.gcno) $(OBJS:.o=.gcda)
install: all
@mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(INCLUDEDIR)
@if [ -f lib/$(LIB) ]; then \
echo "Installing $(LIB)..."; \
$(INSTALL) -m644 lib/$(LIB) $(DESTDIR)$(LIBDIR); \
fi
@if [ -f lib/$(DYLIB) ]; then \
echo "Installing $(DYLIB)..."; \
$(INSTALL) -m755 lib/$(DYLIB) $(DESTDIR)$(LIBDIR); \
ln -sf $(DYLIB) $(DESTDIR)$(LIBDIR)/libxmp-lite.dylib; \
fi
@if [ -f lib/$(SHLIB) ]; then \
echo "Installing $(SHLIB)..."; \
$(INSTALL) -m644 lib/$(SHLIB) $(DESTDIR)$(LIBDIR); \
ln -sf $(SHLIB) $(DESTDIR)$(LIBDIR)/$(SONAME); \
ln -sf $(SONAME) $(DESTDIR)$(LIBDIR)/$(SOLIB); \
fi
@echo "Installing xmp.h..."
@$(INSTALL) -m644 include/libxmp-lite/xmp.h $(DESTDIR)$(INCLUDEDIR)
@echo "Installing libxmp-lite.pc..."
@mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
@$(INSTALL) -m644 libxmp-lite.pc $(DESTDIR)$(LIBDIR)/pkgconfig/
depend:
@echo Building dependencies...
@echo > $@
@for i in $(OBJS) $(T_OBJS); do \
c="$${i%.o}.c"; l="$${i%.o}.lo"; \
$(CC) $(CFLAGS) -MM $$c | \
sed "s!^.*\.o:!$$i $$l:!" >> $@ ; \
done
dist: version-prepare dist-prepare vc-prepare dist-jni dist-subdirs
dist-jni:
mkdir $(DIST)/jni
cp jni/Android.mk jni/Application.mk $(DIST)/jni
dist-prepare:
./config.status
rm -Rf $(DIST) $(DIST).tar.gz
mkdir -p $(DIST)
cp -RPp $(DFILES) $(DIST)/
vc-prepare:
@echo Generate Makefile.vc
@sed -e 's!@OBJS@!$(subst /,\\,$(OBJS:.o=.obj))!' Makefile.vc.in > Makefile.vc
dist-subdirs: $(addprefix dist-,$(DDIRS))
chmod -R u+w $(DIST)/*
tar cvf - $(DIST) | gzip -9c > $(DIST).tar.gz
rm -Rf $(DIST)
ls -l $(DIST).tar.gz
distcheck:
rm -Rf $(DIST)
tar xf $(DIST).tar.gz
(cd $(DIST); ./configure --enable-static --prefix=`pwd`/test-install; make; make check; make install; find test-install)
devcheck:
$(MAKE) -C test-dev
covercheck: coverage
$(MAKE) -C test-dev covercheck
coverclean:
rm -f $(OBJS:.o=.gco) $(OBJS:.o=.gcno) $(OBJS:.o=.gcda)
$(MAKE) -C test-dev coverclean
$(OBJS): Makefile
$(LOBJS): Makefile
version-prepare:
sed -i -e '/^Version: /s/:.*/: $(VERSION)/g' libxmp-lite.pc.in
vercode=`printf "0x%02x%02x%02x" $(VERSION_MAJOR) $(VERSION_MINOR) $(VERSION_RELEASE)`; \
sed -i -e "s/\(^#define XMP_VERSION\).*/\1 \"$(VERSION)\"/;s/\(^#define XMP_VERCODE\).*/\1 $$vercode/;s/\(^#define XMP_VER_MAJOR\).*/\1 $(VERSION_MAJOR)/;s/\(^#define XMP_VER_MINOR\).*/\1 $(VERSION_MINOR)/;s/\(^#define XMP_VER_RELEASE\).*/\1 $(VERSION_RELEASE)/" include/libxmp-lite/xmp.h
./config.status
sinclude depend

View file

@ -0,0 +1,26 @@
CC = cl
CFLAGS = /O2 /Iinclude\libxmp-lite /Isrc /DBUILDING_DLL /DWIN32 \
/Dinline=__inline /D_USE_MATH_DEFINES /DLIBXMP_CORE_PLAYER
LD = link
LDFLAGS = /DLL /RELEASE /OUT:$(DLL)
DLL = libxmp-lite.dll
OBJS = src\virtual.obj src\format.obj src\period.obj src\player.obj src\read_event.obj src\dataio.obj src\lfo.obj src\scan.obj src\control.obj src\filter.obj src\effects.obj src\mixer.obj src\mix_all.obj src\load_helpers.obj src\load.obj src\hio.obj src\smix.obj src\memio.obj src\loaders\common.obj src\loaders\itsex.obj src\loaders\sample.obj src\loaders\xm_load.obj src\loaders\mod_load.obj src\loaders\s3m_load.obj src\loaders\it_load.obj
TEST = test\md5.obj test\test.obj
.c.obj:
$(CC) /c $(CFLAGS) /Fo$*.obj $<
all: $(DLL)
$(DLL): $(OBJS)
$(LD) $(LDFLAGS) $(OBJS)
clean:
del $(OBJS) $(DLL) *.lib *.exp
check: $(TEST)
$(LD) /RELEASE /OUT:test\libxmp-lite-test.exe $(TEST) libxmp-lite.lib
copy libxmp-lite.dll test
cd test & libxmp-lite-test

View file

@ -0,0 +1,42 @@
__ _ __ ___ __
/ / (_) / __ __ __ _ ___ ____/ (_) /____
/ /__/ / _ \\ \ // ' \/ _ \/___/ / / __/ -_)
/____/_/_.__/_\_\/_/_/_/ .__/ /_/_/\__/\__/
/_/
Libxmp-lite is a lean and lightweight subset of Libxmp that plays MOD, S3M,
XM, and IT modules and retains full compatibility with the original API.
It's intended for games and small or embedded applications where module
format diversity and file depacking are not required.
Library size can be further reduced by disabling Impulse Tracker format
support (configure with --disable-it). This option will also disable IT
effects and lowpass filtering.
Please refer to http://xmp.sf.net/libxmp.html for details on the current
Libxmp API.
LICENSE
Extended Module Player Lite
Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,118 @@
dnl AC_CONFIG_AUX_DIR(./scripts)
AC_INIT
AC_ARG_ENABLE(it, [ --disable-it Don't build IT format support])
AC_ARG_ENABLE(static, [ --enable-static Build static library])
AC_ARG_ENABLE(shared, [ --disable-shared Don't build shared library])
AC_SUBST(LD_VERSCRIPT)
AC_CANONICAL_HOST
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_RANLIB
AC_C_BIGENDIAN
dnl XMP_TRY_COMPILE(<message>,<cache-var>,<flags>,<program>,<ifyes>,<ifno>)
AC_DEFUN([XMP_TRY_COMPILE],[
AC_CACHE_CHECK([$1],[$2],[
oldcflags="${CFLAGS}"
CFLAGS="${CFLAGS} $3"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[$4]])],[$2=yes],[$2=no],[true])
CFLAGS="${oldcflags}"])
AS_IF([test "x$$2" = xyes], [$5], [$6])])
AC_DEFUN([AC_CHECK_DEFINED],[
AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$1])dnl
AC_CACHE_CHECK([for $1 defined], ac_var,
AC_TRY_COMPILE(,[
#ifdef $1
int ok;
#else
choke me
#endif
],AS_VAR_SET(ac_var, yes),AS_VAR_SET(ac_var, no)))
AS_IF([test AS_VAR_GET(ac_var) != "no"], [$2], [$3])dnl
AS_VAR_POPDEF([ac_var])dnl
])
case "$host_cpu" in
powerpc64)
CFLAGS="${CFLAGS} -m64"
LDFLAGS="${LDFLAGS} -m64"
;;
esac
case "${host_os}" in
cygwin*)
enable_static=yes
enable_shared=no
;;
esac
if test "${enable_static}" = yes; then
AC_SUBST(STATIC,static)
fi
if test "${enable_shared}" != no; then
AC_CHECK_DEFINED(_WIN32,[
AC_SUBST(SHARED,dll)
CFLAGS="${CFLAGS} -DBUILDING_DLL"
],[
AC_CHECK_DEFINED(__APPLE__,[
AC_SUBST(SHARED,dylib)
],[
AC_SUBST(SHARED,shared)
])
])
fi
if test "${enable_it}" = no; then
CFLAGS="${CFLAGS} -DLIBXMP_CORE_DISABLE_IT"
fi
XMP_TRY_COMPILE(whether compiler understands -Wall,
ac_cv_c_flag_w_all,
-Wall,[
int main(){}],
CFLAGS="${CFLAGS} -Wall")
XMP_TRY_COMPILE(whether compiler understands -xldscope=hidden,
ac_cv_c_flag_xldscope_hidden,
-xldscope=hidden,[
int main(){}],
[LDFLAGS="${LDFLAGS} -xldscope=hidden"],
[XMP_TRY_COMPILE(whether compiler understands -fvisibility=hidden,
ac_cv_c_flag_f_visibility_hidden,
-fvisibility=hidden,[
int main(){}],
[LDFLAGS="${LDFLAGS} -fvisibility=hidden"
LD_VERSCRIPT="-Wl,--version-script,libxmp.map"])]
)
dnl for clang
XMP_TRY_COMPILE(whether compiler understands -Wunknown-warning-option,
ac_cv_c_flag_w_unknown_warning_option,
-Wunknown-warning-option,[
int main(){}],
CFLAGS="${CFLAGS} -Wno-unknown-warning-option")
XMP_TRY_COMPILE(whether compiler understands -Wunused-but-set-variable,
ac_cv_c_flag_w_unused_but_set_variable,
-Wunused-but-set-variable,[
int main(){}],
CFLAGS="${CFLAGS} -Wno-unused-but-set-variable")
XMP_TRY_COMPILE(whether compiler understands -Wunused-result,
ac_cv_c_flag_w_unused_result,
-Wunused-result,[
int main(){}],
CFLAGS="${CFLAGS} -Wno-unused-result")
XMP_TRY_COMPILE(whether compiler understands -Warray-bounds,
ac_cv_c_flag_w_array_bounds,
-Warray-bounds,[
int main(){}],
CFLAGS="${CFLAGS} -Wno-array-bounds")
AC_CHECK_LIB(m,pow)
AC_CHECK_FUNCS(strlcpy strlcat)
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([libxmp-lite.pc])
AC_OUTPUT

View file

@ -0,0 +1,12 @@
INCLUDE_DFILES = Makefile xmp.h
INCLUDE_PATH = include/libxmp-lite
install-include:
$(INSTALL_DATA) xmp.h $(DESTDIR)$(INCLUDEDIR)
dist-include:
mkdir -p $(DIST)/$(INCLUDE_PATH)
cp -RPp $(addprefix $(INCLUDE_PATH)/,$(INCLUDE_DFILES)) $(DIST)/$(INCLUDE_PATH)

View file

@ -0,0 +1,361 @@
#ifndef XMP_H
#define XMP_H
#ifdef __cplusplus
extern "C" {
#endif
#define XMP_VERSION "4.4.1"
#define XMP_VERCODE 0x040401
#define XMP_VER_MAJOR 4
#define XMP_VER_MINOR 4
#define XMP_VER_RELEASE 1
#define EXPORT
/*
#if defined(_WIN32) && !defined(__CYGWIN__)
# if defined(BUILDING_STATIC)
# define EXPORT
# elif defined(BUILDING_DLL)
# define EXPORT __declspec(dllexport)
# else
# define EXPORT __declspec(dllimport)
# endif
#elif __GNUC__ >= 4 || defined(__HP_cc)
# define EXPORT __attribute__((visibility ("default")))
#elif defined(__SUNPRO_C)
# define EXPORT __global
#elif defined(EMSCRIPTEN)
# define EXPORT EMSCRIPTEN_KEEPALIVE
#else
# define EXPORT
#endif
*/
#define XMP_NAME_SIZE 64 /* Size of module name and type */
#define XMP_KEY_OFF 0x81 /* Note number for key off event */
#define XMP_KEY_CUT 0x82 /* Note number for key cut event */
#define XMP_KEY_FADE 0x83 /* Note number for fade event */
/* mixer parameter macros */
/* sample format flags */
#define XMP_FORMAT_8BIT (1 << 0) /* Mix to 8-bit instead of 16 */
#define XMP_FORMAT_UNSIGNED (1 << 1) /* Mix to unsigned samples */
#define XMP_FORMAT_MONO (1 << 2) /* Mix to mono instead of stereo */
/* player parameters */
#define XMP_PLAYER_AMP 0 /* Amplification factor */
#define XMP_PLAYER_MIX 1 /* Stereo mixing */
#define XMP_PLAYER_INTERP 2 /* Interpolation type */
#define XMP_PLAYER_DSP 3 /* DSP effect flags */
#define XMP_PLAYER_FLAGS 4 /* Player flags */
#define XMP_PLAYER_CFLAGS 5 /* Player flags for current module */
#define XMP_PLAYER_SMPCTL 6 /* Sample control flags */
#define XMP_PLAYER_VOLUME 7 /* Player module volume */
#define XMP_PLAYER_STATE 8 /* Internal player state (read only) */
#define XMP_PLAYER_SMIX_VOLUME 9 /* SMIX volume */
#define XMP_PLAYER_DEFPAN 10 /* Default pan setting */
#define XMP_PLAYER_MODE 11 /* Player personality */
#define XMP_PLAYER_MIXER_TYPE 12 /* Current mixer (read only) */
#define XMP_PLAYER_VOICES 13 /* Maximum number of mixer voices */
/* interpolation types */
#define XMP_INTERP_NEAREST 0 /* Nearest neighbor */
#define XMP_INTERP_LINEAR 1 /* Linear (default) */
#define XMP_INTERP_SPLINE 2 /* Cubic spline */
/* dsp effect types */
#define XMP_DSP_LOWPASS (1 << 0) /* Lowpass filter effect */
#define XMP_DSP_ALL (XMP_DSP_LOWPASS)
/* player state */
#define XMP_STATE_UNLOADED 0 /* Context created */
#define XMP_STATE_LOADED 1 /* Module loaded */
#define XMP_STATE_PLAYING 2 /* Module playing */
/* player flags */
#define XMP_FLAGS_VBLANK (1 << 0) /* Use vblank timing */
#define XMP_FLAGS_FX9BUG (1 << 1) /* Emulate FX9 bug */
#define XMP_FLAGS_FIXLOOP (1 << 2) /* Emulate sample loop bug */
#define XMP_FLAGS_A500 (1 << 3) /* Use Paula mixer in Amiga modules */
/* player modes */
#define XMP_MODE_AUTO 0 /* Autodetect mode (default) */
#define XMP_MODE_MOD 1 /* Play as a generic MOD player */
#define XMP_MODE_NOISETRACKER 2 /* Play using Noisetracker quirks */
#define XMP_MODE_PROTRACKER 3 /* Play using Protracker quirks */
#define XMP_MODE_S3M 4 /* Play as a generic S3M player */
#define XMP_MODE_ST3 5 /* Play using ST3 bug emulation */
#define XMP_MODE_ST3GUS 6 /* Play using ST3+GUS quirks */
#define XMP_MODE_XM 7 /* Play as a generic XM player */
#define XMP_MODE_FT2 8 /* Play using FT2 bug emulation */
#define XMP_MODE_IT 9 /* Play using IT quirks */
#define XMP_MODE_ITSMP 10 /* Play using IT sample mode quirks */
/* mixer types */
#define XMP_MIXER_STANDARD 0 /* Standard mixer */
#define XMP_MIXER_A500 1 /* Amiga 500 */
#define XMP_MIXER_A500F 2 /* Amiga 500 with led filter */
/* sample flags */
#define XMP_SMPCTL_SKIP (1 << 0) /* Don't load samples */
/* limits */
#define XMP_MAX_KEYS 121 /* Number of valid keys */
#define XMP_MAX_ENV_POINTS 32 /* Max number of envelope points */
#define XMP_MAX_MOD_LENGTH 256 /* Max number of patterns in module */
#define XMP_MAX_CHANNELS 64 /* Max number of channels in module */
#define XMP_MAX_SRATE 49170 /* max sampling rate (Hz) */
#define XMP_MIN_SRATE 4000 /* min sampling rate (Hz) */
#define XMP_MIN_BPM 20 /* min BPM */
/* frame rate = (50 * bpm / 125) Hz */
/* frame size = (sampling rate * channels * size) / frame rate */
#define XMP_MAX_FRAMESIZE (5 * XMP_MAX_SRATE * 2 / XMP_MIN_BPM)
/* error codes */
#define XMP_END 1
#define XMP_ERROR_INTERNAL 2 /* Internal error */
#define XMP_ERROR_FORMAT 3 /* Unsupported module format */
#define XMP_ERROR_LOAD 4 /* Error loading file */
#define XMP_ERROR_DEPACK 5 /* Error depacking file */
#define XMP_ERROR_SYSTEM 6 /* System error */
#define XMP_ERROR_INVALID 7 /* Invalid parameter */
#define XMP_ERROR_STATE 8 /* Invalid player state */
struct xmp_channel {
int pan; /* Channel pan (0x80 is center) */
int vol; /* Channel volume */
#define XMP_CHANNEL_SYNTH (1 << 0) /* Channel is synthesized */
#define XMP_CHANNEL_MUTE (1 << 1) /* Channel is muted */
#define XMP_CHANNEL_SPLIT (1 << 2) /* Split Amiga channel in bits 5-4 */
#define XMP_CHANNEL_SURROUND (1 << 4) /* Surround channel */
int flg; /* Channel flags */
};
struct xmp_pattern {
int rows; /* Number of rows */
int index[1]; /* Track index */
};
struct xmp_event {
unsigned char note; /* Note number (0 means no note) */
unsigned char ins; /* Patch number */
unsigned char vol; /* Volume (0 to basevol) */
unsigned char fxt; /* Effect type */
unsigned char fxp; /* Effect parameter */
unsigned char f2t; /* Secondary effect type */
unsigned char f2p; /* Secondary effect parameter */
unsigned char _flag; /* Internal (reserved) flags */
};
struct xmp_track {
int rows; /* Number of rows */
struct xmp_event event[1]; /* Event data */
};
struct xmp_envelope {
#define XMP_ENVELOPE_ON (1 << 0) /* Envelope is enabled */
#define XMP_ENVELOPE_SUS (1 << 1) /* Envelope has sustain point */
#define XMP_ENVELOPE_LOOP (1 << 2) /* Envelope has loop */
#define XMP_ENVELOPE_FLT (1 << 3) /* Envelope is used for filter */
#define XMP_ENVELOPE_SLOOP (1 << 4) /* Envelope has sustain loop */
#define XMP_ENVELOPE_CARRY (1 << 5) /* Don't reset envelope position */
int flg; /* Flags */
int npt; /* Number of envelope points */
int scl; /* Envelope scaling */
int sus; /* Sustain start point */
int sue; /* Sustain end point */
int lps; /* Loop start point */
int lpe; /* Loop end point */
short data[XMP_MAX_ENV_POINTS * 2];
};
struct xmp_instrument {
char name[32]; /* Instrument name */
int vol; /* Instrument volume */
int nsm; /* Number of samples */
int rls; /* Release (fadeout) */
struct xmp_envelope aei; /* Amplitude envelope info */
struct xmp_envelope pei; /* Pan envelope info */
struct xmp_envelope fei; /* Frequency envelope info */
struct {
unsigned char ins; /* Instrument number for each key */
signed char xpo; /* Instrument transpose for each key */
} map[XMP_MAX_KEYS];
struct xmp_subinstrument {
int vol; /* Default volume */
int gvl; /* Global volume */
int pan; /* Pan */
int xpo; /* Transpose */
int fin; /* Finetune */
int vwf; /* Vibrato waveform */
int vde; /* Vibrato depth */
int vra; /* Vibrato rate */
int vsw; /* Vibrato sweep */
int rvv; /* Random volume/pan variation (IT) */
int sid; /* Sample number */
#define XMP_INST_NNA_CUT 0x00
#define XMP_INST_NNA_CONT 0x01
#define XMP_INST_NNA_OFF 0x02
#define XMP_INST_NNA_FADE 0x03
int nna; /* New note action */
#define XMP_INST_DCT_OFF 0x00
#define XMP_INST_DCT_NOTE 0x01
#define XMP_INST_DCT_SMP 0x02
#define XMP_INST_DCT_INST 0x03
int dct; /* Duplicate check type */
#define XMP_INST_DCA_CUT XMP_INST_NNA_CUT
#define XMP_INST_DCA_OFF XMP_INST_NNA_OFF
#define XMP_INST_DCA_FADE XMP_INST_NNA_FADE
int dca; /* Duplicate check action */
int ifc; /* Initial filter cutoff */
int ifr; /* Initial filter resonance */
} *sub;
void *extra; /* Extra fields */
};
struct xmp_sample {
char name[32]; /* Sample name */
int len; /* Sample length */
int lps; /* Loop start */
int lpe; /* Loop end */
#define XMP_SAMPLE_16BIT (1 << 0) /* 16bit sample */
#define XMP_SAMPLE_LOOP (1 << 1) /* Sample is looped */
#define XMP_SAMPLE_LOOP_BIDIR (1 << 2) /* Bidirectional sample loop */
#define XMP_SAMPLE_LOOP_REVERSE (1 << 3) /* Backwards sample loop */
#define XMP_SAMPLE_LOOP_FULL (1 << 4) /* Play full sample before looping */
#define XMP_SAMPLE_SLOOP (1 << 5) /* Sample has sustain loop */
#define XMP_SAMPLE_SLOOP_BIDIR (1 << 6) /* Bidirectional sustain loop */
#define XMP_SAMPLE_SYNTH (1 << 15) /* Data contains synth patch */
int flg; /* Flags */
unsigned char *data; /* Sample data */
};
struct xmp_sequence {
int entry_point;
int duration;
};
struct xmp_module {
char name[XMP_NAME_SIZE]; /* Module title */
char type[XMP_NAME_SIZE]; /* Module format */
int pat; /* Number of patterns */
int trk; /* Number of tracks */
int chn; /* Tracks per pattern */
int ins; /* Number of instruments */
int smp; /* Number of samples */
int spd; /* Initial speed */
int bpm; /* Initial BPM */
int len; /* Module length in patterns */
int rst; /* Restart position */
int gvl; /* Global volume */
struct xmp_pattern **xxp; /* Patterns */
struct xmp_track **xxt; /* Tracks */
struct xmp_instrument *xxi; /* Instruments */
struct xmp_sample *xxs; /* Samples */
struct xmp_channel xxc[XMP_MAX_CHANNELS]; /* Channel info */
unsigned char xxo[XMP_MAX_MOD_LENGTH]; /* Orders */
};
struct xmp_test_info {
char name[XMP_NAME_SIZE]; /* Module title */
char type[XMP_NAME_SIZE]; /* Module format */
};
struct xmp_module_info {
unsigned char md5[16]; /* MD5 message digest */
int vol_base; /* Volume scale */
struct xmp_module *mod; /* Pointer to module data */
char *comment; /* Comment text, if any */
int num_sequences; /* Number of valid sequences */
struct xmp_sequence *seq_data; /* Pointer to sequence data */
};
struct xmp_frame_info { /* Current frame information */
int pos; /* Current position */
int pattern; /* Current pattern */
int row; /* Current row in pattern */
int num_rows; /* Number of rows in current pattern */
int frame; /* Current frame */
int speed; /* Current replay speed */
int bpm; /* Current bpm */
int time; /* Current module time in ms */
int total_time; /* Estimated replay time in ms*/
int frame_time; /* Frame replay time in us */
void *buffer; /* Pointer to sound buffer */
int buffer_size; /* Used buffer size */
int total_size; /* Total buffer size */
int volume; /* Current master volume */
int loop_count; /* Loop counter */
int virt_channels; /* Number of virtual channels */
int virt_used; /* Used virtual channels */
int sequence; /* Current sequence */
struct xmp_channel_info { /* Current channel information */
unsigned int period; /* Sample period (* 4096) */
unsigned int position; /* Sample position */
short pitchbend; /* Linear bend from base note*/
unsigned char note; /* Current base note number */
unsigned char instrument; /* Current instrument number */
unsigned char sample; /* Current sample number */
unsigned char volume; /* Current volume */
unsigned char pan; /* Current stereo pan */
unsigned char reserved; /* Reserved */
struct xmp_event event; /* Current track event */
} channel_info[XMP_MAX_CHANNELS];
};
typedef char *xmp_context;
EXPORT extern const char *xmp_version;
EXPORT extern const unsigned int xmp_vercode;
EXPORT xmp_context xmp_create_context (void);
EXPORT void xmp_free_context (xmp_context);
EXPORT int xmp_test_module (char *, struct xmp_test_info *);
EXPORT int xmp_load_module (xmp_context, char *);
EXPORT void xmp_scan_module (xmp_context);
EXPORT void xmp_release_module (xmp_context);
EXPORT int xmp_start_player (xmp_context, int, int);
EXPORT int xmp_play_frame (xmp_context);
EXPORT int xmp_play_buffer (xmp_context, void *, int, int);
EXPORT void xmp_get_frame_info (xmp_context, struct xmp_frame_info *);
EXPORT void xmp_end_player (xmp_context);
EXPORT void xmp_inject_event (xmp_context, int, struct xmp_event *);
EXPORT void xmp_get_module_info (xmp_context, struct xmp_module_info *);
EXPORT char **xmp_get_format_list (void);
EXPORT int xmp_next_position (xmp_context);
EXPORT int xmp_prev_position (xmp_context);
EXPORT int xmp_set_position (xmp_context, int);
EXPORT void xmp_stop_module (xmp_context);
EXPORT void xmp_restart_module (xmp_context);
EXPORT int xmp_seek_time (xmp_context, int);
EXPORT int xmp_channel_mute (xmp_context, int, int);
EXPORT int xmp_channel_vol (xmp_context, int, int);
EXPORT int xmp_set_player (xmp_context, int, int);
EXPORT int xmp_get_player (xmp_context, int);
EXPORT int xmp_set_instrument_path (xmp_context, char *);
EXPORT int xmp_load_module_from_memory (xmp_context, void *, long);
//EXPORT int xmp_load_module_from_file (xmp_context, void *, long);
/* External sample mixer API */
EXPORT int xmp_start_smix (xmp_context, int, int);
EXPORT void xmp_end_smix (xmp_context);
EXPORT int xmp_smix_play_instrument(xmp_context, int, int, int, int);
EXPORT int xmp_smix_play_sample (xmp_context, int, int, int, int);
EXPORT int xmp_smix_channel_pan (xmp_context, int, int);
EXPORT int xmp_smix_load_sample (xmp_context, int, char *);
EXPORT int xmp_smix_release_sample (xmp_context, int);
#ifdef __cplusplus
}
#endif
#endif /* XMP_H */

View file

@ -0,0 +1,520 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2009-04-28.21; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
nl='
'
IFS=" "" $nl"
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit=${DOITPROG-}
if test -z "$doit"; then
doit_exec=exec
else
doit_exec=$doit
fi
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_glob='?'
initialize_posix_glob='
test "$posix_glob" != "?" || {
if (set -f) 2>/dev/null; then
posix_glob=
else
posix_glob=:
fi
}
'
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
no_target_directory=
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *' '* | *'
'* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t) dst_arg=$2
shift;;
-T) no_target_directory=true;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call `install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
trap '(exit $?); exit' 1 2 13 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names starting with `-'.
case $src in
-*) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# Protect names starting with `-'.
case $dst in
-*) dst=./$dst;;
esac
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
if test -n "$no_target_directory"; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
# Prefer dirname, but fall back on a substitute if dirname fails.
dstdir=`
(dirname "$dst") 2>/dev/null ||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$dst" : 'X\(//\)[^/]' \| \
X"$dst" : 'X\(//\)$' \| \
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
echo X"$dst" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
}
/^X\(\/\/\)[^/].*/{
s//\1/
q
}
/^X\(\/\/\)$/{
s//\1/
q
}
/^X\(\/\).*/{
s//\1/
q
}
s/.*/./; q'
`
test -d "$dstdir"
dstdir_status=$?
fi
fi
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
if (umask $mkdir_umask &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writeable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
ls_ld_tmpdir=`ls -ld "$tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/d" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
-*) prefix='./';;
*) prefix='';;
esac
eval "$initialize_posix_glob"
oIFS=$IFS
IFS=/
$posix_glob set -f
set fnord $dstdir
shift
$posix_glob set +f
IFS=$oIFS
prefixes=
for d
do
test -z "$d" && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
eval "$initialize_posix_glob" &&
$posix_glob set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
$posix_glob set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

View file

@ -0,0 +1,17 @@
LOCAL_PATH := $(call my-dir)/..
include $(CLEAR_VARS)
include $(LOCAL_PATH)/src/Makefile
include $(LOCAL_PATH)/src/loaders/Makefile
SRC_SOURCES := $(addprefix src/,$(SRC_OBJS))
LOADERS_SOURCES := $(addprefix src/loaders/,$(LOADERS_OBJS))
LOCAL_MODULE := xmp
LOCAL_CFLAGS := -O3 -DHAVE_MKSTEMP -DHAVE_FNMATCH -DLIBXMP_CORE_PLAYER \
-I$(LOCAL_PATH)/include/libxmp-lite -I$(LOCAL_PATH)/src
LOCAL_SRC_FILES := $(SRC_SOURCES:.o=.c.arm) \
$(LOADERS_SOURCES:.o=.c)
include $(BUILD_STATIC_LIBRARY)

View file

@ -0,0 +1,4 @@
APP_PROJECT_PATH := $(call my-dir)
APP_MODULES := libxmp
APP_ABI := armeabi armeabi-v7a x86
APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk

View file

@ -0,0 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@/libxmp-lite
Name: libxmp-lite
Description: Xmp lite module player library
Version: 4.4.1
Requires:
Libs: -L${libdir} -lxmp-lite
Cflags: -I${includedir}
Libs.private: -lm

View file

@ -0,0 +1,65 @@
XMP_4.0 {
global:
xmp_version;
xmp_vercode;
xmp_get_format_list;
xmp_create_context;
xmp_free_context;
xmp_test_module;
xmp_load_module;
xmp_release_module;
xmp_scan_module;
xmp_get_module_info;
xmp_start_player;
xmp_play_frame;
xmp_get_frame_info;
xmp_end_player;
xmp_next_position;
xmp_prev_position;
xmp_set_position;
xmp_stop_module;
xmp_restart_module;
xmp_seek_time;
xmp_channel_mute;
xmp_channel_vol;
xmp_inject_event;
xmp_set_player;
xmp_get_player;
xmp_set_instrument_path;
local:
*;
};
XMP_4.1 {
global:
xmp_set_player;
xmp_get_player;
xmp_play_buffer;
} XMP_4.0;
XMP_4.2 {
global:
xmp_get_player;
xmp_load_module_from_memory;
xmp_start_smix;
xmp_end_smix;
xmp_smix_play_instrument;
xmp_smix_play_sample;
xmp_smix_channel_pan;
xmp_smix_load_sample;
xmp_smix_release_sample;
} XMP_4.1;
XMP_4.3 {
global:
xmp_set_player;
xmp_get_player;
xmp_load_module_from_file;
} XMP_4.2;
XMP_4.4 {
global:
xmp_set_player;
xmp_get_player;
} XMP_4.3;

View file

@ -0,0 +1,21 @@
SRC_OBJS = virtual.o format.o period.o player.o read_event.o \
dataio.o lfo.o scan.o control.o filter.o \
effects.o mixer.o mix_all.o load_helpers.o load.o \
hio.o smix.o memio.o
SRC_DFILES = Makefile $(SRC_OBJS:.o=.c) common.h effects.h \
format.h lfo.h list.h mixer.h period.h player.h virtual.h \
precomp_lut.h hio.h memio.h mdataio.h tempfile.h
SRC_PATH = src
OBJS += $(addprefix $(SRC_PATH)/,$(SRC_OBJS))
default-src::
$(MAKE) -C ..
dist-src::
mkdir -p $(DIST)/$(SRC_PATH)
cp -RPp $(addprefix $(SRC_PATH)/,$(SRC_DFILES)) $(DIST)/$(SRC_PATH)

View file

@ -0,0 +1,411 @@
#ifndef LIBXMP_COMMON_H
#define LIBXMP_COMMON_H
#ifdef __AROS__
#define __AMIGA__
#endif
#include <stdio.h>
#include <string.h>
#include "xmp.h"
/*
#if defined(__GNUC__) || defined(__clang__)
#if !defined(WIN32) && !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__AMIGA__) && !defined(B_BEOS_VERSION) && !defined(__ATHEOS__) && !defined(EMSCRIPTEN) && !defined(__MINT__)
#define USE_VERSIONED_SYMBOLS
#endif
#endif
*/
/* AmigaOS fixes by Chris Young <cdyoung@ntlworld.com>, Nov 25, 2007
*/
#if defined B_BEOS_VERSION
# include <SupportDefs.h>
#elif defined __amigaos4__
# include <exec/types.h>
#else
typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;
#endif
#ifdef _MSC_VER /* MSVC++6.0 has no long long */
typedef signed __int64 int64;
typedef unsigned __int64 uint64;
#elif !defined B_BEOS_VERSION /* BeOS has its own int64 definition */
typedef unsigned long long uint64;
typedef signed long long int64;
#endif
#ifndef LIBXMP_CORE_PLAYER
#define LIBXMP_PAULA_SIMULATOR
#endif
/* Constants */
#define PAL_RATE 250.0 /* 1 / (50Hz * 80us) */
#define NTSC_RATE 208.0 /* 1 / (60Hz * 80us) */
#define C4_PAL_RATE 8287 /* 7093789.2 / period (C4) * 2 */
#define C4_NTSC_RATE 8363 /* 7159090.5 / period (C4) * 2 */
/* [Amiga] PAL color carrier frequency (PCCF) = 4.43361825 MHz */
/* [Amiga] CPU clock = 1.6 * PCCF = 7.0937892 MHz */
#define DEFAULT_AMPLIFY 1
#define DEFAULT_MIX 100
#define MSN(x) (((x)&0xf0)>>4)
#define LSN(x) ((x)&0x0f)
#define SET_FLAG(a,b) ((a)|=(b))
#define RESET_FLAG(a,b) ((a)&=~(b))
#define TEST_FLAG(a,b) !!((a)&(b))
#define CLAMP(x,a,b) do { \
if ((x) < (a)) (x) = (a); \
else if ((x) > (b)) (x) = (b); \
} while (0)
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define TRACK_NUM(a,c) m->mod.xxp[a]->index[c]
#define EVENT(a,c,r) m->mod.xxt[TRACK_NUM((a),(c))]->event[r]
#ifdef _MSC_VER
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#ifndef CLIB_DECL
#define CLIB_DECL
#endif
// VS prior to VC7.1 does not support variadic macros. VC8.0 does not optimize unused parameters passing
#if _MSC_VER < 1400
void __inline CLIB_DECL D_(const char *text, ...) { do {} while (0); }
#else
#define D_(args, ...) do {} while (0)
#endif
#elif defined __ANDROID__
#ifdef DEBUG
#include <android/log.h>
#define D_CRIT " Error: "
#define D_WARN "Warning: "
#define D_INFO " Info: "
#define D_(args...) do { \
__android_log_print(ANDROID_LOG_DEBUG, "libxmp", args); \
} while (0)
#else
#define D_(args...) do {} while (0)
#endif
#else
#ifdef DEBUG
#define D_INFO "\x1b[33m"
#define D_CRIT "\x1b[31m"
#define D_WARN "\x1b[36m"
#define D_(args...) do { \
printf("\x1b[33m%s \x1b[37m[%s:%d] " D_INFO, __FUNCTION__, \
__FILE__, __LINE__); printf (args); printf ("\x1b[0m\n"); \
} while (0)
#else
//#define D_(args...) do {} while (0)
#define D_(...)
#endif
#endif /* !_MSC_VER */
#ifdef _MSC_VER
#define dup _dup
#define fileno _fileno
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strnicmp _strnicmp
#define strdup _strdup
#define fdopen _fdopen
#define open _open
#define close _close
#define unlink _unlink
#endif
/* Quirks */
#define QUIRK_S3MLOOP (1 << 0) /* S3M loop mode */
#define QUIRK_ENVFADE (1 << 1) /* Fade at end of envelope */
#define QUIRK_PROTRACK (1 << 2) /* Use Protracker-specific quirks */
#define QUIRK_ST3BUGS (1 << 4) /* Scream Tracker 3 bug compatibility */
#define QUIRK_FINEFX (1 << 5) /* Enable 0xf/0xe for fine effects */
#define QUIRK_VSALL (1 << 6) /* Volume slides in all frames */
#define QUIRK_PBALL (1 << 7) /* Pitch bending in all frames */
#define QUIRK_PERPAT (1 << 8) /* Cancel persistent fx at pat start */
#define QUIRK_VOLPDN (1 << 9) /* Set priority to volume slide down */
#define QUIRK_UNISLD (1 << 10) /* Unified pitch slide/portamento */
#define QUIRK_ITVPOR (1 << 11) /* Disable fine bends in IT vol fx */
#define QUIRK_FTMOD (1 << 12) /* Flag for multichannel mods */
/*#define QUIRK_MODRNG (1 << 13)*/ /* Limit periods to MOD range */
#define QUIRK_INSVOL (1 << 14) /* Use instrument volume */
#define QUIRK_VIRTUAL (1 << 15) /* Enable virtual channels */
#define QUIRK_FILTER (1 << 16) /* Enable filter */
#define QUIRK_IGSTPOR (1 << 17) /* Ignore stray tone portamento */
#define QUIRK_KEYOFF (1 << 18) /* Keyoff doesn't reset fadeout */
#define QUIRK_VIBHALF (1 << 19) /* Vibrato is half as deep */
#define QUIRK_VIBALL (1 << 20) /* Vibrato in all frames */
#define QUIRK_VIBINV (1 << 21) /* Vibrato has inverse waveform */
#define QUIRK_PRENV (1 << 22) /* Portamento resets envelope & fade */
#define QUIRK_ITOLDFX (1 << 23) /* IT old effects mode */
#define QUIRK_S3MRTG (1 << 24) /* S3M-style retrig when count == 0 */
#define QUIRK_RTDELAY (1 << 25) /* Delay effect retrigs instrument */
#define QUIRK_FT2BUGS (1 << 26) /* FT2 bug compatibility */
#define QUIRK_MARKER (1 << 27) /* Patterns 0xfe and 0xff reserved */
#define QUIRK_NOBPM (1 << 28) /* Adjust speed only, no BPM */
#define QUIRK_ARPMEM (1 << 29) /* Arpeggio has memory (S3M_ARPEGGIO) */
#define QUIRK_RSTCHN (1 << 30) /* Reset channel on sample end */
#define HAS_QUIRK(x) (m->quirk & (x))
/* Format quirks */
#define QUIRKS_ST3 (QUIRK_S3MLOOP | QUIRK_VOLPDN | QUIRK_FINEFX | \
QUIRK_S3MRTG | QUIRK_MARKER | QUIRK_RSTCHN )
#define QUIRKS_FT2 (QUIRK_RTDELAY | QUIRK_FINEFX )
#define QUIRKS_IT (QUIRK_S3MLOOP | QUIRK_FINEFX | QUIRK_VIBALL | \
QUIRK_ENVFADE | QUIRK_ITVPOR | QUIRK_KEYOFF | \
QUIRK_VIRTUAL | QUIRK_FILTER | QUIRK_RSTCHN | \
QUIRK_IGSTPOR | QUIRK_S3MRTG | QUIRK_MARKER )
/* DSP effects */
#define DSP_EFFECT_CUTOFF 0x02
#define DSP_EFFECT_RESONANCE 0x03
#define DSP_EFFECT_FILTER_A0 0xb0
#define DSP_EFFECT_FILTER_B0 0xb1
#define DSP_EFFECT_FILTER_B1 0xb2
/* Time factor */
#define DEFAULT_TIME_FACTOR 10.0
#define MED_TIME_FACTOR 2.64
#define MAX_SEQUENCES 16
#define MAX_SAMPLE_SIZE 0x10000000
#define MAX_SAMPLES 1024
#define IS_PLAYER_MODE_MOD() (m->read_event_type == READ_EVENT_MOD)
#define IS_PLAYER_MODE_FT2() (m->read_event_type == READ_EVENT_FT2)
#define IS_PLAYER_MODE_ST3() (m->read_event_type == READ_EVENT_ST3)
#define IS_PLAYER_MODE_IT() (m->read_event_type == READ_EVENT_IT)
#define IS_PLAYER_MODE_MED() (m->read_event_type == READ_EVENT_MED)
#define IS_PERIOD_MODRNG() (m->period_type == PERIOD_MODRNG)
#define IS_PERIOD_LINEAR() (m->period_type == PERIOD_LINEAR)
#define IS_PERIOD_CSPD() (m->period_type == PERIOD_CSPD)
#define IS_AMIGA_MOD() (IS_PLAYER_MODE_MOD() && IS_PERIOD_MODRNG())
struct ord_data {
int speed;
int bpm;
int gvl;
int time;
int start_row;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
#endif
};
/* Context */
struct smix_data {
int chn;
int ins;
int smp;
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
};
/* This will be added to the sample structure in the next API revision */
struct extra_sample_data {
double c5spd;
};
struct module_data {
struct xmp_module mod;
char *dirname; /* file dirname */
char *basename; /* file basename */
char *filename; /* Module file name */
char *comment; /* Comments, if any */
uint8 md5[16]; /* MD5 message digest */
int size; /* File size */
double rrate; /* Replay rate */
double time_factor; /* Time conversion constant */
int c4rate; /* C4 replay rate */
int volbase; /* Volume base */
int gvolbase; /* Global volume base */
int gvol; /* Global volume */
int *vol_table; /* Volume translation table */
int quirk; /* player quirks */
#define READ_EVENT_MOD 0
#define READ_EVENT_FT2 1
#define READ_EVENT_ST3 2
#define READ_EVENT_IT 3
#define READ_EVENT_MED 4
int read_event_type;
#define PERIOD_AMIGA 0
#define PERIOD_MODRNG 1
#define PERIOD_LINEAR 2
#define PERIOD_CSPD 3
int period_type;
int smpctl; /* sample control flags */
int defpan; /* default pan setting */
struct ord_data xxo_info[XMP_MAX_MOD_LENGTH];
int num_sequences;
struct xmp_sequence seq_data[MAX_SEQUENCES];
char *instrument_path;
void *extra; /* format-specific extra fields */
char **scan_cnt; /* scan counters */
struct extra_sample_data *xtra;
#ifndef LIBXMP_CORE_DISABLE_IT
struct xmp_sample *xsmp; /* sustain loop samples */
#endif
};
struct player_data {
int ord;
int pos;
int row;
int frame;
int speed;
int bpm;
int mode;
int player_flags;
int flags;
double current_time;
double frame_time;
int loop_count;
int sequence;
unsigned char sequence_control[XMP_MAX_MOD_LENGTH];
int smix_vol; /* SFX volume */
int master_vol; /* Music volume */
int gvol;
struct flow_control {
int pbreak;
int jump;
int delay;
int jumpline;
int loop_chn;
struct pattern_loop {
int start;
int count;
} *loop;
int num_rows;
int end_point;
int rowdelay; /* For IT pattern row delay */
int rowdelay_set;
} flow;
struct {
int time; /* replay time in ms */
int ord;
int row;
int num;
} scan[MAX_SEQUENCES];
struct channel_data *xc_data;
int channel_vol[XMP_MAX_CHANNELS];
char channel_mute[XMP_MAX_CHANNELS];
struct virt_control {
int num_tracks; /* Number of tracks */
int virt_channels; /* Number of virtual channels */
int virt_used; /* Number of voices currently in use */
int maxvoc; /* Number of sound card voices */
struct virt_channel {
int count;
int map;
} *virt_channel;
struct mixer_voice *voice_array;
} virt;
struct xmp_event inject_event[XMP_MAX_CHANNELS];
struct {
int consumed;
int in_size;
char *in_buffer;
} buffer_data;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed; /* For IceTracker speed effect */
#endif
int filter; /* Amiga led filter */
};
struct mixer_data {
int freq; /* sampling rate */
int format; /* sample format */
int amplify; /* amplification multiplier */
int mix; /* percentage of channel separation */
int interp; /* interpolation type */
int dsp; /* dsp effect flags */
char* buffer; /* output buffer */
int32* buf32; /* temporary buffer for 32 bit samples */
int numvoc; /* default softmixer voices number */
int ticksize;
int dtright; /* anticlick control, right channel */
int dtleft; /* anticlick control, left channel */
double pbase; /* period base */
};
struct context_data {
struct player_data p;
struct mixer_data s;
struct module_data m;
struct smix_data smix;
int state;
};
/* Prototypes */
char *libxmp_adjust_string (char *);
int libxmp_exclude_match (char *);
int libxmp_prepare_scan (struct context_data *);
int libxmp_scan_sequences (struct context_data *);
int libxmp_get_sequence (struct context_data *, int);
int libxmp_set_player_mode (struct context_data *);
int8 read8s (FILE *, int *err);
uint8 read8 (FILE *, int *err);
uint16 read16l (FILE *, int *err);
uint16 read16b (FILE *, int *err);
uint32 read24l (FILE *, int *err);
uint32 read24b (FILE *, int *err);
uint32 read32l (FILE *, int *err);
uint32 read32b (FILE *, int *err);
void write8 (FILE *, uint8);
void write16l (FILE *, uint16);
void write16b (FILE *, uint16);
void write32l (FILE *, uint32);
void write32b (FILE *, uint32);
int move_data (FILE *, FILE *, int);
uint16 readmem16l (uint8 *);
uint16 readmem16b (uint8 *);
uint32 readmem24l (uint8 *);
uint32 readmem24b (uint8 *);
uint32 readmem32l (uint8 *);
uint32 readmem32b (uint8 *);
struct xmp_instrument *libxmp_get_instrument(struct context_data *, int);
struct xmp_sample *libxmp_get_sample(struct context_data *, int);
#endif /* LIBXMP_COMMON_H */

View file

@ -0,0 +1,529 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "format.h"
#include "virtual.h"
#include "mixer.h"
const char *xmp_version = XMP_VERSION;
const unsigned int xmp_vercode = XMP_VERCODE;
xmp_context xmp_create_context()
{
struct context_data *ctx;
ctx = calloc(1, sizeof(struct context_data));
if (ctx == NULL) {
return NULL;
}
ctx->state = XMP_STATE_UNLOADED;
ctx->m.defpan = 100;
ctx->s.numvoc = SMIX_NUMVOC;
return (xmp_context)ctx;
}
void xmp_free_context(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
free(opaque);
}
static void set_position(struct context_data *ctx, int pos, int dir)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct flow_control *f = &p->flow;
int seq;
int has_marker;
/* If dir is 0, we can jump to a different sequence */
if (dir == 0) {
seq = libxmp_get_sequence(ctx, pos);
} else {
seq = p->sequence;
}
if (seq == 0xff) {
return;
}
has_marker = HAS_QUIRK(QUIRK_MARKER);
if (seq >= 0) {
int start = m->seq_data[seq].entry_point;
p->sequence = seq;
if (pos >= 0) {
int pat;
while (has_marker && mod->xxo[pos] == 0xfe) {
if (dir < 0) {
if (pos > start) {
pos--;
}
} else {
pos++;
}
}
pat = mod->xxo[pos];
if (pat < mod->pat) {
if (has_marker && pat == 0xff) {
return;
}
if (pos > p->scan[seq].ord) {
f->end_point = 0;
} else {
f->num_rows = mod->xxp[pat]->rows;
f->end_point = p->scan[seq].num;
f->jumpline = 0;
}
}
}
if (pos < mod->len) {
if (pos == 0) {
p->pos = -1;
} else {
p->pos = pos;
}
}
}
}
int xmp_next_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos < m->mod.len)
set_position(ctx, p->pos + 1, 1);
return p->pos;
}
int xmp_prev_position(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (p->pos == m->seq_data[p->sequence].entry_point) {
set_position(ctx, -1, -1);
} else if (p->pos > m->seq_data[p->sequence].entry_point) {
set_position(ctx, p->pos - 1, -1);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_set_position(xmp_context opaque, int pos)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (pos >= m->mod.len)
return -XMP_ERROR_INVALID;
set_position(ctx, pos, 0);
return p->pos;
}
void xmp_stop_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->pos = -2;
}
void xmp_restart_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
p->loop_count = 0;
p->pos = -1;
}
int xmp_seek_time(xmp_context opaque, int time)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i, t;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
for (i = m->mod.len - 1; i >= 0; i--) {
int pat = m->mod.xxo[i];
if (pat >= m->mod.pat) {
continue;
}
if (libxmp_get_sequence(ctx, i) != p->sequence) {
continue;
}
t = m->xxo_info[i].time;
if (time >= t) {
set_position(ctx, i, 1);
break;
}
}
if (i < 0) {
xmp_set_position(opaque, 0);
}
return p->pos < 0 ? 0 : p->pos;
}
int xmp_channel_mute(xmp_context opaque, int chn, int status)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_mute[chn];
if (status >= 2) {
p->channel_mute[chn] = !p->channel_mute[chn];
} else if (status >= 0) {
p->channel_mute[chn] = status;
}
return ret;
}
int xmp_channel_vol(xmp_context opaque, int chn, int vol)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
int ret;
if (ctx->state < XMP_STATE_PLAYING)
return -XMP_ERROR_STATE;
if (chn < 0 || chn >= XMP_MAX_CHANNELS) {
return -XMP_ERROR_INVALID;
}
ret = p->channel_vol[chn];
if (vol >= 0 && vol <= 100) {
p->channel_vol[chn] = vol;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
extern int xmp_set_player_v41__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
extern int xmp_set_player_v43__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
extern int xmp_set_player_v44__(xmp_context, int, int)
__attribute__((alias("xmp_set_player_v40__")));
asm(".symver xmp_set_player_v40__, xmp_set_player@XMP_4.0");
asm(".symver xmp_set_player_v41__, xmp_set_player@XMP_4.1");
asm(".symver xmp_set_player_v43__, xmp_set_player@XMP_4.3");
asm(".symver xmp_set_player_v44__, xmp_set_player@@XMP_4.4");
#define xmp_set_player__ xmp_set_player_v40__
#else
#define xmp_set_player__ xmp_set_player
#endif
int xmp_set_player__(xmp_context opaque, int parm, int val)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
/* these should be set before loading the module */
if (ctx->state >= XMP_STATE_LOADED) {
return -XMP_ERROR_STATE;
}
} else if (parm == XMP_PLAYER_VOICES) {
/* these should be set before start playing */
if (ctx->state >= XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
} else if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
if (val >= 0 && val <= 3) {
s->amplify = val;
ret = 0;
}
break;
case XMP_PLAYER_MIX:
if (val >= -100 && val <= 100) {
s->mix = val;
ret = 0;
}
break;
case XMP_PLAYER_INTERP:
if (val >= XMP_INTERP_NEAREST && val <= XMP_INTERP_SPLINE) {
s->interp = val;
ret = 0;
}
break;
case XMP_PLAYER_DSP:
s->dsp = val;
ret = 0;
break;
case XMP_PLAYER_FLAGS: {
p->player_flags = val;
ret = 0;
break; }
/* 4.1 */
case XMP_PLAYER_CFLAGS: {
int vblank = p->flags & XMP_FLAGS_VBLANK;
p->flags = val;
if (vblank != (p->flags & XMP_FLAGS_VBLANK))
libxmp_scan_sequences(ctx);
ret = 0;
break; }
case XMP_PLAYER_SMPCTL:
m->smpctl = val;
ret = 0;
break;
case XMP_PLAYER_VOLUME:
if (val >= 0 && val <= 200) {
p->master_vol = val;
ret = 0;
}
break;
case XMP_PLAYER_SMIX_VOLUME:
if (val >= 0 && val <= 200) {
p->smix_vol = val;
ret = 0;
}
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
if (val >= 0 && val <= 100) {
m->defpan = val;
ret = 0;
}
break;
/* 4.4 */
case XMP_PLAYER_MODE:
p->mode = val;
libxmp_set_player_mode(ctx);
libxmp_scan_sequences(ctx);
ret = 0;
break;
case XMP_PLAYER_VOICES:
s->numvoc = val;
break;
}
return ret;
}
#ifdef USE_VERSIONED_SYMBOLS
extern int xmp_get_player_v41__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
extern int xmp_get_player_v42__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
extern int xmp_get_player_v43__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
extern int xmp_get_player_v44__(xmp_context, int)
__attribute__((alias("xmp_get_player_v40__")));
asm(".symver xmp_get_player_v40__, xmp_get_player@XMP_4.0");
asm(".symver xmp_get_player_v41__, xmp_get_player@XMP_4.1");
asm(".symver xmp_get_player_v42__, xmp_get_player@XMP_4.2");
asm(".symver xmp_get_player_v43__, xmp_get_player@XMP_4.3");
asm(".symver xmp_get_player_v44__, xmp_get_player@@XMP_4.4");
#define xmp_get_player__ xmp_get_player_v40__
#else
#define xmp_get_player__ xmp_get_player
#endif
int xmp_get_player__(xmp_context opaque, int parm)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int ret = -XMP_ERROR_INVALID;
if (parm == XMP_PLAYER_SMPCTL || parm == XMP_PLAYER_DEFPAN) {
// can read these at any time
} else if (parm != XMP_PLAYER_STATE && ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
switch (parm) {
case XMP_PLAYER_AMP:
ret = s->amplify;
break;
case XMP_PLAYER_MIX:
ret = s->mix;
break;
case XMP_PLAYER_INTERP:
ret = s->interp;
break;
case XMP_PLAYER_DSP:
ret = s->dsp;
break;
case XMP_PLAYER_FLAGS:
ret = p->player_flags;
break;
/* 4.1 */
case XMP_PLAYER_CFLAGS:
ret = p->flags;
break;
case XMP_PLAYER_SMPCTL:
ret = m->smpctl;
break;
case XMP_PLAYER_VOLUME:
ret = p->master_vol;
break;
case XMP_PLAYER_SMIX_VOLUME:
ret = p->smix_vol;
break;
/* 4.2 */
case XMP_PLAYER_STATE:
ret = ctx->state;
break;
/* 4.3 */
case XMP_PLAYER_DEFPAN:
ret = m->defpan;
break;
/* 4.4 */
case XMP_PLAYER_MODE:
ret = p->mode;
break;
case XMP_PLAYER_MIXER_TYPE:
ret = XMP_MIXER_STANDARD;
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
#ifdef LIBXMP_PAULA_SIMULATOR
if (p->filter) {
ret = XMP_MIXER_A500F;
} else {
ret = XMP_MIXER_A500;
}
#endif
}
}
break;
case XMP_PLAYER_VOICES:
ret = s->numvoc;
break;
}
return ret;
}
char **xmp_get_format_list()
{
return format_list();
}
void xmp_inject_event(xmp_context opaque, int channel, struct xmp_event *e)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
if (ctx->state < XMP_STATE_PLAYING)
return;
memcpy(&p->inject_event[channel], e, sizeof(struct xmp_event));
p->inject_event[channel]._flag = 1;
}
int xmp_set_instrument_path(xmp_context opaque, char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
if (m->instrument_path != NULL)
free(m->instrument_path);
size_t length = strlen(path);
m->instrument_path = malloc(length + 1);
memcpy(m->instrument_path, path, length + 1);
if (m->instrument_path == NULL) {
return -XMP_ERROR_SYSTEM;
}
return 0;
}

View file

@ -0,0 +1,274 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "common.h"
#define read_byte(x) do { \
(x) = fgetc(f); \
if ((x) < 0) goto error; \
} while (0)
#define set_error(x) do { \
if (err != NULL) *err = (x); \
} while (0)
uint8 read8(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xff;
}
int8 read8s(FILE *f, int *err)
{
int a;
read_byte(a);
set_error(0);
return (int8)a;
error:
set_error(ferror(f) ? errno : EOF);
return 0;
}
uint16 read16l(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return ((uint16)b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint16 read16b(FILE *f, int *err)
{
int a, b;
read_byte(a);
read_byte(b);
set_error(0);
return (a << 8) | b;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffff;
}
uint32 read24l(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
}
uint32 read24b(FILE *f, int *err)
{
int a, b, c;
read_byte(a);
read_byte(b);
read_byte(c);
set_error(0);
return (a << 16) | (b << 8) | c;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffff;
}
uint32 read32l(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (d << 24) | (c << 16) | (b << 8) | a;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint32 read32b(FILE *f, int *err)
{
int a, b, c, d;
read_byte(a);
read_byte(b);
read_byte(c);
read_byte(d);
set_error(0);
return (a << 24) | (b << 16) | (c << 8) | d;
error:
set_error(ferror(f) ? errno : EOF);
return 0xffffffff;
}
uint16 readmem16l(uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (b << 8) | a;
}
uint16 readmem16b(uint8 *m)
{
uint32 a, b;
a = m[0];
b = m[1];
return (a << 8) | b;
}
uint32 readmem24l(uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (c << 16) | (b << 8) | a;
}
uint32 readmem24b(uint8 *m)
{
uint32 a, b, c;
a = m[0];
b = m[1];
c = m[2];
return (a << 16) | (b << 8) | c;
}
uint32 readmem32l(uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (d << 24) | (c << 16) | (b << 8) | a;
}
uint32 readmem32b(uint8 *m)
{
uint32 a, b, c, d;
a = m[0];
b = m[1];
c = m[2];
d = m[3];
return (a << 24) | (b << 16) | (c << 8) | d;
}
#ifndef LIBXMP_CORE_PLAYER
inline void write8(FILE *f, uint8 b)
{
fputc(b, f);
}
void write16l(FILE *f, uint16 w)
{
write8(f, w & 0x00ff);
write8(f, (w & 0xff00) >> 8);
}
void write16b(FILE *f, uint16 w)
{
write8(f, (w & 0xff00) >> 8);
write8(f, w & 0x00ff);
}
void write32l(FILE *f, uint32 w)
{
write8(f, w & 0x000000ff);
write8(f, (w & 0x0000ff00) >> 8);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0xff000000) >> 24);
}
void write32b(FILE *f, uint32 w)
{
write8(f, (w & 0xff000000) >> 24);
write8(f, (w & 0x00ff0000) >> 16);
write8(f, (w & 0x0000ff00) >> 8);
write8(f, w & 0x000000ff);
}
int move_data(FILE *out, FILE *in, int len)
{
uint8 buf[1024];
int l;
do {
l = fread(buf, 1, len > 1024 ? 1024 : len, in);
fwrite(buf, 1, l, out);
len -= l;
} while (l > 0 && len > 0);
return 0;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
#ifndef LIBXMP_EFFECTS_H
#define LIBXMP_EFFECTS_H
/* Protracker effects */
#define FX_ARPEGGIO 0x00
#define FX_PORTA_UP 0x01
#define FX_PORTA_DN 0x02
#define FX_TONEPORTA 0x03
#define FX_VIBRATO 0x04
#define FX_TONE_VSLIDE 0x05
#define FX_VIBRA_VSLIDE 0x06
#define FX_TREMOLO 0x07
#define FX_OFFSET 0x09
#define FX_VOLSLIDE 0x0a
#define FX_JUMP 0x0b
#define FX_VOLSET 0x0c
#define FX_BREAK 0x0d
#define FX_EXTENDED 0x0e
#define FX_SPEED 0x0f
/* Fast tracker effects */
#define FX_SETPAN 0x08
/* Fast Tracker II effects */
#define FX_GLOBALVOL 0x10
#define FX_GVOL_SLIDE 0x11
#define FX_KEYOFF 0x14
#define FX_ENVPOS 0x15
#define FX_PANSLIDE 0x19
#define FX_MULTI_RETRIG 0x1b
#define FX_TREMOR 0x1d
#define FX_XF_PORTA 0x21
/* Protracker extended effects */
#define EX_FILTER 0x00
#define EX_F_PORTA_UP 0x01
#define EX_F_PORTA_DN 0x02
#define EX_GLISS 0x03
#define EX_VIBRATO_WF 0x04
#define EX_FINETUNE 0x05
#define EX_PATTERN_LOOP 0x06
#define EX_TREMOLO_WF 0x07
#define EX_SETPAN 0x08
#define EX_RETRIG 0x09
#define EX_F_VSLIDE_UP 0x0a
#define EX_F_VSLIDE_DN 0x0b
#define EX_CUT 0x0c
#define EX_DELAY 0x0d
#define EX_PATT_DELAY 0x0e
#define EX_INVLOOP 0x0f
#ifndef LIBXMP_CORE_PLAYER
/* Oktalyzer effects */
#define FX_OKT_ARP3 0x70
#define FX_OKT_ARP4 0x71
#define FX_OKT_ARP5 0x72
#define FX_NSLIDE2_DN 0x73
#define FX_NSLIDE2_UP 0x74
#define FX_F_NSLIDE_DN 0x75
#define FX_F_NSLIDE_UP 0x76
/* Persistent effects -- for FNK and FAR */
#define FX_PER_PORTA_DN 0x78
#define FX_PER_PORTA_UP 0x79
#define FX_PER_TPORTA 0x7a
#define FX_PER_VIBRATO 0x7b
#define FX_PER_VSLD_UP 0x7c
#define FX_PER_VSLD_DN 0x7d
#define FX_SPEED_CP 0x7e
#define FX_PER_CANCEL 0x7f
/* 669 frequency based effects */
#define FX_669_PORTA_UP 0x60
#define FX_669_PORTA_DN 0x61
#define FX_669_TPORTA 0x62
#define FX_669_FINETUNE 0x63
#define FX_669_VIBRATO 0x64
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
/* IT effects */
#define FX_TRK_VOL 0x80
#define FX_TRK_VSLIDE 0x81
#define FX_TRK_FVSLIDE 0x82
#define FX_IT_INSTFUNC 0x83
#define FX_FLT_CUTOFF 0x84
#define FX_FLT_RESN 0x85
#define FX_IT_BPM 0x87
#define FX_IT_ROWDELAY 0x88
#define FX_IT_PANSLIDE 0x89
#define FX_PANBRELLO 0x8a
#define FX_PANBRELLO_WF 0x8b
#define FX_HIOFFSET 0x8c
#define FX_IT_BREAK 0x8e /* like FX_BREAK with hex parameter */
#endif
#ifndef LIBXMP_CORE_PLAYER
/* MED effects */
#define FX_HOLD_DECAY 0x90
#define FX_SETPITCH 0x91
#define FX_VIBRATO2 0x92
/* PTM effects */
#define FX_NSLIDE_DN 0x9c /* IMF/PTM note slide down */
#define FX_NSLIDE_UP 0x9d /* IMF/PTM note slide up */
#define FX_NSLIDE_R_UP 0x9e /* PTM note slide down with retrigger */
#define FX_NSLIDE_R_DN 0x9f /* PTM note slide up with retrigger */
/* Extra effects */
#define FX_VOLSLIDE_UP 0xa0 /* SFX, MDL */
#define FX_VOLSLIDE_DN 0xa1
#define FX_F_VSLIDE 0xa5 /* IMF/MDL */
#define FX_CHORUS 0xa9 /* IMF */
#define FX_ICE_SPEED 0xa2
#define FX_REVERB 0xaa /* IMF */
#define FX_MED_HOLD 0xb1 /* MMD hold/decay */
#define FX_MEGAARP 0xb2 /* Smaksak effect 7: MegaArp */
#define FX_VOL_ADD 0xb6 /* SFX change volume up */
#define FX_VOL_SUB 0xb7 /* SFX change volume down */
#define FX_PITCH_ADD 0xb8 /* SFX add steps to current note */
#define FX_PITCH_SUB 0xb9 /* SFX add steps to current note */
#endif
#define FX_SURROUND 0x8d /* S3M/IT */
#define FX_S3M_SPEED 0xa3 /* S3M */
#define FX_VOLSLIDE_2 0xa4
#define FX_FINETUNE 0xa6
#define FX_S3M_BPM 0xab /* S3M */
#define FX_FINE_VIBRATO 0xac /* S3M/PTM/IMF/LIQ */
#define FX_F_VSLIDE_UP 0xad /* MMD */
#define FX_F_VSLIDE_DN 0xae /* MMD */
#define FX_F_PORTA_UP 0xaf /* MMD */
#define FX_F_PORTA_DN 0xb0 /* MMD */
#define FX_PATT_DELAY 0xb3 /* MMD */
#define FX_S3M_ARPEGGIO 0xb4
#define FX_PANSL_NOMEM 0xb5 /* XM volume column */
#define FX_VSLIDE_UP_2 0xc0 /* IT volume column volume slide */
#define FX_VSLIDE_DN_2 0xc1
#define FX_F_VSLIDE_UP_2 0xc2
#define FX_F_VSLIDE_DN_2 0xc3
#endif /* LIBXMP_EFFECTS_H */

View file

@ -0,0 +1,102 @@
/*
* Based on the public domain version by Olivier Lapicque
* Rewritten for libxmp by Claudio Matsuoka
*
* Copyright (C) 2012 Claudio Matsuoka
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBXMP_CORE_DISABLE_IT
#include <math.h>
#include "xmp.h"
#include "common.h"
#include "mixer.h"
/* LUT for 2 * damping factor */
static const float resonance_table[128] = {
1.0000000000000000f, 0.9786446094512940f, 0.9577452540397644f, 0.9372922182083130f,
0.9172759056091309f, 0.8976871371269226f, 0.8785166740417481f, 0.8597555756568909f,
0.8413951396942139f, 0.8234267830848694f, 0.8058421611785889f, 0.7886331081390381f,
0.7717915177345276f, 0.7553095817565918f, 0.7391796708106995f, 0.7233941555023193f,
0.7079457640647888f, 0.6928272843360901f, 0.6780316829681397f, 0.6635520458221436f,
0.6493816375732422f, 0.6355138421058655f, 0.6219421625137329f, 0.6086603403091431f,
0.5956621170043945f, 0.5829415321350098f, 0.5704925656318665f, 0.5583094954490662f,
0.5463865399360657f, 0.5347182154655457f, 0.5232990980148315f, 0.5121238231658936f,
0.5011872053146362f, 0.4904841780662537f, 0.4800096750259399f, 0.4697588682174683f,
0.4597269892692566f, 0.4499093294143677f, 0.4403013288974762f, 0.4308985173702240f,
0.4216965138912201f, 0.4126909971237183f, 0.4038778245449066f, 0.3952528536319733f,
0.3868120610713959f, 0.3785515129566193f, 0.3704673945903778f, 0.3625559210777283f,
0.3548133969306946f, 0.3472362160682678f, 0.3398208320140839f, 0.3325638175010681f,
0.3254617750644684f, 0.3185114264488220f, 0.3117094635963440f, 0.3050527870655060f,
0.2985382676124573f, 0.2921628654003143f, 0.2859236001968384f, 0.2798175811767578f,
0.2738419771194458f, 0.2679939568042755f, 0.2622708380222321f, 0.2566699385643005f,
0.2511886358261108f, 0.2458244115114212f, 0.2405747324228287f, 0.2354371547698975f,
0.2304092943668366f, 0.2254888117313385f, 0.2206734120845795f, 0.2159608304500580f,
0.2113489061594009f, 0.2068354636430740f, 0.2024184018373489f, 0.1980956792831421f,
0.1938652694225311f, 0.1897251904010773f, 0.1856735348701477f, 0.1817083954811096f,
0.1778279393911362f, 0.1740303486585617f, 0.1703138649463654f, 0.1666767448186874f,
0.1631172895431519f, 0.1596338599920273f, 0.1562248021364212f, 0.1528885662555695f,
0.1496235728263855f, 0.1464282870292664f, 0.1433012634515762f, 0.1402409970760346f,
0.1372461020946503f, 0.1343151479959488f, 0.1314467936754227f, 0.1286396980285645f,
0.1258925348520279f, 0.1232040524482727f, 0.1205729842185974f, 0.1179980933666229f,
0.1154781952500343f, 0.1130121126770973f, 0.1105986908078194f, 0.1082368120551109f,
0.1059253737330437f, 0.1036632955074310f, 0.1014495193958283f, 0.0992830246686935f,
0.0971627980470657f, 0.0950878411531448f, 0.0930572077631950f, 0.0910699293017387f,
0.0891250967979431f, 0.0872217938303947f, 0.0853591337800026f, 0.0835362523794174f,
0.0817523002624512f, 0.0800064504146576f, 0.0782978758215904f, 0.0766257941722870f,
0.0749894231557846f, 0.0733879879117012f, 0.0718207582831383f, 0.0702869966626167f,
0.0687859877943993f, 0.0673170387744904f, 0.0658794566988945f, 0.0644725710153580f,
};
/*
* Simple 2-poles resonant filter
*/
#define FREQ_PARAM_MULT (128.0f / (24.0f * 256.0f))
void libxmp_filter_setup(int srate, int cutoff, int res, int *a0, int *b0, int *b1)
{
float fc, fs = (float)srate;
float fg, fb0, fb1;
float r, d, e;
/* [0-255] => [100Hz-8000Hz] */
CLAMP(cutoff, 0, 255);
CLAMP(res, 0, 255);
fc = 110.0f * powf(2.0f, (float)cutoff * FREQ_PARAM_MULT + 0.25f);
if (fc > fs / 2.0f) {
fc = fs / 2.0f;
}
r = fs / (2.0 * 3.14159265358979f * fc);
d = resonance_table[res >> 1] * (r + 1.0) - 1.0;
e = r * r;
fg = 1.0 / (1.0 + d + e);
fb0 = (d + e + e) / (1.0 + d + e);
fb1 = -e / (1.0 + d + e);
*a0 = (int)(fg * (1 << FILTER_SHIFT));
*b0 = (int)(fb0 * (1 << FILTER_SHIFT));
*b1 = (int)(fb1 * (1 << FILTER_SHIFT));
}
#endif

View file

@ -0,0 +1,62 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#ifndef LIBXMP_CORE_PLAYER
#include "loaders/prowizard/prowiz.h"
#endif
#include "format.h"
extern const struct format_loader libxmp_loader_xm;
extern const struct format_loader libxmp_loader_mod;
extern const struct format_loader libxmp_loader_it;
extern const struct format_loader libxmp_loader_s3m;
extern const struct pw_format *const pw_format[];
const struct format_loader *const format_loader[5] = {
&libxmp_loader_xm,
&libxmp_loader_mod,
#ifndef LIBXMP_CORE_DISABLE_IT
&libxmp_loader_it,
#endif
&libxmp_loader_s3m,
NULL
};
static const char *_farray[5] = { NULL };
char **format_list()
{
int count, i;
if (_farray[0] == NULL) {
for (count = i = 0; format_loader[i] != NULL; i++) {
_farray[count++] = format_loader[i]->name;
}
_farray[count] = NULL;
}
return (char **)_farray;
}

View file

@ -0,0 +1,25 @@
#ifndef LIBXMP_FORMAT_H
#define LIBXMP_FORMAT_H
#include <stdio.h>
#include "common.h"
#include "hio.h"
struct format_loader {
const char *name;
int (*const test)(HIO_HANDLE *, char *, const int);
int (*const loader)(struct module_data *, HIO_HANDLE *, const int);
};
char **format_list(void);
#ifndef LIBXMP_CORE_PLAYER
#define NUM_FORMATS 52
#define NUM_PW_FORMATS 43
int pw_test_format(HIO_HANDLE *, char *, const int, struct xmp_test_info *);
#endif
#endif

View file

@ -0,0 +1,380 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include "common.h"
#include "hio.h"
#include "mdataio.h"
static long get_size(FILE *f)
{
long size, pos;
pos = ftell(f);
if (pos >= 0) {
if (fseek(f, 0, SEEK_END) < 0) {
return -1;
}
size = ftell(f);
if (fseek(f, pos, SEEK_SET) < 0) {
return -1;
}
return size;
} else {
return pos;
}
}
int8 hio_read8s(HIO_HANDLE *h)
{
int err;
int8 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8s(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8s(h->handle.mem);
break;
}
return ret;
}
uint8 hio_read8(HIO_HANDLE *h)
{
int err;
uint8 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read8(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread8(h->handle.mem);
break;
}
return ret;
}
uint16 hio_read16l(HIO_HANDLE *h)
{
int err;
uint16 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16l(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16l(h->handle.mem);
break;
}
return ret;
}
uint16 hio_read16b(HIO_HANDLE *h)
{
int err;
uint16 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read16b(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread16b(h->handle.mem);
break;
}
return ret;
}
uint32 hio_read24l(HIO_HANDLE *h)
{
int err;
uint32 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24l(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24l(h->handle.mem);
break;
}
return ret;
}
uint32 hio_read24b(HIO_HANDLE *h)
{
int err;
uint32 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read24b(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread24b(h->handle.mem);
break;
}
return ret;
}
uint32 hio_read32l(HIO_HANDLE *h)
{
int err;
uint32 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32l(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32l(h->handle.mem);
break;
}
return ret;
}
uint32 hio_read32b(HIO_HANDLE *h)
{
int err;
uint32 ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = read32b(h->handle.file, &err);
if (err != 0) {
h->error = err;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread32b(h->handle.mem);
}
return ret;
}
size_t hio_read(void *buf, size_t size, size_t num, HIO_HANDLE *h)
{
size_t ret = 0;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fread(buf, size, num, h->handle.file);
if (ret != num) {
if (ferror(h->handle.file)) {
h->error = errno;
} else {
h->error = feof(h->handle.file) ? EOF : -2;
}
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mread(buf, size, num, h->handle.mem);
if (ret != num) {
h->error = errno;
}
break;
}
return ret;
}
int hio_seek(HIO_HANDLE *h, long offset, int whence)
{
int ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fseek(h->handle.file, offset, whence);
if (ret < 0) {
h->error = errno;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mseek(h->handle.mem, offset, whence);
if (ret < 0) {
h->error = errno;
}
break;
}
return ret;
}
long hio_tell(HIO_HANDLE *h)
{
long ret = -1;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = ftell(h->handle.file);
if (ret < 0) {
h->error = errno;
}
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mtell(h->handle.mem);
if (ret < 0) {
h->error = errno;
}
break;
}
return ret;
}
int hio_eof(HIO_HANDLE *h)
{
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
return feof(h->handle.file);
case HIO_HANDLE_TYPE_MEMORY:
return meof(h->handle.mem);
default:
return EOF;
}
}
int hio_error(HIO_HANDLE *h)
{
int error = h->error;
h->error = 0;
return error;
}
HIO_HANDLE *hio_open(void *path, char *mode)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)malloc(sizeof (HIO_HANDLE));
if (h == NULL)
goto err;
h->error = 0;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = fopen(path, mode);
if (h->handle.file == NULL)
goto err2;
h->size = get_size(h->handle.file);
if (h->size < 0)
goto err3;
return h;
err3:
fclose(h->handle.file);
err2:
free(h);
err:
return NULL;
}
HIO_HANDLE *hio_open_mem(void *ptr, long size)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)malloc(sizeof (HIO_HANDLE));
if (h == NULL)
return NULL;
h->error = 0;
h->type = HIO_HANDLE_TYPE_MEMORY;
h->handle.mem = mopen(ptr, size);
h->size = size;
return h;
}
HIO_HANDLE *hio_open_file(FILE *f)
{
HIO_HANDLE *h;
h = (HIO_HANDLE *)malloc(sizeof (HIO_HANDLE));
if (h == NULL)
return NULL;
h->error = 0;
h->type = HIO_HANDLE_TYPE_FILE;
h->handle.file = f /*fdopen(fileno(f), "rb")*/;
h->size = get_size(f);
return h;
}
int hio_close(HIO_HANDLE *h)
{
int ret;
switch (HIO_HANDLE_TYPE(h)) {
case HIO_HANDLE_TYPE_FILE:
ret = fclose(h->handle.file);
break;
case HIO_HANDLE_TYPE_MEMORY:
ret = mclose(h->handle.mem);
break;
default:
ret = -1;
}
free(h);
return ret;
}
long hio_size(HIO_HANDLE *h)
{
return h->size;
}

View file

@ -0,0 +1,42 @@
#ifndef XMP_HIO_H
#define XMP_HIO_H
#include <sys/types.h>
#include <sys/stat.h>
#include <stddef.h>
#include "memio.h"
#define HIO_HANDLE_TYPE(x) ((x)->type)
typedef struct {
#define HIO_HANDLE_TYPE_FILE 0
#define HIO_HANDLE_TYPE_MEMORY 1
int type;
long size;
union {
FILE *file;
MFILE *mem;
} handle;
int error;
} HIO_HANDLE;
int8 hio_read8s (HIO_HANDLE *);
uint8 hio_read8 (HIO_HANDLE *);
uint16 hio_read16l (HIO_HANDLE *);
uint16 hio_read16b (HIO_HANDLE *);
uint32 hio_read24l (HIO_HANDLE *);
uint32 hio_read24b (HIO_HANDLE *);
uint32 hio_read32l (HIO_HANDLE *);
uint32 hio_read32b (HIO_HANDLE *);
size_t hio_read (void *, size_t, size_t, HIO_HANDLE *);
int hio_seek (HIO_HANDLE *, long, int);
long hio_tell (HIO_HANDLE *);
int hio_eof (HIO_HANDLE *);
int hio_error (HIO_HANDLE *);
HIO_HANDLE *hio_open (void *, char *);
HIO_HANDLE *hio_open_mem (void *, long);
HIO_HANDLE *hio_open_file (FILE *);
int hio_close (HIO_HANDLE *);
long hio_size (HIO_HANDLE *);
#endif

View file

@ -0,0 +1,164 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include "lfo.h"
#define WAVEFORM_SIZE 64
static const int sine_wave[WAVEFORM_SIZE] = {
0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224,
235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197,
180, 161, 141, 120, 97, 74, 49, 24, 0, -24, -49, -74,
-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,
-255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,
-97, -74, -49, -24
};
/* LFO */
static int get_lfo_mod(struct lfo *lfo)
{
int val;
if (lfo->rate == 0)
return 0;
switch (lfo->type) {
case 0: /* sine */
val = sine_wave[lfo->phase];
break;
case 1: /* ramp down */
val = 255 - (lfo->phase << 3);
break;
case 2: /* square */
val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : -255;
break;
case 3: /* random */
val = ((rand() & 0x1ff) - 256);
break;
#ifndef LIBXMP_CORE_PLAYER
case 669: /* 669 vibrato */
val = lfo->phase & 1;
break;
#endif
default:
return 0;
}
return val * lfo->depth;
}
static int get_lfo_st3(struct lfo *lfo)
{
if (lfo->rate == 0) {
return 0;
}
/* S3M square */
if (lfo->type == 2) {
int val = lfo->phase < WAVEFORM_SIZE / 2 ? 255 : 0;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
/* From OpenMPT VibratoWaveforms.xm:
* "Generally the vibrato and tremolo tables are identical to those that
* ProTracker uses, but the vibratos ramp down table is upside down."
*/
static int get_lfo_ft2(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
/* FT2 ramp */
if (lfo->type == 1) {
int phase = (lfo->phase + (WAVEFORM_SIZE >> 1)) % WAVEFORM_SIZE;
int val = (phase << 3) - 255;
return val * lfo->depth;
}
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
static int get_lfo_it(struct lfo *lfo)
{
if (lfo->rate == 0)
return 0;
return get_lfo_st3(lfo);
}
#endif
int libxmp_lfo_get(struct context_data *ctx, struct lfo *lfo, int is_vibrato)
{
struct module_data *m = &ctx->m;
switch (m->read_event_type) {
case READ_EVENT_ST3:
return get_lfo_st3(lfo);
case READ_EVENT_FT2:
if (is_vibrato) {
return get_lfo_ft2(lfo);
} else {
return get_lfo_mod(lfo);
}
#ifndef LIBXMP_CORE_DISABLE_IT
case READ_EVENT_IT:
return get_lfo_it(lfo);
#endif
default:
return get_lfo_mod(lfo);
}
}
void libxmp_lfo_update(struct lfo *lfo)
{
lfo->phase += lfo->rate;
lfo->phase %= WAVEFORM_SIZE;
}
void libxmp_lfo_set_phase(struct lfo *lfo, int phase)
{
lfo->phase = phase;
}
void libxmp_lfo_set_depth(struct lfo *lfo, int depth)
{
lfo->depth = depth;
}
void libxmp_lfo_set_rate(struct lfo *lfo, int rate)
{
lfo->rate = rate;
}
void libxmp_lfo_set_waveform(struct lfo *lfo, int type)
{
lfo->type = type;
}

View file

@ -0,0 +1,20 @@
#ifndef LIBXMP_LFO_H
#define LIBXMP_LFO_H
#include "common.h"
struct lfo {
int type;
int rate;
int depth;
int phase;
};
int libxmp_lfo_get(struct context_data *, struct lfo *, int);
void libxmp_lfo_update(struct lfo *);
void libxmp_lfo_set_phase(struct lfo *, int);
void libxmp_lfo_set_depth(struct lfo *, int);
void libxmp_lfo_set_rate(struct lfo *, int);
void libxmp_lfo_set_waveform(struct lfo *, int);
#endif

View file

@ -0,0 +1,143 @@
#ifndef LIBXMP_LIST_H
#define LIBXMP_LIST_H
#ifdef _MSC_VER
#define __inline__ __inline
#endif
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_add(struct list_head *_new,
struct list_head * prev,
struct list_head * next)
{
next->prev = _new;
_new->next = next;
_new->prev = prev;
prev->next = _new;
}
/**
* list_add - add a new entry
* @_new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static __inline__ void list_add(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @_new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static __inline__ void __list_del(struct list_head * prev,
struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static __inline__ void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static __inline__ int list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static __inline__ void list_splice(struct list_head *list, struct list_head *head)
{
struct list_head *first = list->next;
if (first != list) {
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(size_t)(&((type *)0)->member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
#endif

View file

@ -0,0 +1,678 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef __native_client__
#include <sys/syslimits.h>
#else
#include <limits.h>
#endif
#include "format.h"
#include "list.h"
#include "hio.h"
#include "tempfile.h"
#ifndef LIBXMP_CORE_PLAYER
#if !defined(HAVE_POPEN) && defined(WIN32)
#include "win32/ptpopen.h"
#endif
#include "md5.h"
#include "extras.h"
#endif
extern struct format_loader *format_loader[];
void libxmp_load_prologue(struct context_data *);
void libxmp_load_epilogue(struct context_data *);
int libxmp_prepare_scan(struct context_data *);
#ifndef LIBXMP_CORE_PLAYER
#include "depacker.h"
static struct depacker *depacker_list[] = {
#if defined __AMIGA__ && !defined __AROS__
&libxmp_depacker_xfd,
#endif
&libxmp_depacker_zip,
&libxmp_depacker_lha,
&libxmp_depacker_gzip,
&libxmp_depacker_bzip2,
&libxmp_depacker_xz,
&libxmp_depacker_compress,
&libxmp_depacker_pp,
&libxmp_depacker_sqsh,
&libxmp_depacker_arcfs,
&libxmp_depacker_mmcmp,
&libxmp_depacker_muse,
&libxmp_depacker_lzx,
&libxmp_depacker_s404,
&libxmp_depacker_arc,
NULL
};
int test_oxm (FILE *);
#define BUFLEN 16384
static int execute_command(char *cmd, char *filename, FILE *t)
{
char line[1024], buf[BUFLEN];
FILE *p;
int n;
snprintf(line, 1024, cmd, filename);
#ifdef WIN32
/* Note: The _popen function returns an invalid file opaque, if
* used in a Windows program, that will cause the program to hang
* indefinitely. _popen works properly in a Console application.
* To create a Windows application that redirects input and output,
* read the section "Creating a Child Process with Redirected Input
* and Output" in the Win32 SDK. -- Mirko
*/
p = popen(line, "rb");
#else
/* Linux popen fails with "rb" */
p = popen(line, "r");
#endif
if (p == NULL) {
return -1;
}
while ((n = fread(buf, 1, BUFLEN, p)) > 0) {
fwrite(buf, 1, n, t);
}
pclose (p);
return 0;
}
static int decrunch(HIO_HANDLE **h, char *filename, char **temp)
{
unsigned char b[1024];
char *cmd;
FILE *f, *t;
int res;
int headersize;
int i;
struct depacker *depacker = NULL;
cmd = NULL;
res = 0;
*temp = NULL;
f = (*h)->handle.file;
headersize = fread(b, 1, 1024, f);
if (headersize < 100) { /* minimum valid file size */
return 0;
}
/* Check built-in depackers */
for (i = 0; depacker_list[i] != NULL; i++) {
if (depacker_list[i]->test(b)) {
depacker = depacker_list[i];
D_(D_INFO "Use depacker %d", i);
break;
}
}
/* Check external commands */
if (depacker == NULL) {
if (b[0] == 'M' && b[1] == 'O' && b[2] == '3') {
/* MO3 */
D_(D_INFO "mo3");
cmd = "unmo3 -s \"%s\" STDOUT";
} else if (memcmp(b, "Rar", 3) == 0) {
/* rar */
D_(D_INFO "rar");
cmd = "unrar p -inul -xreadme -x*.diz -x*.nfo -x*.txt "
"-x*.exe -x*.com \"%s\"";
} else if (test_oxm(f) == 0) {
/* oggmod */
D_(D_INFO "oggmod");
depacker = &libxmp_depacker_oxm;
}
}
if (fseek(f, 0, SEEK_SET) < 0) {
goto err;
}
if (depacker == NULL && cmd == NULL) {
D_(D_INFO "Not packed");
return 0;
}
#if defined __ANDROID__ || defined __native_client__
/* Don't use external helpers in android */
if (cmd) {
return 0;
}
#endif
D_(D_WARN "Depacking file... ");
if ((t = make_temp_file(temp)) == NULL) {
goto err;
}
/* Depack file */
if (cmd) {
D_(D_INFO "External depacker: %s", cmd);
if (execute_command(cmd, filename, t) < 0) {
D_(D_CRIT "failed");
goto err2;
}
} else if (depacker) {
D_(D_INFO "Internal depacker");
if (depacker->depack(f, t) < 0) {
D_(D_CRIT "failed");
goto err2;
}
}
D_(D_INFO "done");
if (fseek(t, 0, SEEK_SET) < 0) {
D_(D_CRIT "fseek error");
goto err2;
}
hio_close(*h);
*h = hio_open_file(t);
return res;
err2:
fclose(t);
err:
return -1;
}
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest)
{
unsigned char buf[BUFLEN];
MD5_CTX ctx;
int bytes_read;
if (hio_size(f) <= 0) {
memset(digest, 0, 16);
return;
}
hio_seek(f, 0, SEEK_SET);
MD5Init(&ctx);
while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) {
MD5Update(&ctx, buf, bytes_read);
}
MD5Final(digest, &ctx);
}
static char *get_dirname(char *name)
{
char *div, *dirname;
int len;
if ((div = strrchr(name, '/'))) {
len = div - name + 1;
dirname = malloc(len + 1);
if (dirname != NULL) {
memcpy(dirname, name, len);
dirname[len] = 0;
}
} else {
dirname = strdup("");
}
return dirname;
}
static char *get_basename(char *name)
{
char *div, *basename;
if ((div = strrchr(name, '/'))) {
basename = strdup(div + 1);
} else {
basename = strdup(name);
}
return basename;
}
#endif /* LIBXMP_CORE_PLAYER */
int xmp_test_module(char *path, struct xmp_test_info *info)
{
HIO_HANDLE *h;
struct stat st;
char buf[XMP_NAME_SIZE];
int i;
int ret = -XMP_ERROR_FORMAT;
#ifndef LIBXMP_CORE_PLAYER
char *temp = NULL;
#endif
if (stat(path, &st) < 0)
return -XMP_ERROR_SYSTEM;
#ifndef _MSC_VER
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
#endif
if ((h = hio_open(path, "rb")) == NULL)
return -XMP_ERROR_SYSTEM;
#ifndef LIBXMP_CORE_PLAYER
if (decrunch(&h, path, &temp) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
/* get size after decrunch */
if (hio_size(h) < 256) { /* set minimum valid module size */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
if (info != NULL) {
*info->name = 0; /* reset name prior to testing */
*info->type = 0; /* reset type prior to testing */
}
for (i = 0; format_loader[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
if (format_loader[i]->test(h, buf, 0) == 0) {
int is_prowizard = 0;
#ifndef LIBXMP_CORE_PLAYER
if (strcmp(format_loader[i]->name, "prowizard") == 0) {
hio_seek(h, 0, SEEK_SET);
pw_test_format(h, buf, 0, info);
is_prowizard = 1;
}
#endif
fclose(h->handle.file);
#ifndef LIBXMP_CORE_PLAYER
unlink_temp_file(temp);
#endif
if (info != NULL && !is_prowizard) {
strncpy(info->name, buf, XMP_NAME_SIZE - 1);
strncpy(info->type, format_loader[i]->name,
XMP_NAME_SIZE - 1);
}
return 0;
}
}
#ifndef LIBXMP_CORE_PLAYER
err:
hio_close(h);
unlink_temp_file(temp);
#else
hio_close(h);
#endif
return ret;
}
static int load_module(xmp_context opaque, HIO_HANDLE *h)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j, ret;
int test_result, load_result;
libxmp_load_prologue(ctx);
D_(D_WARN "load");
test_result = load_result = -1;
for (i = 0; format_loader[i] != NULL; i++) {
hio_seek(h, 0, SEEK_SET);
if (hio_error(h)) {
/* reset error flag */
}
D_(D_WARN "test %s", format_loader[i]->name);
test_result = format_loader[i]->test(h, NULL, 0);
if (test_result == 0) {
hio_seek(h, 0, SEEK_SET);
D_(D_WARN "load format: %s", format_loader[i]->name);
load_result = format_loader[i]->loader(m, h, 0);
break;
}
}
#ifndef LIBXMP_CORE_PLAYER
if (test_result == 0 && load_result == 0)
set_md5sum(h, m->md5);
#endif
if (test_result < 0) {
free(m->basename);
free(m->dirname);
return -XMP_ERROR_FORMAT;
}
if (load_result < 0) {
goto err_load;
}
/* Sanity check: number of channels, module length */
if (mod->chn > XMP_MAX_CHANNELS || mod->len > XMP_MAX_MOD_LENGTH) {
goto err_load;
}
/* Sanity check: channel pan */
for (i = 0; i < mod->chn; i++) {
if (mod->xxc[i].vol < 0 || mod->xxc[i].vol > 0xff) {
goto err_load;
}
if (mod->xxc[i].pan < 0 || mod->xxc[i].pan > 0xff) {
goto err_load;
}
}
/* Sanity check: patterns */
if (mod->xxp == NULL) {
goto err_load;
}
for (i = 0; i < mod->pat; i++) {
if (mod->xxp[i] == NULL) {
goto err_load;
}
for (j = 0; j < mod->chn; j++) {
int t = mod->xxp[i]->index[j];
if (t < 0 || t >= mod->trk || mod->xxt[t] == NULL) {
goto err_load;
}
}
}
libxmp_adjust_string(mod->name);
for (i = 0; i < mod->ins; i++) {
libxmp_adjust_string(mod->xxi[i].name);
}
for (i = 0; i < mod->smp; i++) {
libxmp_adjust_string(mod->xxs[i].name);
}
libxmp_load_epilogue(ctx);
ret = libxmp_prepare_scan(ctx);
if (ret < 0) {
xmp_release_module(opaque);
return ret;
}
libxmp_scan_sequences(ctx);
ctx->state = XMP_STATE_LOADED;
return 0;
err_load:
xmp_release_module(opaque);
return -XMP_ERROR_LOAD;
}
int xmp_load_module(xmp_context opaque, char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
#ifndef LIBXMP_CORE_PLAYER
struct module_data *m = &ctx->m;
long size;
char *temp_name;
#endif
HIO_HANDLE *h;
struct stat st;
int ret;
D_(D_WARN "path = %s", path);
if (stat(path, &st) < 0) {
return -XMP_ERROR_SYSTEM;
}
#ifndef _MSC_VER
if (S_ISDIR(st.st_mode)) {
errno = EISDIR;
return -XMP_ERROR_SYSTEM;
}
#endif
if ((h = hio_open(path, "rb")) == NULL) {
return -XMP_ERROR_SYSTEM;
}
#ifndef LIBXMP_CORE_PLAYER
D_(D_INFO "decrunch");
if (decrunch(&h, path, &temp_name) < 0) {
ret = -XMP_ERROR_DEPACK;
goto err;
}
size = hio_size(h);
if (size < 256) { /* get size after decrunch */
ret = -XMP_ERROR_FORMAT;
goto err;
}
#endif
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
#ifndef LIBXMP_CORE_PLAYER
m->dirname = get_dirname(path);
if (m->dirname == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->basename = get_basename(path);
if (m->basename == NULL) {
ret = -XMP_ERROR_SYSTEM;
goto err;
}
m->filename = path; /* For ALM, SSMT, etc */
m->size = size;
#endif
ret = load_module(opaque, h);
hio_close(h);
#ifndef LIBXMP_CORE_PLAYER
unlink_temp_file(temp_name);
#endif
return ret;
#ifndef LIBXMP_CORE_PLAYER
err:
hio_close(h);
unlink_temp_file(temp_name);
return ret;
#endif
}
int xmp_load_module_from_memory(xmp_context opaque, void *mem, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
int ret;
/* Use size < 0 for unknown/undetermined size */
if (size == 0)
size--;
if ((h = hio_open_mem(mem, size)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = size;
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
/*
int xmp_load_module_from_file(xmp_context opaque, void *file, long size)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
HIO_HANDLE *h;
FILE *f = fdopen(fileno((FILE *)file), "rb");
int ret;
if ((h = hio_open_file(f)) == NULL)
return -XMP_ERROR_SYSTEM;
if (ctx->state > XMP_STATE_UNLOADED)
xmp_release_module(opaque);
m->filename = NULL;
m->basename = NULL;
m->dirname = NULL;
m->size = hio_size(h);
ret = load_module(opaque, h);
hio_close(h);
return ret;
}
*/
void xmp_release_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i;
/* can't test this here, we must call release_module to clean up
* load errors
if (ctx->state < XMP_STATE_LOADED)
return;
*/
if (ctx->state > XMP_STATE_LOADED)
xmp_end_player(opaque);
ctx->state = XMP_STATE_UNLOADED;
D_(D_INFO "Freeing memory");
#ifndef LIBXMP_CORE_PLAYER
libxmp_release_module_extras(ctx);
#endif
if (mod->xxt != NULL) {
for (i = 0; i < mod->trk; i++) {
free(mod->xxt[i]);
}
free(mod->xxt);
}
if (mod->xxp != NULL) {
for (i = 0; i < mod->pat; i++) {
free(mod->xxp[i]);
}
free(mod->xxp);
}
if (mod->xxi != NULL) {
for (i = 0; i < mod->ins; i++) {
free(mod->xxi[i].sub);
free(mod->xxi[i].extra);
}
free(mod->xxi);
}
if (mod->xxs != NULL) {
for (i = 0; i < mod->smp; i++) {
if (mod->xxs[i].data != NULL) {
free(mod->xxs[i].data - 4);
}
}
free(mod->xxs);
free(m->xtra);
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (m->xsmp != NULL) {
for (i = 0; i < mod->smp; i++) {
if (m->xsmp[i].data != NULL) {
free(m->xsmp[i].data - 4);
}
}
free(m->xsmp);
}
#endif
if (m->scan_cnt) {
for (i = 0; i < mod->len; i++)
free(m->scan_cnt[i]);
free(m->scan_cnt);
}
free(m->comment);
D_("free dirname/basename");
free(m->dirname);
free(m->basename);
}
void xmp_scan_module(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
if (ctx->state < XMP_STATE_LOADED)
return;
libxmp_scan_sequences(ctx);
}

View file

@ -0,0 +1,425 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"
#include "loaders/loader.h"
#ifndef LIBXMP_CORE_PLAYER
#ifdef __ANDROID__
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#include <fnmatch.h>
/*
* Handle special "module quirks" that can't be detected automatically
* such as Protracker 2.x compatibility, vblank timing, etc.
*/
struct module_quirk {
uint8 md5[16];
int flags;
int mode;
};
const struct module_quirk mq[] = {
/* "No Mercy" by Alf/VTL (added by Martin Willers) */
{
{ 0x36, 0x6e, 0xc0, 0xfa, 0x96, 0x2a, 0xeb, 0xee,
0x03, 0x4a, 0xa2, 0xdb, 0xaa, 0x49, 0xaa, 0xea },
0, XMP_MODE_PROTRACKER
},
/* mod.souvenir of china */
{
{ 0x93, 0xf1, 0x46, 0xae, 0xb7, 0x58, 0xc3, 0x9d,
0x8b, 0x5f, 0xbc, 0x98, 0xbf, 0x23, 0x7a, 0x43 },
XMP_FLAGS_FIXLOOP, XMP_MODE_AUTO
},
/* "siedler ii" (added by Daniel Åkerud) */
{
{ 0x70, 0xaa, 0x03, 0x4d, 0xfb, 0x2f, 0x1f, 0x73,
0xd9, 0xfd, 0xba, 0xfe, 0x13, 0x1b, 0xb7, 0x01 },
XMP_FLAGS_VBLANK, XMP_MODE_AUTO
},
/* "Klisje paa klisje" (added by Kjetil Torgrim Homme) */
{
{ 0xe9, 0x98, 0x01, 0x2c, 0x70, 0x0e, 0xb4, 0x3a,
0xf0, 0x32, 0x17, 0x11, 0x30, 0x58, 0x29, 0xb2 },
0, XMP_MODE_NOISETRACKER
},
#if 0
/* -- Already covered by Noisetracker fingerprinting -- */
/* Another version of Klisje paa klisje sent by Steve Fernandez */
{
{ 0x12, 0x19, 0x1c, 0x90, 0x41, 0xe3, 0xfd, 0x70,
0xb7, 0xe6, 0xb3, 0x94, 0x8b, 0x21, 0x07, 0x63 },
XMP_FLAGS_VBLANK
},
#endif
/* "((((( nebulos )))))" sent by Tero Auvinen (AMP version) */
{
{ 0x51, 0x6e, 0x8d, 0xcc, 0x35, 0x7d, 0x50, 0xde,
0xa9, 0x85, 0xbe, 0xbf, 0x90, 0x2e, 0x42, 0xdc },
0, XMP_MODE_NOISETRACKER
},
/* Purple Motion's Sundance.mod, Music Channel BBS edit */
{
{ 0x5d, 0x3e, 0x1e, 0x08, 0x28, 0x52, 0x12, 0xc7,
0x17, 0x64, 0x95, 0x75, 0x98, 0xe6, 0x95, 0xc1 },
0, XMP_MODE_ST3
},
/* Asle's Ode to Protracker */
{
{ 0x97, 0xa3, 0x7d, 0x30, 0xd7, 0xae, 0x6d, 0x50,
0xc9, 0x62, 0xe9, 0xd8, 0x87, 0x1b, 0x7e, 0x8a },
0, XMP_MODE_PROTRACKER
},
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
0, 0
}
};
static void module_quirks(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i;
for (i = 0; mq[i].flags != 0 || mq[i].mode != 0; i++) {
if (!memcmp(m->md5, mq[i].md5, 16)) {
p->flags |= mq[i].flags;
p->mode = mq[i].mode;
}
}
}
/*
* Check whether the given string matches one of the blacklisted glob
* patterns. Used to filter file names stored in archive files.
*/
int libxmp_exclude_match(char *name)
{
int i;
static const char *const exclude[] = {
"README", "readme",
"*.DIZ", "*.diz",
"*.NFO", "*.nfo",
"*.DOC", "*.Doc", "*.doc",
"*.INFO", "*.info", "*.Info",
"*.TXT", "*.txt",
"*.EXE", "*.exe",
"*.COM", "*.com",
"*.README", "*.readme", "*.Readme", "*.ReadMe",
NULL
};
for (i = 0; exclude[i] != NULL; i++) {
if (fnmatch(exclude[i], name, 0) == 0) {
return 1;
}
}
return 0;
}
#endif /* LIBXMP_CORE_PLAYER */
char *libxmp_adjust_string(char *s)
{
int i;
for (i = 0; i < strlen(s); i++) {
if (!isprint((int)s[i]) || ((uint8) s[i] > 127))
s[i] = ' ';
}
while (*s && (s[strlen(s) - 1] == ' ')) {
s[strlen(s) - 1] = 0;
}
return s;
}
static void check_envelope(struct xmp_envelope *env)
{
/* Disable envelope if invalid number of points */
if (env->npt <= 0 || env->npt > XMP_MAX_ENV_POINTS) {
env->flg &= ~XMP_ENVELOPE_ON;
}
/* Disable envelope loop if invalid loop parameters */
if (env->lps >= env->npt || env->lpe >= env->npt) {
env->flg &= ~XMP_ENVELOPE_LOOP;
}
/* Disable envelope loop if invalid sustain */
if (env->sus >= env->npt) {
env->flg &= ~XMP_ENVELOPE_ON;
}
}
void libxmp_load_prologue(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
int i;
/* Reset variables */
memset(&m->mod, 0, sizeof (struct xmp_module));
m->rrate = PAL_RATE;
m->c4rate = C4_PAL_RATE;
m->volbase = 0x40;
m->gvol = m->gvolbase = 0x40;
m->vol_table = NULL;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
m->comment = NULL;
m->scan_cnt = NULL;
/* Set defaults */
m->mod.pat = 0;
m->mod.trk = 0;
m->mod.chn = 4;
m->mod.ins = 0;
m->mod.smp = 0;
m->mod.spd = 6;
m->mod.bpm = 125;
m->mod.len = 0;
m->mod.rst = 0;
#ifndef LIBXMP_CORE_PLAYER
m->extra = NULL;
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
m->xsmp = NULL;
#endif
m->time_factor = DEFAULT_TIME_FACTOR;
for (i = 0; i < 64; i++) {
int pan = (((i + 1) / 2) % 2) * 0xff;
m->mod.xxc[i].pan = 0x80 + (pan - 0x80) * m->defpan / 100;
m->mod.xxc[i].vol = 0x40;
m->mod.xxc[i].flg = 0;
}
}
void libxmp_load_epilogue(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, j;
mod->gvl = m->gvol;
/* Sanity check for module parameters */
CLAMP(mod->len, 0, XMP_MAX_MOD_LENGTH);
CLAMP(mod->pat, 0, 257); /* some formats have an extra pattern */
CLAMP(mod->ins, 0, 255);
CLAMP(mod->smp, 0, MAX_SAMPLES);
CLAMP(mod->chn, 0, XMP_MAX_CHANNELS);
/* Fix cases where the restart value is invalid e.g. kc_fall8.xm
* from http://aminet.net/mods/mvp/mvp_0002.lha (reported by
* Ralf Hoffmann <ralf@boomerangsworld.de>)
*/
if (mod->rst >= mod->len) {
mod->rst = 0;
}
/* Sanity check for tempo and BPM */
if (mod->spd <= 0 || mod->spd > 255) {
mod->spd = 6;
}
CLAMP(mod->bpm, XMP_MIN_BPM, 255);
/* Set appropriate values for instrument volumes and subinstrument
* global volumes when QUIRK_INSVOL is not set, to keep volume values
* consistent if the user inspects struct xmp_module. We can later
* set volumes in the loaders and eliminate the quirk.
*/
for (i = 0; i < mod->ins; i++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].vol = m->volbase;
}
for (j = 0; j < mod->xxi[i].nsm; j++) {
if (~m->quirk & QUIRK_INSVOL) {
mod->xxi[i].sub[j].gvl = m->volbase;
}
}
}
/* Sanity check for envelopes
*/
for (i = 0; i < mod->ins; i++) {
check_envelope(&mod->xxi[i].aei);
check_envelope(&mod->xxi[i].fei);
check_envelope(&mod->xxi[i].pei);
}
p->filter = 0;
p->mode = XMP_MODE_AUTO;
p->flags = p->player_flags;
#ifndef LIBXMP_CORE_PLAYER
module_quirks(ctx);
#endif
libxmp_set_player_mode(ctx);
}
int libxmp_prepare_scan(struct context_data *ctx)
{
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, ord;
if (mod->xxp == NULL || mod->xxt == NULL)
return -XMP_ERROR_LOAD;
ord = 0;
while (ord < mod->len && mod->xxo[ord] >= mod->pat) {
ord++;
}
if (ord >= mod->len) {
mod->len = 0;
return 0;
}
m->scan_cnt = calloc(sizeof (char *), mod->len);
if (m->scan_cnt == NULL)
return -XMP_ERROR_SYSTEM;
for (i = 0; i < mod->len; i++) {
int pat_idx = mod->xxo[i];
struct xmp_pattern *pat;
/* Add pattern if referenced in orders */
if (pat_idx < mod->pat && !mod->xxp[pat_idx]) {
if (libxmp_alloc_pattern(mod, pat_idx) < 0) {
return -XMP_ERROR_SYSTEM;
}
}
pat = pat_idx >= mod->pat ? NULL : mod->xxp[pat_idx];
m->scan_cnt[i] = calloc(1, pat && pat->rows ? pat->rows : 1);
if (m->scan_cnt[i] == NULL)
return -XMP_ERROR_SYSTEM;
}
return 0;
}
/* Process player personality flags */
int libxmp_set_player_mode(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int q;
switch (p->mode) {
case XMP_MODE_AUTO:
break;
case XMP_MODE_MOD:
m->c4rate = C4_PAL_RATE;
m->quirk = 0;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_AMIGA;
break;
case XMP_MODE_NOISETRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_NOBPM;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_PROTRACKER:
m->c4rate = C4_PAL_RATE;
m->quirk = QUIRK_PROTRACK;
m->read_event_type = READ_EVENT_MOD;
m->period_type = PERIOD_MODRNG;
break;
case XMP_MODE_S3M:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_ST3GUS:
q = m->quirk & (QUIRK_VSALL | QUIRK_ARPMEM);
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_ST3 | QUIRK_ST3BUGS | q;
m->quirk &= ~QUIRK_RSTCHN;
m->read_event_type = READ_EVENT_ST3;
break;
case XMP_MODE_XM:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_FT2:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_FT2 | QUIRK_FT2BUGS;
m->read_event_type = READ_EVENT_FT2;
break;
case XMP_MODE_IT:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->read_event_type = READ_EVENT_IT;
break;
case XMP_MODE_ITSMP:
m->c4rate = C4_NTSC_RATE;
m->quirk = QUIRKS_IT | QUIRK_VIBHALF | QUIRK_VIBINV;
m->quirk &= ~(QUIRK_VIRTUAL | QUIRK_RSTCHN);
m->read_event_type = READ_EVENT_IT;
break;
default:
return -1;
}
return 0;
}

View file

@ -0,0 +1,15 @@
LOADERS = xm_load.o mod_load.o s3m_load.o it_load.o
LOADERS_OBJS = common.o itsex.o sample.o $(LOADERS)
LOADERS_DFILES = Makefile $(LOADERS_OBJS:.o=.c) \
it.h loader.h mod.h s3m.h xm.h
LOADERS_PATH = src/loaders
OBJS += $(addprefix $(LOADERS_PATH)/,$(LOADERS_OBJS))
default:
dist-loaders::
mkdir -p $(DIST)/$(LOADERS_PATH)
cp -RPp $(addprefix $(LOADERS_PATH)/,$(LOADERS_DFILES)) $(DIST)/$(LOADERS_PATH)

View file

@ -0,0 +1,364 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <ctype.h>
#include <sys/types.h>
#include <stdarg.h>
#ifndef WIN32
#include <dirent.h>
#endif
#include "xmp.h"
#include "../common.h"
#include "../period.h"
#include "loader.h"
int libxmp_init_instrument(struct module_data *m)
{
struct xmp_module *mod = &m->mod;
if (mod->ins > 0) {
mod->xxi = calloc(sizeof (struct xmp_instrument), mod->ins);
if (mod->xxi == NULL)
return -1;
}
if (mod->smp > 0) {
int i;
mod->xxs = calloc(sizeof (struct xmp_sample), mod->smp);
if (mod->xxs == NULL)
return -1;
m->xtra = calloc(sizeof (struct extra_sample_data), mod->smp);
if (m->xtra == NULL)
return -1;
for (i = 0; i < mod->smp; i++) {
m->xtra[i].c5spd = m->c4rate;
}
}
return 0;
}
int libxmp_alloc_subinstrument(struct xmp_module *mod, int i, int num)
{
if (num == 0)
return 0;
mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), num);
if (mod->xxi[i].sub == NULL)
return -1;
return 0;
}
int libxmp_init_pattern(struct xmp_module *mod)
{
mod->xxt = calloc(sizeof (struct xmp_track *), mod->trk);
if (mod->xxt == NULL)
return -1;
mod->xxp = calloc(sizeof (struct xmp_pattern *), mod->pat);
if (mod->xxp == NULL)
return -1;
return 0;
}
int libxmp_alloc_pattern(struct xmp_module *mod, int num)
{
/* Sanity check */
if (num < 0 || num >= mod->pat || mod->xxp[num] != NULL)
return -1;
mod->xxp[num] = calloc(1, sizeof (struct xmp_pattern) +
sizeof (int) * (mod->chn - 1));
if (mod->xxp[num] == NULL)
return -1;
return 0;
}
int libxmp_alloc_track(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (num < 0 || num >= mod->trk || mod->xxt[num] != NULL || rows <= 0)
return -1;
mod->xxt[num] = calloc(sizeof (struct xmp_track) +
sizeof (struct xmp_event) * (rows - 1), 1);
if (mod->xxt[num] == NULL)
return -1;
mod->xxt[num]->rows = rows;
return 0;
}
int libxmp_alloc_tracks_in_pattern(struct xmp_module *mod, int num)
{
int i;
D_(D_INFO "Alloc %d tracks of %d rows", mod->chn, mod->xxp[num]->rows);
for (i = 0; i < mod->chn; i++) {
int t = num * mod->chn + i;
int rows = mod->xxp[num]->rows;
if (libxmp_alloc_track(mod, t, rows) < 0)
return -1;
mod->xxp[num]->index[i] = t;
}
return 0;
}
int libxmp_alloc_pattern_tracks(struct xmp_module *mod, int num, int rows)
{
/* Sanity check */
if (rows < 0 || rows > 256)
return -1;
if (libxmp_alloc_pattern(mod, num) < 0)
return -1;
mod->xxp[num]->rows = rows;
if (libxmp_alloc_tracks_in_pattern(mod, num) < 0)
return -1;
return 0;
}
/* Sample number adjustment by Vitamin/CAIG */
struct xmp_sample *libxmp_realloc_samples(struct xmp_sample *buf, int *size, int new_size)
{
buf = realloc(buf, sizeof (struct xmp_sample) * new_size);
if (buf == NULL)
return NULL;
if (new_size > *size)
memset(buf + *size, 0, sizeof (struct xmp_sample) * (new_size - *size));
*size = new_size;
return buf;
}
char *libxmp_instrument_name(struct xmp_module *mod, int i, uint8 *r, int n)
{
CLAMP(n, 0, 31);
return libxmp_copy_adjust(mod->xxi[i].name, r, n);
}
char *libxmp_copy_adjust(char *s, uint8 *r, int n)
{
int i;
memset(s, 0, n + 1);
strncpy(s, (char *)r, n);
for (i = 0; s[i] && i < n; i++) {
if (!isprint((int)s[i]) || ((uint8)s[i] > 127))
s[i] = '.';
}
while (*s && (s[strlen(s) - 1] == ' '))
s[strlen(s) - 1] = 0;
return s;
}
void libxmp_read_title(HIO_HANDLE *f, char *t, int s)
{
uint8 buf[XMP_NAME_SIZE];
if (t == NULL)
return;
if (s >= XMP_NAME_SIZE)
s = XMP_NAME_SIZE -1;
memset(t, 0, s + 1);
hio_read(buf, 1, s, f); /* coverity[check_return] */
buf[s] = 0;
libxmp_copy_adjust(t, buf, s);
}
#ifndef LIBXMP_CORE_PLAYER
int libxmp_test_name(uint8 *s, int n)
{
int i;
/* ACS_Team2.mod has a backspace in instrument name */
for (i = 0; i < n; i++) {
if (s[i] > 0x7f)
return -1;
if (s[i] > 0 && s[i] < 32 && s[i] != 0x08)
return -1;
}
return 0;
}
/*
* Honor Noisetracker effects:
*
* 0 - arpeggio
* 1 - portamento up
* 2 - portamento down
* 3 - Tone-portamento
* 4 - Vibrato
* A - Slide volume
* B - Position jump
* C - Set volume
* D - Pattern break
* E - Set filter (keep the led off, please!)
* F - Set speed (now up to $1F)
*
* Pex Tufvesson's notes from http://www.livet.se/mahoney/:
*
* Note that some of the modules will have bugs in the playback with all
* known PC module players. This is due to that in many demos where I synced
* events in the demo with the music, I used commands that these newer PC
* module players erroneously interpret as "newer-version-trackers commands".
* Which they aren't.
*/
void libxmp_decode_noisetracker_event(struct xmp_event *event, uint8 *mod_event)
{
int fxt;
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
fxt = LSN(mod_event[2]);
if (fxt <= 0x06 || (fxt >= 0x0a && fxt != 0x0e)) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
#endif
void libxmp_decode_protracker_event(struct xmp_event *event, uint8 *mod_event)
{
int fxt = LSN(mod_event[2]);
memset(event, 0, sizeof (struct xmp_event));
event->note = libxmp_period_to_note((LSN(mod_event[0]) << 8) + mod_event[1]);
event->ins = ((MSN(mod_event[0]) << 4) | MSN(mod_event[2]));
if (fxt != 0x08) {
event->fxt = fxt;
event->fxp = mod_event[3];
}
libxmp_disable_continue_fx(event);
}
void libxmp_disable_continue_fx(struct xmp_event *event)
{
if (event->fxp == 0) {
switch (event->fxt) {
case 0x05:
event->fxt = 0x03;
break;
case 0x06:
event->fxt = 0x04;
break;
case 0x01:
case 0x02:
case 0x0a:
event->fxt = 0x00;
}
} else if (event->fxt == 0x0e) {
if (event->fxp == 0xa0 || event->fxp == 0xb0) {
event->fxt = event->fxp = 0;
}
}
}
#ifndef LIBXMP_CORE_PLAYER
#ifndef WIN32
/* Given a directory, see if file exists there, ignoring case */
int libxmp_check_filename_case(char *dir, char *name, char *new_name, int size)
{
int found = 0;
DIR *dirfd;
struct dirent *d;
dirfd = opendir(dir);
if (dirfd == NULL)
return 0;
while ((d = readdir(dirfd))) {
if (!strcasecmp(d->d_name, name)) {
found = 1;
break;
}
}
if (found)
strncpy(new_name, d->d_name, size);
closedir(dirfd);
return found;
}
#else
/* FIXME: implement functionality for Win32 */
int libxmp_check_filename_case(char *dir, char *name, char *new_name, int size)
{
return 0;
}
#endif
void libxmp_get_instrument_path(struct module_data *m, char *path, int size)
{
if (m->instrument_path) {
strncpy(path, m->instrument_path, size);
} else if (getenv("XMP_INSTRUMENT_PATH")) {
strncpy(path, getenv("XMP_INSTRUMENT_PATH"), size);
} else {
strncpy(path, ".", size);
}
}
#endif /* LIBXMP_CORE_PLAYER */
void libxmp_set_type(struct module_data *m, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(m->mod.type, XMP_NAME_SIZE, fmt, ap);
va_end(ap);
}

View file

@ -0,0 +1,181 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* IT flags */
#define IT_STEREO 0x01
#define IT_VOL_OPT 0x02 /* Not recognized */
#define IT_USE_INST 0x04
#define IT_LINEAR_FREQ 0x08
#define IT_OLD_FX 0x10
#define IT_LINK_GXX 0x20
/* IT special */
#define IT_HAS_MSG 0x01
/* IT instrument flags */
#define IT_INST_SAMPLE 0x01
#define IT_INST_16BIT 0x02
#define IT_INST_STEREO 0x04
#define IT_INST_LOOP 0x10
#define IT_INST_SLOOP 0x20
#define IT_INST_BLOOP 0x40
#define IT_INST_BSLOOP 0x80
/* IT sample flags */
#define IT_SMP_SAMPLE 0x01
#define IT_SMP_16BIT 0x02
#define IT_SMP_STEREO 0x04 /* unsupported */
#define IT_SMP_COMP 0x08 /* unsupported */
#define IT_SMP_LOOP 0x10
#define IT_SMP_SLOOP 0x20
#define IT_SMP_BLOOP 0x40
#define IT_SMP_BSLOOP 0x80
/* IT sample conversion flags */
#define IT_CVT_SIGNED 0x01
#define IT_CVT_BIGEND 0x02 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_DIFF 0x04 /* Compressed sample flag */
#define IT_CVT_BYTEDIFF 0x08 /* 'safe to ignore' according to ittech.txt */
#define IT_CVT_12BIT 0x10 /* 'safe to ignore' according to ittech.txt */
/* IT envelope flags */
#define IT_ENV_ON 0x01
#define IT_ENV_LOOP 0x02
#define IT_ENV_SLOOP 0x04
#define IT_ENV_CARRY 0x08
#define IT_ENV_FILTER 0x80
struct it_file_header {
uint32 magic; /* 'IMPM' */
uint8 name[26]; /* ASCIIZ Song name */
uint8 hilite_min; /* Pattern editor highlight */
uint8 hilite_maj; /* Pattern editor highlight */
uint16 ordnum; /* Number of orders (must be even) */
uint16 insnum; /* Number of instruments */
uint16 smpnum; /* Number of samples */
uint16 patnum; /* Number of patterns */
uint16 cwt; /* Tracker ID and version */
uint16 cmwt; /* Format version */
uint16 flags; /* Flags */
uint16 special; /* More flags */
uint8 gv; /* Global volume */
uint8 mv; /* Master volume */
uint8 is; /* Initial speed */
uint8 it; /* Initial tempo */
uint8 sep; /* Panning separation */
uint8 pwd; /* Pitch wheel depth */
uint16 msglen; /* Message length */
uint32 msgofs; /* Message offset */
uint32 rsvd; /* Reserved */
uint8 chpan[64]; /* Channel pan settings */
uint8 chvol[64]; /* Channel volume settings */
};
struct it_instrument1_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 flags; /* Instrument flags */
uint8 vls; /* Volume loop start */
uint8 vle; /* Volume loop end */
uint8 sls; /* Sustain loop start */
uint8 sle; /* Sustain loop end */
uint16 rsvd1; /* Reserved */
uint16 fadeout; /* Fadeout (release) */
uint8 nna; /* New note action */
uint8 dnc; /* Duplicate note check */
uint16 trkvers; /* Tracker version */
uint8 nos; /* Number of samples */
uint8 rsvd2; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 rsvd3[6]; /* Reserved */
uint8 keys[240];
uint8 epoint[200];
uint8 enode[50];
};
struct it_instrument2_header {
uint32 magic; /* 'IMPI' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 nna; /* New Note Action */
uint8 dct; /* Duplicate Check Type */
uint8 dca; /* Duplicate Check Action */
uint16 fadeout;
uint8 pps; /* Pitch-Pan Separation */
uint8 ppc; /* Pitch-Pan Center */
uint8 gbv; /* Global Volume */
uint8 dfp; /* Default pan */
uint8 rv; /* Random volume variation */
uint8 rp; /* Random pan variation */
uint16 trkvers; /* Not used: tracked version */
uint8 nos; /* Not used: number of samples */
uint8 rsvd1; /* Reserved */
uint8 name[26]; /* ASCIIZ Instrument name */
uint8 ifc; /* Initial filter cutoff */
uint8 ifr; /* Initial filter resonance */
uint8 mch; /* MIDI channel */
uint8 mpr; /* MIDI program */
uint16 mbnk; /* MIDI bank */
uint8 keys[240];
};
struct it_envelope_node {
int8 y;
uint16 x;
};
struct it_envelope {
uint8 flg; /* Flags */
uint8 num; /* Number of node points */
uint8 lpb; /* Loop beginning */
uint8 lpe; /* Loop end */
uint8 slb; /* Sustain loop beginning */
uint8 sle; /* Sustain loop end */
struct it_envelope_node node[25];
uint8 unused;
};
struct it_sample_header {
uint32 magic; /* 'IMPS' */
uint8 dosname[12]; /* DOS filename */
uint8 zero; /* Always zero */
uint8 gvl; /* Global volume for instrument */
uint8 flags; /* Sample flags */
uint8 vol; /* Volume */
uint8 name[26]; /* ASCIIZ sample name */
uint8 convert; /* Sample flags */
uint8 dfp; /* Default pan */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
uint32 loopend; /* Loop end */
uint32 c5spd; /* C 5 speed */
uint32 sloopbeg; /* SusLoop begin */
uint32 sloopend; /* SusLoop end */
uint32 sample_ptr; /* Sample pointer */
uint8 vis; /* Vibrato speed */
uint8 vid; /* Vibrato depth */
uint8 vir; /* Vibrato rate */
uint8 vit; /* Vibrato waveform */
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,232 @@
#ifndef LIBXMP_CORE_DISABLE_IT
/* Public domain IT sample decompressor by Olivier Lapicque */
#include "loader.h"
static inline uint32 read_bits(HIO_HANDLE *ibuf, uint32 *bitbuf, int *bitnum, int n)
{
uint32 retval = 0;
int i = n;
int bnum = *bitnum, bbuf = *bitbuf;
if (n > 0) {
do {
if (bnum == 0) {
bbuf = hio_read8(ibuf);
bnum = 8;
}
retval >>= 1;
retval |= bbuf << 31;
bbuf >>= 1;
bnum--;
i--;
} while (i != 0);
i = n;
*bitnum = bnum;
*bitbuf = bbuf;
}
return (retval >> (32 - i));
}
int itsex_decompress8(HIO_HANDLE *src, uint8 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0, temp = 0, temp2 = 0;
uint32 d, pos;
while (len) {
if (!block_count) {
block_count = 0x8000;
/*size =*/ hio_read16l(src);
left = 9;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint16 bits = read_bits(src, &bitbuf, &bitnum, left);
if (hio_eof(src))
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits & 0xffff;
if (i != j)
goto unpack_byte;
bits = (read_bits(src, &bitbuf, &bitnum, 3)
+ 1) & 0xff;
if (hio_eof(src))
return -1;
left = ((uint8)bits < left) ? (uint8)bits :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 9) {
uint16 i = (0xff >> (9 - left)) + 4;
uint16 j = i - 8;
if ((bits <= j) || (bits > i))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 10)
goto skip_byte;
if (bits >= 256) {
left = (uint8) (bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 8) {
uint8 shift = 8 - left;
signed char c = (signed char)(bits << shift);
c >>= shift;
bits = (uint16) c;
}
bits += temp;
temp = (uint8)bits;
temp2 += temp;
dst[pos] = it215 ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
}
return 0;
}
int itsex_decompress16(HIO_HANDLE *src, int16 *dst, int len, int it215)
{
/* uint32 size = 0; */
uint32 block_count = 0;
uint32 bitbuf = 0;
int bitnum = 0;
uint8 left = 0;
int16 temp = 0, temp2 = 0;
uint32 d, pos;
while (len) {
if (!block_count) {
block_count = 0x4000;
/*size =*/ hio_read16l(src);
left = 17;
temp = temp2 = 0;
bitbuf = bitnum = 0;
}
d = block_count;
if (d > len)
d = len;
/* Unpacking */
pos = 0;
do {
uint32 bits = read_bits(src, &bitbuf, &bitnum, left);
if (hio_eof(src))
return -1;
if (left < 7) {
uint32 i = 1 << (left - 1);
uint32 j = bits;
if (i != j)
goto unpack_byte;
bits = read_bits(src, &bitbuf, &bitnum, 4) + 1;
if (hio_eof(src))
return -1;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left < 17) {
uint32 i = (0xffff >> (17 - left)) + 8;
uint32 j = (i - 16) & 0xffff;
if ((bits <= j) || (bits > (i & 0xffff)))
goto unpack_byte;
bits -= j;
left = ((uint8)(bits & 0xff) < left) ?
(uint8)(bits & 0xff) :
(uint8)((bits + 1) & 0xff);
goto next;
}
if (left >= 18)
goto skip_byte;
if (bits >= 0x10000) {
left = (uint8)(bits + 1) & 0xff;
goto next;
}
unpack_byte:
if (left < 16) {
uint8 shift = 16 - left;
int16 c = (int16)(bits << shift);
c >>= shift;
bits = (uint32) c;
}
bits += temp;
temp = (int16)bits;
temp2 += temp;
dst[pos] = (it215) ? temp2 : temp;
skip_byte:
pos++;
next:
/* if (slen <= 0)
return -1 */;
} while (pos < d);
/* Move On */
block_count -= d;
len -= d;
dst += d;
if (len <= 0)
break;
}
return 0;
}
#endif /* LIBXMP_CORE_DISABLE_IT */

View file

@ -0,0 +1,64 @@
#ifndef XMP_LOADER_H
#define XMP_LOADER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common.h"
#include "../effects.h"
#include "../format.h"
#include "../hio.h"
/* Sample flags */
#define SAMPLE_FLAG_DIFF 0x0001 /* Differential */
#define SAMPLE_FLAG_UNS 0x0002 /* Unsigned */
#define SAMPLE_FLAG_8BDIFF 0x0004
#define SAMPLE_FLAG_7BIT 0x0008
#define SAMPLE_FLAG_NOLOAD 0x0010 /* Get from buffer, don't load */
#define SAMPLE_FLAG_BIGEND 0x0040 /* Big-endian */
#define SAMPLE_FLAG_VIDC 0x0080 /* Archimedes VIDC logarithmic */
/*#define SAMPLE_FLAG_STEREO 0x0100 Interleaved stereo sample */
#define SAMPLE_FLAG_FULLREP 0x0200 /* Play full sample before looping */
#define SAMPLE_FLAG_ADLIB 0x1000 /* Adlib synth instrument */
#define SAMPLE_FLAG_HSC 0x2000 /* HSC Adlib synth instrument */
#define SAMPLE_FLAG_ADPCM 0x4000 /* ADPCM4 encoded samples */
#define DEFPAN(x) (0x80 + ((x) - 0x80) * m->defpan / 100)
int libxmp_init_instrument (struct module_data *);
int libxmp_alloc_subinstrument (struct xmp_module *, int, int);
int libxmp_init_pattern (struct xmp_module *);
int libxmp_alloc_pattern (struct xmp_module *, int);
int libxmp_alloc_track (struct xmp_module *, int, int);
int libxmp_alloc_tracks_in_pattern (struct xmp_module *, int);
int libxmp_alloc_pattern_tracks (struct xmp_module *, int, int);
char *libxmp_instrument_name (struct xmp_module *, int, uint8 *, int);
struct xmp_sample* libxmp_realloc_samples(struct xmp_sample *, int *, int);
char *libxmp_copy_adjust (char *, uint8 *, int);
int libxmp_test_name (uint8 *, int);
void libxmp_read_title (HIO_HANDLE *, char *, int);
void libxmp_set_xxh_defaults (struct xmp_module *);
void libxmp_decode_protracker_event (struct xmp_event *, uint8 *);
void libxmp_decode_noisetracker_event(struct xmp_event *, uint8 *);
void libxmp_disable_continue_fx (struct xmp_event *);
int libxmp_check_filename_case (char *, char *, char *, int);
void libxmp_get_instrument_path (struct module_data *, char *, int);
void libxmp_set_type (struct module_data *, char *, ...);
int libxmp_load_sample (struct module_data *, HIO_HANDLE *, int,
struct xmp_sample *, void *);
extern uint8 libxmp_ord_xlat[];
extern const int libxmp_arch_vol_table[];
#define MAGIC4(a,b,c,d) \
(((uint32)(a)<<24)|((uint32)(b)<<16)|((uint32)(c)<<8)|(d))
#define LOAD_INIT()
#define MODULE_INFO() do { \
D_(D_WARN "Module title: \"%s\"", m->mod.name); \
D_(D_WARN "Module type: %s", m->mod.type); \
} while (0)
#endif

View file

@ -0,0 +1,55 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
struct mod_instrument {
uint8 name[22]; /* Instrument name */
uint16 size; /* Sample length in 16-bit words */
int8 finetune; /* Finetune (signed nibble) */
int8 volume; /* Linear playback volume */
uint16 loop_start; /* Loop start in 16-bit words */
uint16 loop_size; /* Loop length in 16-bit words */
};
struct mod_header {
uint8 name[20];
struct mod_instrument ins[31];
uint8 len;
uint8 restart; /* Number of patterns in Soundtracker,
* Restart in Noisetracker/Startrekker,
* 0x7F in Protracker
*/
uint8 order[128];
uint8 magic[4];
};
#ifndef LIBXMP_CORE_PLAYER
/* Soundtracker 15-instrument module header */
struct st_header {
uint8 name[20];
struct mod_instrument ins[15];
uint8 len;
uint8 restart;
uint8 order[128];
};
#endif

View file

@ -0,0 +1,233 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* This loader recognizes the following variants of the Protracker
* module format:
*
* - Protracker M.K.
* - Fasttracker ?CHN and ??CH
*/
#include <ctype.h>
#include <limits.h>
#include "loader.h"
#include "mod.h"
static int mod_test (HIO_HANDLE *, char *, const int);
static int mod_load (struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_mod = {
"Protracker",
mod_test,
mod_load
};
static int mod_test(HIO_HANDLE *f, char *t, const int start)
{
int i;
char buf[4];
hio_seek(f, start + 1080, SEEK_SET);
if (hio_read(buf, 1, 4, f) < 4)
return -1;
if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0]) && isdigit((int)buf[1])) {
i = (buf[0] - '0') * 10 + buf[1] - '0';
if (i > 0 && i <= 32) {
goto found;
}
}
if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf)) {
if (*buf >= '0' && *buf <='9') {
goto found;
}
}
if (memcmp(buf, "M.K.", 4))
return -1;
found:
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 20);
return 0;
}
static int mod_load(struct module_data *m, HIO_HANDLE *f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xmp_event *event;
struct mod_header mh;
uint8 mod_event[4];
char magic[8];
int ptkloop = 0; /* Protracker loop */
LOAD_INIT();
mod->ins = 31;
mod->smp = mod->ins;
mod->chn = 0;
m->quirk |= QUIRK_PROTRACK;
m->period_type = PERIOD_MODRNG;
hio_read(&mh.name, 20, 1, f);
for (i = 0; i < 31; i++) {
hio_read(&mh.ins[i].name, 22, 1, f); /* Instrument name */
mh.ins[i].size = hio_read16b(f); /* Length in 16-bit words */
mh.ins[i].finetune = hio_read8(f); /* Finetune (signed nibble) */
mh.ins[i].volume = hio_read8(f); /* Linear playback volume */
mh.ins[i].loop_start = hio_read16b(f); /* Loop start in 16-bit words */
mh.ins[i].loop_size = hio_read16b(f); /* Loop size in 16-bit words */
}
mh.len = hio_read8(f);
mh.restart = hio_read8(f);
hio_read(&mh.order, 128, 1, f);
memset(magic, 0, 8);
hio_read(magic, 4, 1, f);
if (!memcmp(magic, "M.K.", 4)) {
mod->chn = 4;
} else if (!strncmp(magic + 2, "CH", 2) &&
isdigit((int)magic[0]) && isdigit((int)magic[1])) {
mod->chn = (*magic - '0') * 10 + magic[1] - '0';
} else if (!strncmp(magic + 1, "CHN", 3) && isdigit((int)*magic)) {
mod->chn = *magic - '0';
} else {
return -1;
}
strncpy(mod->name, (char *)mh.name, 20);
mod->len = mh.len;
/* mod->rst = mh.restart; */
if (mod->rst >= mod->len)
mod->rst = 0;
memcpy(mod->xxo, mh.order, 128);
for (i = 0; i < 128; i++) {
/* This fixes dragnet.mod (garbage in the order list) */
if (mod->xxo[i] > 0x7f)
break;
if (mod->xxo[i] > mod->pat)
mod->pat = mod->xxo[i];
}
mod->pat++;
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi;
struct xmp_subinstrument *sub;
struct xmp_sample *xxs;
if (libxmp_alloc_subinstrument(mod, i, 1) < 0)
return -1;
xxi = &mod->xxi[i];
sub = &xxi->sub[0];
xxs = &mod->xxs[i];
xxs->len = 2 * mh.ins[i].size;
xxs->lps = 2 * mh.ins[i].loop_start;
xxs->lpe = xxs->lps + 2 * mh.ins[i].loop_size;
if (xxs->lpe > xxs->len) {
xxs->lpe = xxs->len;
}
xxs->flg = (mh.ins[i].loop_size > 1 && xxs->lpe >= 4) ?
XMP_SAMPLE_LOOP : 0;
sub->fin = (int8)(mh.ins[i].finetune << 4);
sub->vol = mh.ins[i].volume;
sub->pan = 0x80;
sub->sid = i;
libxmp_instrument_name(mod, i, mh.ins[i].name, 22);
if (xxs->len > 0) {
xxi->nsm = 1;
}
}
mod->trk = mod->chn * mod->pat;
libxmp_set_type(m, mod->chn == 4 ? "Protracker" : "Fasttracker");
MODULE_INFO();
for (i = 0; i < mod->ins; i++) {
D_(D_INFO "[%2X] %-22.22s %04x %04x %04x %c V%02x %+d %c\n",
i, mod->xxi[i].name,
mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe,
(mh.ins[i].loop_size > 1 && mod->xxs[i].lpe > 8) ?
'L' : ' ', mod->xxi[i].sub[0].vol,
mod->xxi[i].sub[0].fin >> 4,
ptkloop && mod->xxs[i].lps == 0 && mh.ins[i].loop_size > 1 &&
mod->xxs[i].len > mod->xxs[i].lpe ? '!' : ' ');
}
if (libxmp_init_pattern(mod) < 0)
return -1;
/* Load and convert patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
return -1;
for (j = 0; j < (64 * mod->chn); j++) {
event = &EVENT (i, j % mod->chn, j / mod->chn);
hio_read (mod_event, 1, 4, f);
libxmp_decode_protracker_event(event, mod_event);
}
}
/* Load samples */
D_(D_INFO "Stored samples: %d", mod->smp);
for (i = 0; i < mod->smp; i++) {
int flags;
if (!mod->xxs[i].len)
continue;
flags = ptkloop ? SAMPLE_FLAG_FULLREP : 0;
if (libxmp_load_sample(m, f, flags, &mod->xxs[i], NULL) < 0)
return -1;
}
if (mod->chn > 4) {
m->quirk &= ~QUIRK_PROTRACK;
m->quirk |= QUIRKS_FT2 | QUIRK_FTMOD;
m->read_event_type = READ_EVENT_FT2;
m->period_type = PERIOD_AMIGA;
}
return 0;
}

View file

@ -0,0 +1,116 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* S3M packed pattern macros */
#define S3M_EOR 0 /* End of row */
#define S3M_CH_MASK 0x1f /* Channel */
#define S3M_NI_FOLLOW 0x20 /* Note and instrument follow */
#define S3M_VOL_FOLLOWS 0x40 /* Volume follows */
#define S3M_FX_FOLLOWS 0x80 /* Effect and parameter follow */
/* S3M channel info macros */
#define S3M_CH_ON 0x80 /* Psi says it's bit 8, I'll assume bit 7 */
#define S3M_CH_OFF 0xff
#define S3M_CH_PAN 0x7f /* Left/Right */
/* S3M channel pan macros */
#define S3M_PAN_SET 0x20
#define S3M_PAN_MASK 0x0f
/* S3M flags */
#define S3M_ST2_VIB 0x01 /* Not recognized */
#define S3M_ST2_TEMPO 0x02 /* Not recognized */
#define S3M_AMIGA_SLIDE 0x04 /* Not recognized */
#define S3M_VOL_OPT 0x08 /* Not recognized */
#define S3M_AMIGA_RANGE 0x10
#define S3M_SB_FILTER 0x20 /* Not recognized */
#define S3M_ST300_VOLS 0x40
#define S3M_CUSTOM_DATA 0x80 /* Not recognized */
/* S3M Adlib instrument types */
#define S3M_INST_SAMPLE 0x01
#define S3M_INST_AMEL 0x02
#define S3M_INST_ABD 0x03
#define S3M_INST_ASNARE 0x04
#define S3M_INST_ATOM 0x05
#define S3M_INST_ACYM 0x06
#define S3M_INST_AHIHAT 0x07
struct s3m_file_header {
uint8 name[28]; /* Song name */
uint8 doseof; /* 0x1a */
uint8 type; /* File type */
uint8 rsvd1[2]; /* Reserved */
uint16 ordnum; /* Number of orders (must be even) */
uint16 insnum; /* Number of instruments */
uint16 patnum; /* Number of patterns */
uint16 flags; /* Flags */
uint16 version; /* Tracker ID and version */
uint16 ffi; /* File format information */
uint32 magic; /* 'SCRM' */
uint8 gv; /* Global volume */
uint8 is; /* Initial speed */
uint8 it; /* Initial tempo */
uint8 mv; /* Master volume */
uint8 uc; /* Ultra click removal */
uint8 dp; /* Default pan positions if 0xfc */
uint8 rsvd2[8]; /* Reserved */
uint16 special; /* Ptr to special custom data */
uint8 chset[32]; /* Channel settings */
};
struct s3m_instrument_header {
uint8 dosname[13]; /* DOS file name */
uint16 memseg; /* Pointer to sample data */
uint32 length; /* Length */
uint32 loopbeg; /* Loop begin */
uint32 loopend; /* Loop end */
uint8 vol; /* Volume */
uint8 rsvd1; /* Reserved */
uint8 pack; /* Packing type (not used) */
uint8 flags; /* Loop/stereo/16bit samples flags */
uint16 c2spd; /* C 4 speed */
uint16 rsvd2; /* Reserved */
uint8 rsvd3[4]; /* Reserved */
uint16 int_gp; /* Internal - GUS pointer */
uint16 int_512; /* Internal - SB pointer */
uint32 int_last; /* Internal - SB index */
uint8 name[28]; /* Instrument name */
uint32 magic; /* 'SCRS' */
};
#ifndef LIBXMP_CORE_PLAYER
struct s3m_adlib_header {
uint8 dosname[12]; /* DOS file name */
uint8 rsvd1[3]; /* 0x00 0x00 0x00 */
uint8 reg[12]; /* Adlib registers */
uint8 vol;
uint8 dsk;
uint8 rsvd2[2];
uint16 c2spd; /* C 4 speed */
uint16 rsvd3; /* Reserved */
uint8 rsvd4[12]; /* Reserved */
uint8 name[28]; /* Instrument name */
uint32 magic; /* 'SCRI' */
};
#endif

View file

@ -0,0 +1,659 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Tue, 30 Jun 1998 20:23:11 +0200
* Reported by John v/d Kamp <blade_@dds.nl>:
* I have this song from Purple Motion called wcharts.s3m, the global
* volume was set to 0, creating a devide by 0 error in xmp. There should
* be an extra test if it's 0 or not.
*
* Claudio's fix: global volume ignored
*/
/*
* Sat, 29 Aug 1998 18:50:43 -0500 (CDT)
* Reported by Joel Jordan <scriber@usa.net>:
* S3M files support tempos outside the ranges defined by xmp (that is,
* the MOD/XM tempo ranges). S3M's can have tempos from 0 to 255 and speeds
* from 0 to 255 as well, since these are handled with separate effects
* unlike the MOD format. This becomes an issue in such songs as Skaven's
* "Catch that Goblin", which uses speeds above 0x1f.
*
* Claudio's fix: FX_S3M_SPEED added. S3M supports speeds from 0 to 255 and
* tempos from 32 to 255 (S3M speed == xmp tempo, S3M tempo == xmp BPM).
*/
/* Wed, 21 Oct 1998 15:03:44 -0500 Geoff Reedy <vader21@imsa.edu>
* It appears that xmp has issues loading/playing a specific instrument
* used in LUCCA.S3M.
* (Fixed by Hipolito in xmp-2.0.0dev34)
*/
/*
* From http://code.pui.ch/2007/02/18/turn-demoscene-modules-into-mp3s/
* The only flaw I noticed [in xmp] is a problem with portamento in Purple
* Motion's second reality soundtrack (1:06-1:17)
*
* Claudio's note: that's a dissonant beating between channels 6 and 7
* starting at pos12, caused by pitchbending effect F25.
*/
/*
* From: Ralf Hoffmann <ralf@boomerangsworld.de>
* Date: Wed, 26 Sep 2007 17:12:41 +0200
* ftp://ftp.scenesp.org/pub/compilations/modplanet/normal/bonuscd/artists/
* Iq/return%20of%20litmus.s3m doesn't start playing, just uses 100% cpu,
* the number of patterns is unusually high
*
* Claudio's fix: this module seems to be a bad conversion, bad rip or
* simply corrupted since it has many instances of 0x87 instead of 0x00
* in the module and instrument headers. I'm adding a simple workaround
* to be able to load/play the module as is, see the fix87() macro below.
*/
#include "loader.h"
#include "s3m.h"
#include "../period.h"
#define MAGIC_SCRM MAGIC4('S','C','R','M')
#define MAGIC_SCRI MAGIC4('S','C','R','I')
#define MAGIC_SCRS MAGIC4('S','C','R','S')
static int s3m_test(HIO_HANDLE *, char *, const int);
static int s3m_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_s3m = {
"Scream Tracker 3",
s3m_test,
s3m_load
};
static int s3m_test(HIO_HANDLE *f, char *t, const int start)
{
hio_seek(f, start + 44, SEEK_SET);
if (hio_read32b(f) != MAGIC_SCRM)
return -1;
hio_seek(f, start + 29, SEEK_SET);
if (hio_read8(f) != 0x10)
return -1;
hio_seek(f, start + 0, SEEK_SET);
libxmp_read_title(f, t, 28);
return 0;
}
#define NONE 0xff
#define FX_S3M_EXTENDED 0xfe
#define fix87(x) do { \
int i; for (i = 0; i < sizeof(x); i++) { \
if (*((uint8 *)&x + i) == 0x87) *((uint8 *)&x + i) = 0; } \
} while (0)
/* Effect conversion table */
static const uint8 fx[] = {
NONE,
FX_S3M_SPEED, /* Axx Set speed to xx (the default is 06) */
FX_JUMP, /* Bxx Jump to order xx (hexadecimal) */
FX_BREAK, /* Cxx Break pattern to row xx (decimal) */
FX_VOLSLIDE, /* Dxy Volume slide down by y/up by x */
FX_PORTA_DN, /* Exx Slide down by xx */
FX_PORTA_UP, /* Fxx Slide up by xx */
FX_TONEPORTA, /* Gxx Tone portamento with speed xx */
FX_VIBRATO, /* Hxy Vibrato with speed x and depth y */
FX_TREMOR, /* Ixy Tremor with ontime x and offtime y */
FX_S3M_ARPEGGIO, /* Jxy Arpeggio with halfnote additions */
FX_VIBRA_VSLIDE, /* Kxy Dual command: H00 and Dxy */
FX_TONE_VSLIDE, /* Lxy Dual command: G00 and Dxy */
NONE,
NONE,
FX_OFFSET, /* Oxy Set sample offset */
NONE,
FX_MULTI_RETRIG, /* Qxy Retrig (+volumeslide) note */
FX_TREMOLO, /* Rxy Tremolo with speed x and depth y */
FX_S3M_EXTENDED, /* Sxx (misc effects) */
FX_S3M_BPM, /* Txx Tempo = xx (hex) */
FX_FINE_VIBRATO, /* Uxx Fine vibrato */
FX_GLOBALVOL, /* Vxx Set global volume */
NONE,
FX_SETPAN, /* Xxx Set pan */
NONE,
NONE
};
/* Effect translation */
static void xlat_fx(int c, struct xmp_event *e)
{
uint8 h = MSN(e->fxp), l = LSN(e->fxp);
if (e->fxt > 26) {
D_(D_WARN "invalid effect %02x", e->fxt);
e->fxt = e->fxp = 0;
return;
}
switch (e->fxt = fx[e->fxt]) {
case FX_S3M_BPM:
if (e->fxp < 0x20) {
e->fxp = e->fxt = 0;
}
break;
case FX_S3M_EXTENDED: /* Extended effects */
e->fxt = FX_EXTENDED;
switch (h) {
case 0x1: /* Glissando */
e->fxp = LSN(e->fxp) | (EX_GLISS << 4);
break;
case 0x2: /* Finetune */
e->fxp =
((LSN(e->fxp) - 8) & 0x0f) | (EX_FINETUNE << 4);
break;
case 0x3: /* Vibrato wave */
e->fxp = LSN(e->fxp) | (EX_VIBRATO_WF << 4);
break;
case 0x4: /* Tremolo wave */
e->fxp = LSN(e->fxp) | (EX_TREMOLO_WF << 4);
break;
case 0x5:
case 0x6:
case 0x7:
case 0x9:
case 0xa: /* Ignore */
e->fxt = e->fxp = 0;
break;
case 0x8: /* Set pan */
e->fxt = FX_SETPAN;
e->fxp = l << 4;
break;
case 0xb: /* Pattern loop */
e->fxp = LSN(e->fxp) | (EX_PATTERN_LOOP << 4);
break;
case 0xc:
if (!l)
e->fxt = e->fxp = 0;
}
break;
case FX_SETPAN:
/* Saga Musix says: "The X effect in S3M files is not
* exclusive to IT and clones. You will find tons of S3Ms made
* with ST3 itself using this effect (and relying on an
* external player being used). X in S3M also behaves
* differently than in IT, which your code does not seem to
* handle: X00 - X80 is left... right, XA4 is surround (like
* S91 in IT), other values are not supposed to do anything.
*/
if (e->fxp == 0xa4) {
// surround
e->fxt = FX_SURROUND;
e->fxp = 1;
} else {
int pan = ((int)e->fxp) << 1;
if (pan > 0xff) {
pan = 0xff;
}
e->fxp = pan;
}
break;
case NONE: /* No effect */
e->fxt = e->fxp = 0;
break;
}
}
static int s3m_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int c, r, i;
struct xmp_event *event = 0, dummy;
struct s3m_file_header sfh;
struct s3m_instrument_header sih;
#ifndef LIBXMP_CORE_PLAYER
struct s3m_adlib_header sah;
char tracker_name[40];
int quirk87 = 0;
#endif
int pat_len;
uint8 n, b, x8;
uint16 *pp_ins; /* Parapointers to instruments */
uint16 *pp_pat; /* Parapointers to patterns */
int ret;
LOAD_INIT();
hio_read(&sfh.name, 28, 1, f); /* Song name */
hio_read8(f); /* 0x1a */
sfh.type = hio_read8(f); /* File type */
hio_read16l(f); /* Reserved */
sfh.ordnum = hio_read16l(f); /* Number of orders (must be even) */
sfh.insnum = hio_read16l(f); /* Number of instruments */
sfh.patnum = hio_read16l(f); /* Number of patterns */
sfh.flags = hio_read16l(f); /* Flags */
sfh.version = hio_read16l(f); /* Tracker ID and version */
sfh.ffi = hio_read16l(f); /* File format information */
/* Sanity check */
if (hio_error(f)) {
goto err;
}
if (sfh.ffi != 1 && sfh.ffi != 2) {
goto err;
}
if (sfh.ordnum > 255 || sfh.insnum > 255 || sfh.patnum > 255) {
goto err;
}
sfh.magic = hio_read32b(f); /* 'SCRM' */
sfh.gv = hio_read8(f); /* Global volume */
sfh.is = hio_read8(f); /* Initial speed */
sfh.it = hio_read8(f); /* Initial tempo */
sfh.mv = hio_read8(f); /* Master volume */
sfh.uc = hio_read8(f); /* Ultra click removal */
sfh.dp = hio_read8(f); /* Default pan positions if 0xfc */
hio_read32l(f); /* Reserved */
hio_read32l(f); /* Reserved */
sfh.special = hio_read16l(f); /* Ptr to special custom data */
hio_read(sfh.chset, 32, 1, f); /* Channel settings */
if (hio_error(f)) {
goto err;
}
#if 0
if (sfh.magic != MAGIC_SCRM)
return -1;
#endif
#ifndef LIBXMP_CORE_PLAYER
/* S3M anomaly in return_of_litmus.s3m */
if (sfh.version == 0x1301 && sfh.name[27] == 0x87)
quirk87 = 1;
if (quirk87) {
fix87(sfh.name);
fix87(sfh.patnum);
fix87(sfh.flags);
}
#endif
libxmp_copy_adjust(mod->name, sfh.name, 28);
pp_ins = calloc(2, sfh.insnum);
if (pp_ins == NULL)
goto err;
pp_pat = calloc(2, sfh.patnum);
if (pp_pat == NULL)
goto err2;
if (sfh.flags & S3M_AMIGA_RANGE)
m->period_type = PERIOD_MODRNG;
if (sfh.flags & S3M_ST300_VOLS)
m->quirk |= QUIRK_VSALL;
/* m->volbase = 4096 / sfh.gv; */
mod->spd = sfh.is;
mod->bpm = sfh.it;
mod->chn = 0;
for (i = 0; i < 32; i++) {
if (sfh.chset[i] == S3M_CH_OFF)
continue;
mod->chn = i + 1;
if (sfh.mv & 0x80) { /* stereo */
int x = sfh.chset[i] & S3M_CH_PAN;
mod->xxc[i].pan = (x & 0x0f) < 8 ? 0x30 : 0xc0;
} else {
mod->xxc[i].pan = 0x80;
}
}
if (sfh.ordnum <= XMP_MAX_MOD_LENGTH) {
mod->len = sfh.ordnum;
hio_read(mod->xxo, 1, mod->len, f);
} else {
mod->len = XMP_MAX_MOD_LENGTH;
hio_read(mod->xxo, 1, mod->len, f);
hio_seek(f, sfh.ordnum - XMP_MAX_MOD_LENGTH, SEEK_CUR);
}
if (hio_error(f)) {
goto err3;
}
/* Don't trust sfh.patnum */
mod->pat = -1;
for (i = 0; i < mod->len; ++i) {
if (mod->xxo[i] < 0xfe && mod->xxo[i] > mod->pat) {
mod->pat = mod->xxo[i];
}
}
mod->pat++;
if (mod->pat > sfh.patnum)
mod->pat = sfh.patnum;
if (mod->pat == 0)
goto err3;
mod->trk = mod->pat * mod->chn;
/* Load and convert header */
mod->ins = sfh.insnum;
mod->smp = mod->ins;
for (i = 0; i < sfh.insnum; i++)
pp_ins[i] = hio_read16l(f);
for (i = 0; i < sfh.patnum; i++)
pp_pat[i] = hio_read16l(f);
/* Default pan positions */
for (i = 0, sfh.dp -= 0xfc; !sfh.dp /* && n */ && (i < 32); i++) {
uint8 x = hio_read8(f);
if (x & S3M_PAN_SET) {
mod->xxc[i].pan = (x << 4) & 0xff;
} else {
mod->xxc[i].pan =
sfh.mv % 0x80 ? 0x30 + 0xa0 * (i & 1) : 0x80;
}
}
m->c4rate = C4_NTSC_RATE;
if (sfh.version == 0x1300) {
m->quirk |= QUIRK_VSALL;
}
#ifndef LIBXMP_CORE_PLAYER
switch (sfh.version >> 12) {
case 1:
snprintf(tracker_name, 40, "Scream Tracker %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
m->quirk |= QUIRK_ST3BUGS;
break;
case 2:
snprintf(tracker_name, 40, "Imago Orpheus %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
break;
case 3:
if (sfh.version == 0x3216) {
strcpy(tracker_name, "Impulse Tracker 2.14v3");
} else if (sfh.version == 0x3217) {
strcpy(tracker_name, "Impulse Tracker 2.14v5");
} else {
snprintf(tracker_name, 40, "Impulse Tracker %d.%02x",
(sfh.version & 0x0f00) >> 8,
sfh.version & 0xff);
}
break;
case 5:
snprintf(tracker_name, 40, "OpenMPT %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
m->quirk |= QUIRK_ST3BUGS;
break;
case 4:
if (sfh.version != 0x4100) {
snprintf(tracker_name, 40, "Schism Tracker %d.%02x",
(sfh.version & 0x0f00) >> 8,
sfh.version & 0xff);
break;
}
/* fall through */
case 6:
snprintf(tracker_name, 40, "BeRoTracker %d.%02x",
(sfh.version & 0x0f00) >> 8, sfh.version & 0xff);
break;
default:
snprintf(tracker_name, 40, "unknown (%04x)", sfh.version);
}
libxmp_set_type(m, "%s S3M", tracker_name);
#else
libxmp_set_type(m, "Scream Tracker 3");
m->quirk |= QUIRK_ST3BUGS;
#endif
MODULE_INFO();
if (libxmp_init_pattern(mod) < 0)
goto err3;
/* Read patterns */
D_(D_INFO "Stored patterns: %d", mod->pat);
for (i = 0; i < mod->pat; i++) {
if (libxmp_alloc_pattern_tracks(mod, i, 64) < 0)
goto err3;
if (pp_pat[i] == 0)
continue;
hio_seek(f, start + pp_pat[i] * 16, SEEK_SET);
r = 0;
pat_len = hio_read16l(f) - 2;
while (pat_len >= 0 && r < mod->xxp[i]->rows) {
b = hio_read8(f);
if (hio_error(f)) {
goto err3;
}
if (b == S3M_EOR) {
r++;
continue;
}
c = b & S3M_CH_MASK;
event = c >= mod->chn ? &dummy : &EVENT(i, c, r);
if (b & S3M_NI_FOLLOW) {
switch (n = hio_read8(f)) {
case 255:
n = 0;
break; /* Empty note */
case 254:
n = XMP_KEY_OFF;
break; /* Key off */
default:
n = 13 + 12 * MSN(n) + LSN(n);
}
event->note = n;
event->ins = hio_read8(f);
pat_len -= 2;
}
if (b & S3M_VOL_FOLLOWS) {
event->vol = hio_read8(f) + 1;
pat_len--;
}
if (b & S3M_FX_FOLLOWS) {
event->fxt = hio_read8(f);
event->fxp = hio_read8(f);
xlat_fx(c, event);
pat_len -= 2;
}
}
}
D_(D_INFO "Stereo enabled: %s", sfh.mv & 0x80 ? "yes" : "no");
D_(D_INFO "Pan settings: %s", sfh.dp ? "no" : "yes");
if (libxmp_init_instrument(m) < 0)
goto err3;
/* Read and convert instruments and samples */
D_(D_INFO "Instruments: %d", mod->ins);
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
struct xmp_sample *xxs = &mod->xxs[i];
struct xmp_subinstrument *sub;
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
if (xxi->sub == NULL) {
goto err3;
}
sub = &xxi->sub[0];
hio_seek(f, start + pp_ins[i] * 16, SEEK_SET);
x8 = hio_read8(f);
sub->pan = 0x80;
sub->sid = i;
if (x8 >= 2) {
#ifndef LIBXMP_CORE_PLAYER
/* OPL2 FM instrument */
hio_read(&sah.dosname, 12, 1, f); /* DOS file name */
hio_read(&sah.rsvd1, 3, 1, f); /* 0x00 0x00 0x00 */
hio_read(&sah.reg, 12, 1, f); /* Adlib registers */
sah.vol = hio_read8(f);
sah.dsk = hio_read8(f);
hio_read16l(f);
sah.c2spd = hio_read16l(f); /* C 4 speed */
hio_read16l(f);
hio_read(&sah.rsvd4, 12, 1, f); /* Reserved */
hio_read(&sah.name, 28, 1, f); /* Instrument name */
sah.magic = hio_read32b(f); /* 'SCRI' */
if (sah.magic != MAGIC_SCRI) {
D_(D_CRIT "error: FM instrument magic");
goto err3;
}
sah.magic = 0;
libxmp_instrument_name(mod, i, sah.name, 28);
xxi->nsm = 1;
sub->vol = sah.vol;
libxmp_c2spd_to_note(sah.c2spd, &sub->xpo, &sub->fin);
sub->xpo += 12;
ret =
libxmp_load_sample(m, f, SAMPLE_FLAG_ADLIB, xxs,
(char *)&sah.reg);
if (ret < 0)
goto err3;
D_(D_INFO "[%2X] %-28.28s", i, xxi->name);
continue;
#else
goto err3;
#endif
}
hio_read(&sih.dosname, 13, 1, f); /* DOS file name */
sih.memseg = hio_read16l(f); /* Pointer to sample data */
sih.length = hio_read32l(f); /* Length */
#if 0
/* ST3 limit */
if ((sfh.version >> 12) == 1 && sih.length > 64000)
sih.length = 64000;
#endif
if (sih.length > MAX_SAMPLE_SIZE) {
goto err3;
}
sih.loopbeg = hio_read32l(f); /* Loop begin */
sih.loopend = hio_read32l(f); /* Loop end */
sih.vol = hio_read8(f); /* Volume */
sih.rsvd1 = hio_read8(f); /* Reserved */
sih.pack = hio_read8(f); /* Packing type (not used) */
sih.flags = hio_read8(f); /* Loop/stereo/16bit flags */
sih.c2spd = hio_read16l(f); /* C 4 speed */
sih.rsvd2 = hio_read16l(f); /* Reserved */
hio_read(&sih.rsvd3, 4, 1, f); /* Reserved */
sih.int_gp = hio_read16l(f); /* Internal - GUS pointer */
sih.int_512 = hio_read16l(f); /* Internal - SB pointer */
sih.int_last = hio_read32l(f); /* Internal - SB index */
hio_read(&sih.name, 28, 1, f); /* Instrument name */
sih.magic = hio_read32b(f); /* 'SCRS' */
if (x8 == 1 && sih.magic != MAGIC_SCRS) {
D_(D_CRIT "error: instrument magic");
goto err3;
}
#ifndef LIBXMP_CORE_PLAYER
if (quirk87) {
fix87(sih.length);
fix87(sih.loopbeg);
fix87(sih.loopend);
fix87(sih.flags);
}
#endif
xxs->len = sih.length;
xxi->nsm = sih.length > 0 ? 1 : 0;
xxs->lps = sih.loopbeg;
xxs->lpe = sih.loopend;
xxs->flg = sih.flags & 1 ? XMP_SAMPLE_LOOP : 0;
if (sih.flags & 4) {
xxs->flg |= XMP_SAMPLE_16BIT;
}
sub->vol = sih.vol;
sih.magic = 0;
libxmp_instrument_name(mod, i, sih.name, 28);
D_(D_INFO "[%2X] %-28.28s %04x%c%04x %04x %c V%02x %5d",
i, mod->xxi[i].name, mod->xxs[i].len,
xxs->flg & XMP_SAMPLE_16BIT ? '+' : ' ',
xxs->lps, mod->xxs[i].lpe,
xxs->flg & XMP_SAMPLE_LOOP ? 'L' : ' ', sub->vol, sih.c2spd);
libxmp_c2spd_to_note(sih.c2spd, &sub->xpo, &sub->fin);
hio_seek(f, start + 16L * sih.memseg, SEEK_SET);
ret = libxmp_load_sample(m, f, sfh.ffi == 1 ? 0 : SAMPLE_FLAG_UNS,
xxs, NULL);
if (ret < 0) {
goto err3;
}
}
free(pp_pat);
free(pp_ins);
m->quirk |= QUIRKS_ST3 | QUIRK_ARPMEM;
m->read_event_type = READ_EVENT_ST3;
return 0;
err3:
free(pp_pat);
err2:
free(pp_ins);
err:
return -1;
}

View file

@ -0,0 +1,416 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "../common.h"
#include "loader.h"
#ifndef LIBXMP_CORE_PLAYER
/*
* From the Audio File Formats (version 2.5)
* Submitted-by: Guido van Rossum <guido@cwi.nl>
* Last-modified: 27-Aug-1992
*
* The Acorn Archimedes uses a variation on U-LAW with the bit order
* reversed and the sign bit in bit 0. Being a 'minority' architecture,
* Arc owners are quite adept at converting sound/image formats from
* other machines, and it is unlikely that you'll ever encounter sound in
* one of the Arc's own formats (there are several).
*/
static const int8 vdic_table[128] = {
/* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 8 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* 24 */ 1, 1, 1, 1, 1, 1, 1, 1,
/* 32 */ 1, 1, 1, 1, 2, 2, 2, 2,
/* 40 */ 2, 2, 2, 2, 3, 3, 3, 3,
/* 48 */ 3, 3, 4, 4, 4, 4, 5, 5,
/* 56 */ 5, 5, 6, 6, 6, 6, 7, 7,
/* 64 */ 7, 8, 8, 9, 9, 10, 10, 11,
/* 72 */ 11, 12, 12, 13, 13, 14, 14, 15,
/* 80 */ 15, 16, 17, 18, 19, 20, 21, 22,
/* 88 */ 23, 24, 25, 26, 27, 28, 29, 30,
/* 96 */ 31, 33, 34, 36, 38, 40, 42, 44,
/* 104 */ 46, 48, 50, 52, 54, 56, 58, 60,
/* 112 */ 62, 65, 68, 72, 77, 80, 84, 91,
/* 120 */ 95, 98, 103, 109, 114, 120, 126, 127
};
/* Convert 7 bit samples to 8 bit */
static void convert_7bit_to_8bit(uint8 *p, int l)
{
for (; l--; p++) {
*p <<= 1;
}
}
/* Convert Archimedes VIDC samples to linear */
static void convert_vidc_to_linear(uint8 *p, int l)
{
int i;
uint8 x;
for (i = 0; i < l; i++) {
x = p[i];
p[i] = vdic_table[x >> 1];
if (x & 0x01)
p[i] *= -1;
}
}
static void adpcm4_decoder(uint8 *inp, uint8 *outp, char *tab, int len)
{
char delta = 0;
uint8 b0, b1;
int i;
len = (len + 1) / 2;
for (i = 0; i < len; i++) {
b0 = *inp;
b1 = *inp++ >> 4;
delta += tab[b0 & 0x0f];
*outp++ = delta;
delta += tab[b1 & 0x0f];
*outp++ = delta;
}
}
#endif
/* Convert differential to absolute sample data */
static void convert_delta(uint8 *p, int l, int r)
{
uint16 *w = (uint16 *)p;
uint16 abs = 0;
if (r) {
for (; l--;) {
abs = *w + abs;
*w++ = abs;
}
} else {
for (; l--;) {
abs = *p + abs;
*p++ = (uint8) abs;
}
}
}
/* Convert signed to unsigned sample data */
static void convert_signal(uint8 *p, int l, int r)
{
uint16 *w = (uint16 *)p;
if (r) {
for (; l--; w++)
*w += 0x8000;
} else {
for (; l--; p++)
*p += (char)0x80; /* cast needed by MSVC++ */
}
}
/* Convert little-endian 16 bit samples to big-endian */
static void convert_endian(uint8 *p, int l)
{
uint8 b;
int i;
for (i = 0; i < l; i++) {
b = p[0];
p[0] = p[1];
p[1] = b;
p += 2;
}
}
#if 0
/* Downmix stereo samples to mono */
static void convert_stereo_to_mono(uint8 *p, int l, int r)
{
int16 *b = (int16 *)p;
int i;
if (r) {
l /= 2;
for (i = 0; i < l; i++)
b[i] = (b[i * 2] + b[i * 2 + 1]) / 2;
} else {
for (i = 0; i < l; i++)
p[i] = (p[i * 2] + p[i * 2 + 1]) / 2;
}
}
#endif
static void unroll_loop(struct xmp_sample *xxs)
{
int8 *s8;
int16 *s16;
int start, loop_size;
int i;
s16 = (int16 *)xxs->data;
s8 = (int8 *)xxs->data;
if (xxs->len > xxs->lpe) {
start = xxs->lpe;
} else {
start = xxs->len;
}
loop_size = xxs->lpe - xxs->lps;
if (xxs->flg & XMP_SAMPLE_16BIT) {
s16 += start;
for (i = 0; i < loop_size; i++) {
*(s16 + i) = *(s16 - i - 1);
}
} else {
s8 += start;
for (i = 0; i < loop_size; i++) {
*(s8 + i) = *(s8 - i - 1);
}
}
}
int libxmp_load_sample(struct module_data *m, HIO_HANDLE *f, int flags, struct xmp_sample *xxs, void *buffer)
{
int bytelen, extralen, unroll_extralen, i;
#ifndef LIBXMP_CORE_PLAYER
/* Adlib FM patches */
if (flags & SAMPLE_FLAG_ADLIB) {
return 0;
}
#endif
/* Empty or invalid samples
*/
if (xxs->len <= 0) {
return 0;
}
/* Skip sample loading
* FIXME: fails for ADPCM samples
*
* + Sanity check: skip huge samples (likely corrupt module)
*/
if (xxs->len > MAX_SAMPLE_SIZE || (m && m->smpctl & XMP_SMPCTL_SKIP)) {
if (~flags & SAMPLE_FLAG_NOLOAD) {
/* coverity[check_return] */
hio_seek(f, xxs->len, SEEK_CUR);
}
return 0;
}
/* Loop parameters sanity check
*/
if (xxs->lps < 0) {
xxs->lps = 0;
}
if (xxs->lpe > xxs->len) {
xxs->lpe = xxs->len;
}
if (xxs->lps >= xxs->len || xxs->lps >= xxs->lpe) {
xxs->lps = xxs->lpe = 0;
xxs->flg &= ~(XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR);
}
/* Patches with samples
* Allocate extra sample for interpolation.
*/
bytelen = xxs->len;
extralen = 4;
unroll_extralen = 0;
/* Disable birectional loop flag if sample is not looped
*/
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
if (~xxs->flg & XMP_SAMPLE_LOOP)
xxs->flg &= ~XMP_SAMPLE_LOOP_BIDIR;
}
/* Unroll bidirectional loops
*/
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_extralen = (xxs->lpe - xxs->lps) -
(xxs->len - xxs->lpe);
if (unroll_extralen < 0) {
unroll_extralen = 0;
}
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
bytelen *= 2;
extralen *= 2;
unroll_extralen *= 2;
}
/* add guard bytes before the buffer for higher order interpolation */
xxs->data = malloc(bytelen + extralen + unroll_extralen + 4);
if (xxs->data == NULL) {
goto err;
}
*(uint32 *)xxs->data = 0;
xxs->data += 4;
if (flags & SAMPLE_FLAG_NOLOAD) {
memcpy(xxs->data, buffer, bytelen);
} else
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_ADPCM) {
int x2 = (bytelen + 1) >> 1;
char table[16];
if (hio_read(table, 1, 16, f) != 16) {
goto err2;
}
if (hio_read(xxs->data + x2, 1, x2, f) != x2) {
goto err2;
}
adpcm4_decoder((uint8 *)xxs->data + x2,
(uint8 *)xxs->data, table, bytelen);
} else
#endif
{
int x = hio_read(xxs->data, 1, bytelen, f);
if (x != bytelen) {
D_(D_WARN "short read (%d) in sample load", x - bytelen);
memset(xxs->data + x, 0, bytelen - x);
}
}
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_7BIT) {
convert_7bit_to_8bit(xxs->data, xxs->len);
}
#endif
/* Fix endianism if needed */
if (xxs->flg & XMP_SAMPLE_16BIT) {
#ifdef WORDS_BIGENDIAN
if (~flags & SAMPLE_FLAG_BIGEND)
convert_endian(xxs->data, xxs->len);
#else
if (flags & SAMPLE_FLAG_BIGEND)
convert_endian(xxs->data, xxs->len);
#endif
}
/* Convert delta samples */
if (flags & SAMPLE_FLAG_DIFF) {
convert_delta(xxs->data, xxs->len, xxs->flg & XMP_SAMPLE_16BIT);
} else if (flags & SAMPLE_FLAG_8BDIFF) {
int len = xxs->len;
if (xxs->flg & XMP_SAMPLE_16BIT) {
len *= 2;
}
convert_delta(xxs->data, len, 0);
}
/* Convert samples to signed */
if (flags & SAMPLE_FLAG_UNS) {
convert_signal(xxs->data, xxs->len,
xxs->flg & XMP_SAMPLE_16BIT);
}
#if 0
/* Downmix stereo samples */
if (flags & SAMPLE_FLAG_STEREO) {
convert_stereo_to_mono(xxs->data, xxs->len,
xxs->flg & XMP_SAMPLE_16BIT);
xxs->len /= 2;
}
#endif
#ifndef LIBXMP_CORE_PLAYER
if (flags & SAMPLE_FLAG_VIDC) {
convert_vidc_to_linear(xxs->data, xxs->len);
}
#endif
/* Check for full loop samples */
if (flags & SAMPLE_FLAG_FULLREP) {
if (xxs->lps == 0 && xxs->len > xxs->lpe)
xxs->flg |= XMP_SAMPLE_LOOP_FULL;
}
/* Unroll bidirectional loops */
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
unroll_loop(xxs);
bytelen += unroll_extralen;
}
/* Add extra samples at end */
if (xxs->flg & XMP_SAMPLE_16BIT) {
for (i = 0; i < 8; i++) {
xxs->data[bytelen + i] = xxs->data[bytelen - 2 + i];
}
} else {
for (i = 0; i < 4; i++) {
xxs->data[bytelen + i] = xxs->data[bytelen - 1 + i];
}
}
/* Add extra samples at start */
if (xxs->flg & XMP_SAMPLE_16BIT) {
xxs->data[-2] = xxs->data[0];
xxs->data[-1] = xxs->data[1];
} else {
xxs->data[-1] = xxs->data[0];
}
/* Fix sample at loop */
if (xxs->flg & XMP_SAMPLE_LOOP) {
int lpe = xxs->lpe;
int lps = xxs->lps;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
lpe += lpe - lps;
}
if (xxs->flg & XMP_SAMPLE_16BIT) {
lpe <<= 1;
lps <<= 1;
for (i = 0; i < 8; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
} else {
for (i = 0; i < 4; i++) {
xxs->data[lpe + i] = xxs->data[lps + i];
}
}
}
return 0;
#ifndef LIBXMP_CORE_PLAYER
err2:
free(xxs->data - 4);
#endif
err:
return -1;
}

View file

@ -0,0 +1,101 @@
#ifndef LIBXMP_LOADERS_XM_H
#define LIBXMP_LOADERS_XM_H
#define XM_EVENT_PACKING 0x80
#define XM_EVENT_PACK_MASK 0x7f
#define XM_EVENT_NOTE_FOLLOWS 0x01
#define XM_EVENT_INSTRUMENT_FOLLOWS 0x02
#define XM_EVENT_VOLUME_FOLLOWS 0x04
#define XM_EVENT_FXTYPE_FOLLOWS 0x08
#define XM_EVENT_FXPARM_FOLLOWS 0x10
#define XM_LINEAR_FREQ 0x01
#define XM_LOOP_MASK 0x03
#define XM_LOOP_NONE 0
#define XM_LOOP_FORWARD 1
#define XM_LOOP_PINGPONG 2
#define XM_SAMPLE_16BIT 0x10
#define XM_ENVELOPE_ON 0x01
#define XM_ENVELOPE_SUSTAIN 0x02
#define XM_ENVELOPE_LOOP 0x04
#define XM_LINEAR_PERIOD_MODE 0x01
struct xm_file_header {
uint8 id[17]; /* ID text: "Extended module: " */
uint8 name[20]; /* Module name, padded with zeroes */
uint8 doseof; /* 0x1a */
uint8 tracker[20]; /* Tracker name */
uint16 version; /* Version number, minor-major */
uint32 headersz; /* Header size */
uint16 songlen; /* Song length (in patten order table) */
uint16 restart; /* Restart position */
uint16 channels; /* Number of channels (2,4,6,8,10,...,32) */
uint16 patterns; /* Number of patterns (max 256) */
uint16 instruments; /* Number of instruments (max 128) */
uint16 flags; /* bit 0: 0=Amiga freq table, 1=Linear */
uint16 tempo; /* Default tempo */
uint16 bpm; /* Default BPM */
uint8 order[256]; /* Pattern order table */
};
struct xm_pattern_header {
uint32 length; /* Pattern header length */
uint8 packing; /* Packing type (always 0) */
uint16 rows; /* Number of rows in pattern (1..256) */
uint16 datasize; /* Packed patterndata size */
};
struct xm_instrument_header {
uint32 size; /* Instrument size */
uint8 name[22]; /* Instrument name */
uint8 type; /* Instrument type (always 0) */
uint16 samples; /* Number of samples in instrument */
uint32 sh_size; /* Sample header size */
};
struct xm_instrument {
uint8 sample[96]; /* Sample number for all notes */
uint16 v_env[24]; /* Points for volume envelope */
uint16 p_env[24]; /* Points for panning envelope */
uint8 v_pts; /* Number of volume points */
uint8 p_pts; /* Number of panning points */
uint8 v_sus; /* Volume sustain point */
uint8 v_start; /* Volume loop start point */
uint8 v_end; /* Volume loop end point */
uint8 p_sus; /* Panning sustain point */
uint8 p_start; /* Panning loop start point */
uint8 p_end; /* Panning loop end point */
uint8 v_type; /* Bit 0: On; 1: Sustain; 2: Loop */
uint8 p_type; /* Bit 0: On; 1: Sustain; 2: Loop */
uint8 y_wave; /* Vibrato waveform */
uint8 y_sweep; /* Vibrato sweep */
uint8 y_depth; /* Vibrato depth */
uint8 y_rate; /* Vibrato rate */
uint16 v_fade; /* Volume fadeout */
#if 0
uint8 reserved[22]; /* Reserved; 2 bytes in specs, 22 in 1.04 */
#endif
};
struct xm_sample_header {
uint32 length; /* Sample length */
uint32 loop_start; /* Sample loop start */
uint32 loop_length; /* Sample loop length */
uint8 volume; /* Volume */
int8 finetune; /* Finetune (signed byte -128..+127) */
uint8 type; /* 0=No loop,1=Fwd loop,2=Ping-pong,16-bit */
uint8 pan; /* Panning (0-255) */
int8 relnote; /* Relative note number (signed byte) */
uint8 reserved; /* Reserved */
uint8 name[22]; /* Sample_name */
};
struct xm_event {
uint8 note; /* Note (0-71, 0 = C-0) */
uint8 instrument; /* Instrument (0-128) */
uint8 volume; /* Volume column byte */
uint8 fx_type; /* Effect type */
uint8 fx_parm; /* Effect parameter */
};
#endif

View file

@ -0,0 +1,729 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Fri, 26 Jun 1998 17:45:59 +1000 Andrew Leahy <alf@cit.nepean.uws.edu.au>
* Finally got it working on the DEC Alpha running DEC UNIX! In the pattern
* reading loop I found I was getting "0" for (p-patbuf) and "0" for
* xph.datasize, the next if statement (where it tries to read the patbuf)
* would then cause a seg_fault.
*
* Sun Sep 27 12:07:12 EST 1998 Claudio Matsuoka <claudio@pos.inf.ufpr.br>
* Extended Module 1.02 stores data in a different order, we must handle
* this accordingly. MAX_SAMP used as a workaround to check the number of
* samples recognized by the player.
*/
#include "loader.h"
#include "xm.h"
static int xm_test(HIO_HANDLE *, char *, const int);
static int xm_load(struct module_data *, HIO_HANDLE *, const int);
const struct format_loader libxmp_loader_xm = {
"Fast Tracker II",
xm_test,
xm_load
};
static int xm_test(HIO_HANDLE *f, char *t, const int start)
{
char buf[20];
if (hio_read(buf, 1, 17, f) < 17) /* ID text */
return -1;
if (memcmp(buf, "Extended Module: ", 17))
return -1;
libxmp_read_title(f, t, 20);
return 0;
}
static int load_xm_pattern(struct module_data *m, int num, int version, HIO_HANDLE *f)
{
const int headsize = version > 0x0102 ? 9 : 8;
struct xmp_module *mod = &m->mod;
struct xm_pattern_header xph;
struct xmp_event *event;
uint8 *patbuf, *pat, b;
int j, r;
int size;
xph.length = hio_read32l(f);
xph.packing = hio_read8(f);
xph.rows = version > 0x0102 ? hio_read16l(f) : hio_read8(f) + 1;
/* Sanity check */
if (xph.rows > 256) {
goto err;
}
xph.datasize = hio_read16l(f);
hio_seek(f, xph.length - headsize, SEEK_CUR);
if (hio_error(f)) {
goto err;
}
r = xph.rows;
if (r == 0) {
r = 0x100;
}
if (libxmp_alloc_pattern_tracks(mod, num, r) < 0) {
goto err;
}
if (xph.datasize == 0) {
return 0;
}
size = xph.datasize;
pat = patbuf = calloc(1, size);
if (patbuf == NULL) {
goto err;
}
hio_read(patbuf, 1, size, f);
for (j = 0; j < (mod->chn * r); j++) {
/*if ((pat - patbuf) >= xph.datasize)
break; */
event = &EVENT(num, j % mod->chn, j / mod->chn);
if (--size < 0) {
goto err2;
}
if ((b = *pat++) & XM_EVENT_PACKING) {
if (b & XM_EVENT_NOTE_FOLLOWS) {
if (--size < 0)
goto err2;
event->note = *pat++;
}
if (b & XM_EVENT_INSTRUMENT_FOLLOWS) {
if (--size < 0)
goto err2;
event->ins = *pat++;
}
if (b & XM_EVENT_VOLUME_FOLLOWS) {
if (--size < 0)
goto err2;
event->vol = *pat++;
}
if (b & XM_EVENT_FXTYPE_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxt = *pat++;
}
if (b & XM_EVENT_FXPARM_FOLLOWS) {
if (--size < 0)
goto err2;
event->fxp = *pat++;
}
} else {
size -= 4;
if (size < 0)
goto err2;
event->note = b;
event->ins = *pat++;
event->vol = *pat++;
event->fxt = *pat++;
event->fxp = *pat++;
}
/* Sanity check */
switch (event->fxt) {
case 18:
case 19:
case 22:
case 23:
case 24:
case 26:
case 28:
case 30:
case 31:
case 32:
event->fxt = 0;
}
if (event->fxt > 34) {
event->fxt = 0;
}
if (event->note == 0x61) {
/* See OpenMPT keyoff+instr.xm test case */
if (event->fxt == 0x0e && MSN(event->fxp) == 0x0d) {
event->note = XMP_KEY_OFF;
} else {
event->note =
event->ins ? XMP_KEY_FADE : XMP_KEY_OFF;
}
} else if (event->note > 0) {
event->note += 12;
}
if (event->fxt == 0x0e) {
if (MSN(event->fxp) == EX_FINETUNE) {
unsigned char val = (LSN(event->fxp) - 8) & 0xf;
event->fxp = (EX_FINETUNE << 4) | val;
}
switch (event->fxp) {
case 0x43:
case 0x73:
event->fxp--;
break;
}
}
if (!event->vol) {
continue;
}
/* Volume set */
if ((event->vol >= 0x10) && (event->vol <= 0x50)) {
event->vol -= 0x0f;
continue;
}
/* Volume column effects */
switch (event->vol >> 4) {
case 0x06: /* Volume slide down */
event->f2t = FX_VOLSLIDE_2;
event->f2p = event->vol - 0x60;
break;
case 0x07: /* Volume slide up */
event->f2t = FX_VOLSLIDE_2;
event->f2p = (event->vol - 0x70) << 4;
break;
case 0x08: /* Fine volume slide down */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_DN << 4) | (event->vol - 0x80);
break;
case 0x09: /* Fine volume slide up */
event->f2t = FX_EXTENDED;
event->f2p =
(EX_F_VSLIDE_UP << 4) | (event->vol - 0x90);
break;
case 0x0a: /* Set vibrato speed */
event->f2t = FX_VIBRATO;
event->f2p = (event->vol - 0xa0) << 4;
break;
case 0x0b: /* Vibrato */
event->f2t = FX_VIBRATO;
event->f2p = event->vol - 0xb0;
break;
case 0x0c: /* Set panning */
event->f2t = FX_SETPAN;
event->f2p = (event->vol - 0xc0) << 4;
break;
case 0x0d: /* Pan slide left */
event->f2t = FX_PANSL_NOMEM;
event->f2p = (event->vol - 0xd0) << 4;
break;
case 0x0e: /* Pan slide right */
event->f2t = FX_PANSL_NOMEM;
event->f2p = event->vol - 0xe0;
break;
case 0x0f: /* Tone portamento */
event->f2t = FX_TONEPORTA;
event->f2p = (event->vol - 0xf0) << 4;
/* From OpenMPT TonePortamentoMemory.xm:
* "Another nice bug (...) is the combination of both
* portamento commands (Mx and 3xx) in the same cell:
* The 3xx parameter is ignored completely, and the Mx
* parameter is doubled. (M2 3FF is the same as M4 000)
*/
if (event->fxt == FX_TONEPORTA
|| event->fxt == FX_TONE_VSLIDE) {
if (event->fxt == FX_TONEPORTA) {
event->fxt = 0;
} else {
event->fxt = FX_VOLSLIDE;
}
event->fxp = 0;
if (event->f2p < 0x80) {
event->f2p <<= 1;
} else {
event->f2p = 0xff;
}
}
/* From OpenMPT porta-offset.xm:
* "If there is a portamento command next to an offset
* command, the offset command is ignored completely. In
* particular, the offset parameter is not memorized."
*/
if (event->fxt == FX_OFFSET
&& event->f2t == FX_TONEPORTA) {
event->fxt = event->fxp = 0;
}
break;
}
event->vol = 0;
}
free(patbuf);
return 0;
err2:
free(patbuf);
err:
return -1;
}
static int load_patterns(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
int i, j;
mod->pat++;
if (libxmp_init_pattern(mod) < 0) {
return -1;
}
D_(D_INFO "Stored patterns: %d", mod->pat - 1);
for (i = 0; i < mod->pat - 1; i++) {
if (load_xm_pattern(m, i, version, f) < 0) {
goto err;
}
}
/* Alloc one extra pattern */
{
int t = i * mod->chn;
if (libxmp_alloc_pattern(mod, i) < 0) {
goto err;
}
mod->xxp[i]->rows = 64;
if (libxmp_alloc_track(mod, t, 64) < 0) {
goto err;
}
for (j = 0; j < mod->chn; j++) {
mod->xxp[i]->index[j] = t;
}
}
return 0;
err:
return -1;
}
/* Packed structures size */
#define XM_INST_HEADER_SIZE 33
#define XM_INST_SIZE 208
static int load_instruments(struct module_data *m, int version, HIO_HANDLE *f)
{
struct xmp_module *mod = &m->mod;
struct xm_instrument_header xih;
struct xm_instrument xi;
struct xm_sample_header xsh[16];
int sample_num = 0;
int i, j;
D_(D_INFO "Instruments: %d", mod->ins);
/* ESTIMATED value! We don't know the actual value at this point */
mod->smp = MAX_SAMPLES;
if (libxmp_init_instrument(m) < 0)
return -1;
for (i = 0; i < mod->ins; i++) {
struct xmp_instrument *xxi = &mod->xxi[i];
xih.size = hio_read32l(f); /* Instrument size */
/* Modules converted with MOD2XM 1.0 always say we have 31
* instruments, but file may end abruptly before that. This test
* will not work if file has trailing garbage.
*/
if (hio_eof(f)) {
break;
}
hio_read(&xih.name, 22, 1, f); /* Instrument name */
xih.type = hio_read8(f); /* Instrument type (always 0) */
xih.samples = hio_read16l(f); /* Number of samples */
xih.sh_size = hio_read32l(f); /* Sample header size */
/* Sanity check */
if (xih.samples > 0x10 || (xih.samples > 0 && xih.sh_size > 0x100)) {
D_(D_CRIT "Sanity check: %d %d", xih.samples, xih.sh_size);
return -1;
}
libxmp_instrument_name(mod, i, xih.name, 22);
xxi->nsm = xih.samples;
if (xxi->nsm > 16)
xxi->nsm = 16;
D_(D_INFO "[%2X] %-22.22s %2d", i, xxi->name, xxi->nsm);
if (xxi->nsm) {
if (libxmp_alloc_subinstrument(mod, i, xxi->nsm) < 0)
return -1;
if (xih.size < XM_INST_HEADER_SIZE)
return -1;
/* for BoobieSqueezer (see http://boobie.rotfl.at/)
* It works pretty much the same way as Impulse Tracker's sample
* only mode, where it will strip off the instrument data.
*/
if (xih.size < XM_INST_HEADER_SIZE + XM_INST_SIZE) {
memset(&xi, 0, sizeof (struct xm_instrument));
hio_seek(f, xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
} else {
hio_read(&xi.sample, 96, 1, f); /* Sample map */
for (j = 0; j < 24; j++)
xi.v_env[j] = hio_read16l(f); /* Points for volume envelope */
for (j = 0; j < 24; j++)
xi.p_env[j] = hio_read16l(f); /* Points for pan envelope */
xi.v_pts = hio_read8(f); /* Number of volume points */
xi.p_pts = hio_read8(f); /* Number of pan points */
xi.v_sus = hio_read8(f); /* Volume sustain point */
xi.v_start = hio_read8(f); /* Volume loop start point */
xi.v_end = hio_read8(f); /* Volume loop end point */
xi.p_sus = hio_read8(f); /* Pan sustain point */
xi.p_start = hio_read8(f); /* Pan loop start point */
xi.p_end = hio_read8(f); /* Pan loop end point */
xi.v_type = hio_read8(f); /* Bit 0:On 1:Sustain 2:Loop */
xi.p_type = hio_read8(f); /* Bit 0:On 1:Sustain 2:Loop */
xi.y_wave = hio_read8(f); /* Vibrato waveform */
xi.y_sweep = hio_read8(f); /* Vibrato sweep */
xi.y_depth = hio_read8(f); /* Vibrato depth */
xi.y_rate = hio_read8(f); /* Vibrato rate */
xi.v_fade = hio_read16l(f); /* Volume fadeout */
/* Skip reserved space */
hio_seek(f, (int)xih.size - (XM_INST_HEADER_SIZE + XM_INST_SIZE), SEEK_CUR);
/* Envelope */
xxi->rls = xi.v_fade << 1;
xxi->aei.npt = xi.v_pts;
xxi->aei.sus = xi.v_sus;
xxi->aei.lps = xi.v_start;
xxi->aei.lpe = xi.v_end;
xxi->aei.flg = xi.v_type;
xxi->pei.npt = xi.p_pts;
xxi->pei.sus = xi.p_sus;
xxi->pei.lps = xi.p_start;
xxi->pei.lpe = xi.p_end;
xxi->pei.flg = xi.p_type;
if (xxi->aei.npt <= 0 || xxi->aei.npt > 12 /*XMP_MAX_ENV_POINTS*/)
xxi->aei.flg &= ~XMP_ENVELOPE_ON;
else
memcpy(xxi->aei.data, xi.v_env, xxi->aei.npt * 4);
if (xxi->pei.npt <= 0 || xxi->pei.npt > 12 /*XMP_MAX_ENV_POINTS*/)
xxi->pei.flg &= ~XMP_ENVELOPE_ON;
else
memcpy(xxi->pei.data, xi.p_env, xxi->pei.npt * 4);
for (j = 12; j < 108; j++) {
xxi->map[j].ins = xi.sample[j - 12];
if (xxi->map[j].ins >= xxi->nsm)
xxi->map[j].ins = -1;
}
}
for (j = 0; j < xxi->nsm; j++, sample_num++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
struct xmp_sample *xxs;
if (sample_num >= mod->smp) {
mod->xxs = libxmp_realloc_samples(mod->xxs, &mod->smp, mod->smp * 3 / 2);
if (mod->xxs == NULL)
return -1;
}
xxs = &mod->xxs[sample_num];
xsh[j].length = hio_read32l(f); /* Sample length */
/* Sanity check */
if (xsh[j].length > MAX_SAMPLE_SIZE)
return -1;
xsh[j].loop_start = hio_read32l(f); /* Sample loop start */
xsh[j].loop_length = hio_read32l(f); /* Sample loop length */
xsh[j].volume = hio_read8(f); /* Volume */
xsh[j].finetune = hio_read8s(f); /* Finetune (-128..+127) */
xsh[j].type = hio_read8(f); /* Flags */
xsh[j].pan = hio_read8(f); /* Panning (0-255) */
xsh[j].relnote = hio_read8s(f); /* Relative note number */
xsh[j].reserved = hio_read8(f);
hio_read(&xsh[j].name, 22, 1, f); /* Sample_name */
sub->vol = xsh[j].volume;
sub->pan = xsh[j].pan;
sub->xpo = xsh[j].relnote;
sub->fin = xsh[j].finetune;
sub->vwf = xi.y_wave;
sub->vde = xi.y_depth << 2;
sub->vra = xi.y_rate;
sub->vsw = xi.y_sweep;
sub->sid = sample_num;
libxmp_copy_adjust(xxs->name, xsh[j].name, 22);
xxs->len = xsh[j].length;
xxs->lps = xsh[j].loop_start;
xxs->lpe = xsh[j].loop_start + xsh[j].loop_length;
xxs->flg = 0;
if (xsh[j].type & XM_SAMPLE_16BIT) {
xxs->flg |= XMP_SAMPLE_16BIT;
xxs->len >>= 1;
xxs->lps >>= 1;
xxs->lpe >>= 1;
}
xxs->flg |= xsh[j].type & XM_LOOP_FORWARD ?
XMP_SAMPLE_LOOP : 0;
xxs->flg |= xsh[j].type & XM_LOOP_PINGPONG ?
XMP_SAMPLE_LOOP | XMP_SAMPLE_LOOP_BIDIR : 0;
}
for (j = 0; j < xxi->nsm; j++) {
struct xmp_subinstrument *sub = &xxi->sub[j];
int flags;
D_(D_INFO " %1x: %06x%c%06x %06x %c V%02x F%+04d P%02x R%+03d",
j, mod->xxs[sub->sid].len,
mod->xxs[sub->sid].flg & XMP_SAMPLE_16BIT ? '+' : ' ',
mod->xxs[sub->sid].lps,
mod->xxs[sub->sid].lpe,
mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP_BIDIR ? 'B' :
mod->xxs[sub->sid].flg & XMP_SAMPLE_LOOP ? 'L' : ' ',
sub->vol, sub->fin,
sub->pan, sub->xpo);
flags = SAMPLE_FLAG_DIFF;
#ifndef LIBXMP_CORE_PLAYER
if (xsh[j].reserved == 0xad) {
flags = SAMPLE_FLAG_ADPCM;
}
#endif
if (version > 0x0103) {
if (libxmp_load_sample(m, f, flags,
&mod->xxs[sub->sid], NULL) < 0) {
return -1;
}
}
}
} else {
/* Sample size should be in struct xm_instrument according to
* the official format description, but FT2 actually puts it in
* struct xm_instrument header. There's a tracker or converter
* that follow the specs, so we must handle both cases (see
* "Braintomb" by Jazztiz/ART).
*/
/* Umm, Cyke O'Path <cyker@heatwave.co.uk> sent me a couple of
* mods ("Breath of the Wind" and "Broken Dimension") that
* reserve the instrument data space after the instrument header
* even if the number of instruments is set to 0. In these modules
* the instrument header size is marked as 263. The following
* generalization should take care of both cases.
*/
hio_seek(f, (int)xih.size - XM_INST_HEADER_SIZE, SEEK_CUR);
}
}
/* Final sample number adjustment */
mod->xxs = libxmp_realloc_samples(mod->xxs, &mod->smp, sample_num);
if (mod->xxs == NULL)
return -1;
return 0;
}
static int xm_load(struct module_data *m, HIO_HANDLE * f, const int start)
{
struct xmp_module *mod = &m->mod;
int i, j;
struct xm_file_header xfh;
char tracker_name[21];
int len;
LOAD_INIT();
hio_read(&xfh.id, 17, 1, f); /* ID text */
hio_read(&xfh.name, 20, 1, f); /* Module name */
hio_read8(f); /* 0x1a */
hio_read(&xfh.tracker, 20, 1, f); /* Tracker name */
xfh.version = hio_read16l(f); /* Version number, minor-major */
xfh.headersz = hio_read32l(f); /* Header size */
xfh.songlen = hio_read16l(f); /* Song length */
xfh.restart = hio_read16l(f); /* Restart position */
xfh.channels = hio_read16l(f); /* Number of channels */
xfh.patterns = hio_read16l(f); /* Number of patterns */
xfh.instruments = hio_read16l(f); /* Number of instruments */
xfh.flags = hio_read16l(f); /* 0=Amiga freq table, 1=Linear */
xfh.tempo = hio_read16l(f); /* Default tempo */
xfh.bpm = hio_read16l(f); /* Default BPM */
/* Sanity checks */
if (xfh.songlen > 256 || xfh.patterns > 256 || xfh.instruments > 255) {
D_(D_CRIT "Sanity check: %d %d %d", xfh.songlen, xfh.patterns,
xfh.instruments);
return -1;
}
if (xfh.restart > 255 || xfh.channels > XMP_MAX_CHANNELS) {
D_(D_CRIT "Sanity check: %d %d", xfh.restart, xfh.channels);
return -1;
}
if (xfh.tempo >= 32 || xfh.bpm < 32 || xfh.bpm > 255) {
if (memcmp("MED2XM", xfh.tracker, 6)) {
D_(D_CRIT "Sanity check: %d %d", xfh.tempo, xfh.bpm);
return -1;
}
}
len = xfh.headersz - 0x14;
if (len < 0 || len > 256) {
D_(D_CRIT "Sanity check: %d", len);
return -1;
}
/* Honor header size -- needed by BoobieSqueezer XMs */
hio_read(&xfh.order, len, 1, f); /* Pattern order table */
strncpy(mod->name, (char *)xfh.name, 20);
mod->len = xfh.songlen;
mod->chn = xfh.channels;
mod->pat = xfh.patterns;
mod->ins = xfh.instruments;
mod->rst = xfh.restart;
mod->spd = xfh.tempo;
mod->bpm = xfh.bpm;
mod->trk = mod->chn * mod->pat + 1;
m->c4rate = C4_NTSC_RATE;
m->period_type = xfh.flags & XM_LINEAR_PERIOD_MODE ?
PERIOD_LINEAR : PERIOD_AMIGA;
memcpy(mod->xxo, xfh.order, mod->len);
/*tracker_name[20] = 0;*/
snprintf(tracker_name, 21, "%-20.20s", xfh.tracker);
for (i = 20; i >= 0; i--) {
if (tracker_name[i] == 0x20)
tracker_name[i] = 0;
if (tracker_name[i])
break;
}
/* OpenMPT accurately emulates weird FT2 bugs */
if (!strncmp(tracker_name, "FastTracker v2.00", 17) ||
!strncmp(tracker_name, "OpenMPT ", 8)) {
m->quirk |= QUIRK_FT2BUGS;
}
#ifndef LIBXMP_CORE_PLAYER
if (xfh.headersz == 0x0113) {
strcpy(tracker_name, "unknown tracker");
m->quirk &= ~QUIRK_FT2BUGS;
} else if (*tracker_name == 0) {
strcpy(tracker_name, "Digitrakker"); /* best guess */
m->quirk &= ~QUIRK_FT2BUGS;
}
/* See MMD1 loader for explanation */
if (!strncmp(tracker_name, "MED2XM by J.Pynnone", 19)) {
if (mod->bpm <= 10)
mod->bpm = 125 * (0x35 - mod->bpm * 2) / 33;
m->quirk &= ~QUIRK_FT2BUGS;
}
if (!strncmp(tracker_name, "FastTracker v 2.00", 18)) {
strcpy(tracker_name, "old ModPlug Tracker");
m->quirk &= ~QUIRK_FT2BUGS;
}
libxmp_set_type(m, "%s XM %d.%02d", tracker_name,
xfh.version >> 8, xfh.version & 0xff);
#else
libxmp_set_type(m, tracker_name);
#endif
MODULE_INFO();
/* Honor header size */
hio_seek(f, start + xfh.headersz + 60, SEEK_SET);
/* XM 1.02/1.03 has a different patterns and instruments order */
if (xfh.version <= 0x0103) {
if (load_instruments(m, xfh.version, f) < 0)
return -1;
if (load_patterns(m, xfh.version, f) < 0)
return -1;
} else {
if (load_patterns(m, xfh.version, f) < 0)
return -1;
if (load_instruments(m, xfh.version, f) < 0)
return -1;
}
D_(D_INFO "Stored samples: %d", mod->smp);
/* XM 1.02 stores all samples after the patterns */
if (xfh.version <= 0x0103) {
for (i = 0; i < mod->ins; i++) {
for (j = 0; j < mod->xxi[i].nsm; j++) {
int sid = mod->xxi[i].sub[j].sid;
if (libxmp_load_sample(m, f, SAMPLE_FLAG_DIFF,
&mod->xxs[sid], NULL) < 0) {
return -1;
}
}
}
}
for (i = 0; i < mod->chn; i++) {
mod->xxc[i].pan = 0x80;
}
m->quirk |= QUIRKS_FT2;
m->read_event_type = READ_EVENT_FT2;
return 0;
}

View file

@ -0,0 +1,105 @@
#ifndef LIBXMP_MDATAIO_H
#define LIBXMP_MDATAIO_H
#include <stddef.h>
#include "common.h"
static inline ptrdiff_t CAN_READ(MFILE *m)
{
if (m->size >= 0)
return m->pos >= 0 ? m->size - m->pos : 0;
return INT_MAX;
}
static inline uint8 mread8(MFILE *m)
{
uint8 x = 0xff;
mread(&x, 1, 1, m);
return x;
}
static inline int8 mread8s(MFILE *m)
{
return (int8)mgetc(m);
}
static inline uint16 mread16l(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 2) {
uint16 n = readmem16l(m->start + m->pos);
m->pos += 2;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
static inline uint16 mread16b(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 2) {
uint16 n = readmem16b(m->start + m->pos);
m->pos += 2;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
static inline uint32 mread24l(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 3) {
uint32 n = readmem24l(m->start + m->pos);
m->pos += 3;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
static inline uint32 mread24b(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 3) {
uint32 n = readmem24b(m->start + m->pos);
m->pos += 3;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
static inline uint32 mread32l(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 4) {
uint32 n = readmem32l(m->start + m->pos);
m->pos += 4;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
static inline uint32 mread32b(MFILE *m)
{
ptrdiff_t can_read = CAN_READ(m);
if (can_read >= 4) {
uint32 n = readmem32b(m->start + m->pos);
m->pos += 4;
return n;
} else {
m->pos += can_read;
return EOF;
}
}
#endif

View file

@ -0,0 +1,136 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#ifndef LIBXMP_CORE_PLAYER
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "common.h"
#include "memio.h"
static inline ptrdiff_t CAN_READ(MFILE *m)
{
if (m->size >= 0)
return m->pos >= 0 ? m->size - m->pos : 0;
return INT_MAX;
}
int mgetc(MFILE *m)
{
if (CAN_READ(m) >= 1)
return *(uint8 *)(m->start + m->pos++);
else
return EOF;
}
size_t mread(void *buf, size_t size, size_t num, MFILE *m)
{
size_t should_read = size * num;
ptrdiff_t can_read = CAN_READ(m);
if (size <= 0 || num <= 0 || can_read <= 0) {
return 0;
}
if (should_read > can_read) {
should_read = can_read;
}
memcpy(buf, m->start + m->pos, should_read);
m->pos += should_read;
return should_read / size;
}
int mseek(MFILE *m, long offset, int whence)
{
switch (whence) {
default:
case SEEK_SET:
if (m->size >= 0 && (offset > m->size || offset < 0))
return -1;
m->pos = offset;
return 0;
case SEEK_CUR:
if (m->size >= 0 && (offset > CAN_READ(m) || offset < -m->pos))
return -1;
m->pos += offset;
return 0;
case SEEK_END:
if (m->size < 0)
return -1;
m->pos = m->size + offset;
return 0;
}
}
long mtell(MFILE *m)
{
return (long)m->pos;
}
int meof(MFILE *m)
{
if (m->size <= 0)
return 0;
else
return CAN_READ(m) <= 0;
}
MFILE *mopen(void *ptr, long size)
{
MFILE *m;
m = (MFILE *)malloc(sizeof (MFILE));
if (m == NULL)
return NULL;
m->start = ptr;
m->pos = 0;
m->size = size;
return m;
}
int mclose(MFILE *m)
{
free(m);
return 0;
}
#ifndef LIBXMP_CORE_PLAYER
int mstat(MFILE *m, struct stat *st)
{
memset(st, 0, sizeof (struct stat));
st->st_size = m->size;
return 0;
}
#endif

View file

@ -0,0 +1,31 @@
#ifndef LIBXMP_MEMIO_H
#define LIBXMP_MEMIO_H
#include <stdio.h>
typedef struct {
unsigned char *start;
ptrdiff_t pos;
ptrdiff_t size;
} MFILE;
#ifdef __cplusplus
extern "C" {
#endif
MFILE *mopen(void *, long);
int mgetc(MFILE *stream);
size_t mread(void *, size_t, size_t, MFILE *);
int mseek(MFILE *, long, int);
long mtell(MFILE *);
int mclose(MFILE *);
int meof(MFILE *);
#ifndef LIBXMP_CORE_PLAYER
int mstat(MFILE *, struct stat *);
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,439 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#include "precomp_lut.h"
/* Mixers
*
* To increase performance eight mixers are defined, one for each
* combination of the following parameters: interpolation, resolution
* and number of channels.
*/
#define NEAREST_NEIGHBOR() do { \
smp_in = ((int16)sptr[pos] << 8); \
} while (0)
#define NEAREST_NEIGHBOR_16BIT() do { \
smp_in = sptr[pos]; \
} while (0)
#define LINEAR_INTERP() do { \
smp_l1 = ((int16)sptr[pos] << 8); \
smp_dt = ((int16)sptr[pos + 1] << 8) - smp_l1; \
smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)
#define LINEAR_INTERP_16BIT() do { \
smp_l1 = sptr[pos]; \
smp_dt = sptr[pos + 1] - smp_l1; \
smp_in = smp_l1 + (((frac >> 1) * smp_dt) >> (SMIX_SHIFT - 1)); \
} while (0)
/* The following lut settings are PRECOMPUTED. If you plan on changing these
* settings, you MUST also regenerate the arrays.
*/
/* number of bits used to scale spline coefs */
#define SPLINE_QUANTBITS 14
#define SPLINE_SHIFT (SPLINE_QUANTBITS)
/* log2(number) of precalculated splines (range is [4..14]) */
#define SPLINE_FRACBITS 10
#define SPLINE_LUTLEN (1L<<SPLINE_FRACBITS)
#define SPLINE_FRACSHIFT ((16 - SPLINE_FRACBITS) - 2)
#define SPLINE_FRACMASK (((1L << (16 - SPLINE_FRACSHIFT)) - 1) & ~3)
#define SPLINE_INTERP() do { \
int f = frac >> 6; \
smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
cubic_spline_lut1[f] * sptr[pos ] + \
cubic_spline_lut3[f] * sptr[pos + 2] + \
cubic_spline_lut2[f] * sptr[pos + 1]) >> (SPLINE_SHIFT - 8); \
} while (0)
#define SPLINE_INTERP_16BIT() do { \
int f = frac >> 6; \
smp_in = (cubic_spline_lut0[f] * sptr[(int)pos - 1] + \
cubic_spline_lut1[f] * sptr[pos ] + \
cubic_spline_lut3[f] * sptr[pos + 2] + \
cubic_spline_lut2[f] * sptr[pos + 1]) >> SPLINE_SHIFT; \
} while (0)
#define LOOP_AC for (; count > ramp; count--)
#define LOOP for (; count; count--)
#define UPDATE_POS() do { \
frac += step; \
pos += frac >> SMIX_SHIFT; \
frac &= SMIX_MASK; \
} while (0)
#define MIX_MONO() do { \
*(buffer++) += smp_in * vl; \
} while (0)
#define MIX_MONO_AC() do { \
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)
#define MIX_MONO_FILTER() do { \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sl; \
} while (0)
#define MIX_MONO_FILTER_AC() do { \
int vl = old_vl >> 8; \
MIX_MONO_FILTER(); \
old_vl += delta_l; \
} while (0)
#define MIX_STEREO() do { \
*(buffer++) += smp_in * vr; \
*(buffer++) += smp_in * vl; \
} while (0)
#define MIX_STEREO_AC() do { \
*(buffer++) += smp_in * (old_vr >> 8); old_vr += delta_r; \
*(buffer++) += smp_in * (old_vl >> 8); old_vl += delta_l; \
} while (0)
#define MIX_STEREO_FILTER() do { \
sr = (a0 * smp_in * vr + b0 * fr1 + b1 * fr2) >> FILTER_SHIFT; \
fr2 = fr1; fr1 = sr; \
sl = (a0 * smp_in * vl + b0 * fl1 + b1 * fl2) >> FILTER_SHIFT; \
fl2 = fl1; fl1 = sl; \
*(buffer++) += sr; \
*(buffer++) += sl; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
int vr = old_vr >> 8; \
int vl = old_vl >> 8; \
MIX_STEREO_FILTER(); \
old_vr += delta_r; \
old_vl += delta_l; \
} while (0)
#define MIX_STEREO_FILTER_AC() do { \
int vr = old_vr >> 8; \
int vl = old_vl >> 8; \
MIX_STEREO_FILTER(); \
old_vr += delta_r; \
old_vl += delta_l; \
} while (0)
#define VAR_NORM(x) \
register int smp_in; \
x *sptr = vi->sptr; \
unsigned int pos = vi->pos; \
int frac = (1 << SMIX_SHIFT) * (vi->pos - (int)vi->pos)
#define VAR_LINEAR_MONO(x) \
VAR_NORM(x); \
int old_vl = vi->old_vl; \
int smp_l1, smp_dt
#define VAR_LINEAR_STEREO(x) \
VAR_LINEAR_MONO(x); \
int old_vr = vi->old_vr
#define VAR_SPLINE_MONO(x) \
int old_vl = vi->old_vl; \
VAR_NORM(x)
#define VAR_SPLINE_STEREO(x); \
VAR_SPLINE_MONO(x); \
int old_vr = vi->old_vr; \
#ifndef LIBXMP_CORE_DISABLE_IT
#define VAR_FILTER_MONO \
int fl1 = vi->filter.l1, fl2 = vi->filter.l2; \
int64 a0 = vi->filter.a0, b0 = vi->filter.b0, b1 = vi->filter.b1; \
int sl
#define VAR_FILTER_STEREO \
VAR_FILTER_MONO; \
int fr1 = vi->filter.r1, fr2 = vi->filter.r2; \
int sr
#define SAVE_FILTER_MONO() do { \
vi->filter.l1 = fl1; \
vi->filter.l2 = fl2; \
} while (0)
#define SAVE_FILTER_STEREO() do { \
SAVE_FILTER_MONO(); \
vi->filter.r1 = fr1; \
vi->filter.r2 = fr2; \
} while (0)
#endif
/*
* Nearest neighbor mixers
*/
/* Handler for 8 bit samples, nearest neighbor mono output
*/
MIXER(mono_8bit_nearest)
{
VAR_NORM(int8);
LOOP { NEAREST_NEIGHBOR(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, nearest neighbor mono output
*/
MIXER(mono_16bit_nearest)
{
VAR_NORM(int16);
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, nearest neighbor stereo output
*/
MIXER(stereo_8bit_nearest)
{
VAR_NORM(int8);
LOOP { NEAREST_NEIGHBOR(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, nearest neighbor stereo output
*/
MIXER(stereo_16bit_nearest)
{
VAR_NORM(int16);
LOOP { NEAREST_NEIGHBOR_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
/*
* Linear mixers
*/
/* Handler for 8 bit samples, linear interpolated mono output
*/
MIXER(mono_8bit_linear)
{
VAR_LINEAR_MONO(int8);
LOOP_AC { LINEAR_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, linear interpolated mono output
*/
MIXER(mono_16bit_linear)
{
VAR_LINEAR_MONO(int16);
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, linear interpolated stereo output
*/
MIXER(stereo_8bit_linear)
{
VAR_LINEAR_STEREO(int8);
LOOP_AC { LINEAR_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, linear interpolated stereo output
*/
MIXER(stereo_16bit_linear)
{
VAR_LINEAR_STEREO(int16);
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* Handler for 8 bit samples, filtered linear interpolated mono output
*/
MIXER(mono_8bit_linear_filter)
{
VAR_LINEAR_MONO(int8);
VAR_FILTER_MONO;
LOOP_AC { LINEAR_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 16 bit samples, filtered linear interpolated mono output
*/
MIXER(mono_16bit_linear_filter)
{
VAR_LINEAR_MONO(int16);
VAR_FILTER_MONO;
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 8 bit samples, filtered linear interpolated stereo output
*/
MIXER(stereo_8bit_linear_filter)
{
VAR_LINEAR_STEREO(int8);
VAR_FILTER_STEREO;
LOOP_AC { LINEAR_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
/* Handler for 16 bit samples, filtered linear interpolated stereo output
*/
MIXER(stereo_16bit_linear_filter)
{
VAR_LINEAR_STEREO(int16);
VAR_FILTER_STEREO;
LOOP_AC { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { LINEAR_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
#endif
/*
* Spline mixers
*/
/* Handler for 8 bit samples, spline interpolated mono output
*/
MIXER(mono_8bit_spline)
{
VAR_SPLINE_MONO(int8);
LOOP_AC { SPLINE_INTERP(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, spline interpolated mono output
*/
MIXER(mono_16bit_spline)
{
VAR_SPLINE_MONO(int16);
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_MONO(); UPDATE_POS(); }
}
/* Handler for 8 bit samples, spline interpolated stereo output
*/
MIXER(stereo_8bit_spline)
{
VAR_SPLINE_STEREO(int8);
LOOP_AC { SPLINE_INTERP(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_STEREO(); UPDATE_POS(); }
}
/* Handler for 16 bit samples, spline interpolated stereo output
*/
MIXER(stereo_16bit_spline)
{
VAR_SPLINE_STEREO(int16);
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO(); UPDATE_POS(); }
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* Handler for 8 bit samples, filtered spline interpolated mono output
*/
MIXER(mono_8bit_spline_filter)
{
VAR_SPLINE_MONO(int8);
VAR_FILTER_MONO;
LOOP_AC { SPLINE_INTERP(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 16 bit samples, filtered spline interpolated mono output
*/
MIXER(mono_16bit_spline_filter)
{
VAR_SPLINE_MONO(int16);
VAR_FILTER_MONO;
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_MONO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_MONO();
}
/* Handler for 8 bit samples, filtered spline interpolated stereo output
*/
MIXER(stereo_8bit_spline_filter)
{
VAR_SPLINE_STEREO(int8);
VAR_FILTER_STEREO;
LOOP_AC { SPLINE_INTERP(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
/* Handler for 16 bit samples, filtered spline interpolated stereo output
*/
MIXER(stereo_16bit_spline_filter)
{
VAR_SPLINE_STEREO(int16);
VAR_FILTER_STEREO;
LOOP_AC { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER_AC(); UPDATE_POS(); }
LOOP { SPLINE_INTERP_16BIT(); MIX_STEREO_FILTER(); UPDATE_POS(); }
SAVE_FILTER_STEREO();
}
#endif

View file

@ -0,0 +1,840 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#include "period.h"
#include "player.h" /* for set_sample_end() */
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define FLAG_16_BITS 0x01
#define FLAG_STEREO 0x02
#define FLAG_FILTER 0x04
#define FLAG_ACTIVE 0x10
/* #define FLAG_SYNTH 0x20 */
#define FIDX_FLAGMASK (FLAG_16_BITS | FLAG_STEREO | FLAG_FILTER)
#define DOWNMIX_SHIFT 12
#define LIM8_HI 127
#define LIM8_LO -128
#define LIM16_HI 32767
#define LIM16_LO -32768
#define MIX_FN(x) void libxmp_mix_##x(struct mixer_voice *, int *, int, int, int, int, int, int, int)
MIX_FN(mono_8bit_nearest);
MIX_FN(mono_8bit_linear);
MIX_FN(mono_16bit_nearest);
MIX_FN(mono_16bit_linear);
MIX_FN(stereo_8bit_nearest);
MIX_FN(stereo_8bit_linear);
MIX_FN(stereo_16bit_nearest);
MIX_FN(stereo_16bit_linear);
MIX_FN(mono_8bit_spline);
MIX_FN(mono_16bit_spline);
MIX_FN(stereo_8bit_spline);
MIX_FN(stereo_16bit_spline);
#ifndef LIBXMP_CORE_DISABLE_IT
MIX_FN(mono_8bit_linear_filter);
MIX_FN(mono_16bit_linear_filter);
MIX_FN(stereo_8bit_linear_filter);
MIX_FN(stereo_16bit_linear_filter);
MIX_FN(mono_8bit_spline_filter);
MIX_FN(mono_16bit_spline_filter);
MIX_FN(stereo_8bit_spline_filter);
MIX_FN(stereo_16bit_spline_filter);
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
MIX_FN(mono_a500);
MIX_FN(mono_a500_filter);
MIX_FN(stereo_a500);
MIX_FN(stereo_a500_filter);
#endif
/* Mixers array index:
*
* bit 0: 0=8 bit sample, 1=16 bit sample
* bit 1: 0=mono output, 1=stereo output
* bit 2: 0=unfiltered, 1=filtered
*/
typedef void (*mixer_set[])(struct mixer_voice *, int *, int, int, int, int, int, int, int);
static mixer_set nearest_mixers = {
libxmp_mix_mono_8bit_nearest,
libxmp_mix_mono_16bit_nearest,
libxmp_mix_stereo_8bit_nearest,
libxmp_mix_stereo_16bit_nearest,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_nearest,
libxmp_mix_mono_16bit_nearest,
libxmp_mix_stereo_8bit_nearest,
libxmp_mix_stereo_16bit_nearest,
#endif
};
static mixer_set linear_mixers = {
libxmp_mix_mono_8bit_linear,
libxmp_mix_mono_16bit_linear,
libxmp_mix_stereo_8bit_linear,
libxmp_mix_stereo_16bit_linear,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_linear_filter,
libxmp_mix_mono_16bit_linear_filter,
libxmp_mix_stereo_8bit_linear_filter,
libxmp_mix_stereo_16bit_linear_filter
#endif
};
static mixer_set spline_mixers = {
libxmp_mix_mono_8bit_spline,
libxmp_mix_mono_16bit_spline,
libxmp_mix_stereo_8bit_spline,
libxmp_mix_stereo_16bit_spline,
#ifndef LIBXMP_CORE_DISABLE_IT
libxmp_mix_mono_8bit_spline_filter,
libxmp_mix_mono_16bit_spline_filter,
libxmp_mix_stereo_8bit_spline_filter,
libxmp_mix_stereo_16bit_spline_filter
#endif
};
#ifdef LIBXMP_PAULA_SIMULATOR
static mixer_set a500_mixers = {
libxmp_mix_mono_a500,
NULL,
libxmp_mix_stereo_a500,
NULL,
NULL,
NULL,
NULL,
NULL
};
static mixer_set a500led_mixers = {
libxmp_mix_mono_a500_filter,
NULL,
libxmp_mix_stereo_a500_filter,
NULL,
NULL,
NULL,
NULL,
NULL
};
#endif
/* Downmix 32bit samples to 8bit, signed or unsigned, mono or stereo output */
static void downmix_int_8bit(char *dest, int32 *src, int num, int amp, int offs)
{
int smp;
int shift = DOWNMIX_SHIFT + 8 - amp;
for (; num--; src++, dest++) {
smp = *src >> shift;
if (smp > LIM8_HI) {
*dest = LIM8_HI;
} else if (smp < LIM8_LO) {
*dest = LIM8_LO;
} else {
*dest = smp;
}
if (offs) *dest += offs;
}
}
/* Downmix 32bit samples to 16bit, signed or unsigned, mono or stereo output */
static void downmix_int_16bit(int16 *dest, int32 *src, int num, int amp, int offs)
{
int smp;
int shift = DOWNMIX_SHIFT - amp;
for (; num--; src++, dest++) {
smp = *src >> shift;
if (smp > LIM16_HI) {
*dest = LIM16_HI;
} else if (smp < LIM16_LO) {
*dest = LIM16_LO;
} else {
*dest = smp;
}
if (offs) *dest += offs;
}
}
static void anticlick(struct mixer_voice *vi)
{
vi->flags |= ANTICLICK;
vi->old_vl = 0;
vi->old_vr = 0;
}
/* Ok, it's messy, but it works :-) Hipolito */
static void do_anticlick(struct context_data *ctx, int voc, int32 *buf, int count)
{
struct player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc];
int smp_l, smp_r, max_x2;
int discharge = s->ticksize >> ANTICLICK_SHIFT;
smp_r = vi->sright;
smp_l = vi->sleft;
vi->sright = vi->sleft = 0;
if (smp_l == 0 && smp_r == 0) {
return;
}
if (buf == NULL) {
buf = s->buf32;
count = discharge;
} else if (count > discharge) {
count = discharge;
}
if (count <= 0) {
return;
}
max_x2 = count * count;
while (count--) {
if (~s->format & XMP_FORMAT_MONO) {
*buf++ += (count * (smp_r >> 10) / max_x2 * count) << 10;
}
*buf++ += (count * (smp_l >> 10) / max_x2 * count) << 10;
}
}
static void set_sample_end(struct context_data *ctx, int voc, int end)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct channel_data *xc;
if ((uint32)voc >= p->virt.maxvoc)
return;
xc = &p->xc_data[vi->chn];
if (end) {
SET_NOTE(NOTE_SAMPLE_END);
if (HAS_QUIRK(QUIRK_RSTCHN)) {
libxmp_virt_resetvoice(ctx, voc, 0);
}
} else {
RESET_NOTE(NOTE_SAMPLE_END);
}
}
static void adjust_voice_end(struct mixer_voice *vi, struct xmp_sample *xxs)
{
if (xxs->flg & XMP_SAMPLE_LOOP) {
if ((xxs->flg & XMP_SAMPLE_LOOP_FULL) && (~vi->flags & SAMPLE_LOOP)) {
vi->end = xxs->len;
} else {
vi->end = xxs->lpe;
}
} else {
vi->end = xxs->len;
}
}
static void loop_reposition(struct context_data *ctx, struct mixer_voice *vi, struct xmp_sample *xxs)
{
#ifndef LIBXMP_CORE_DISABLE_IT
struct module_data *m = &ctx->m;
#endif
int loop_size = xxs->lpe - xxs->lps;
/* Reposition for next loop */
vi->pos -= loop_size; /* forward loop */
vi->end = xxs->lpe;
vi->flags |= SAMPLE_LOOP;
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += loop_size; /* unrolled loop */
vi->pos -= loop_size; /* forward loop */
#ifndef LIBXMP_CORE_DISABLE_IT
/* OpenMPT Bidi-Loops.it: "In Impulse Trackers software mixer,
* ping-pong loops are shortened by one sample.
*/
if (IS_PLAYER_MODE_IT()) {
vi->end--;
vi->pos++;
}
#endif
}
}
/* Prepare the mixer for the next tick */
void libxmp_mixer_prepare(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_data *s = &ctx->s;
int bytelen;
s->ticksize = s->freq * m->time_factor * m->rrate / p->bpm / 1000;
bytelen = s->ticksize * sizeof(int);
if (~s->format & XMP_FORMAT_MONO) {
bytelen *= 2;
}
memset(s->buf32, 0, bytelen);
}
/* Fill the output buffer calling one of the handlers. The buffer contains
* sound for one tick (a PAL frame or 1/50s for standard vblank-timed mods)
*/
void libxmp_mixer_softmixer(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct mixer_data *s = &ctx->s;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
struct mixer_voice *vi;
double step;
int samples, size;
int vol_l, vol_r, voc, usmp;
int prev_l, prev_r = 0;
int lps, lpe;
int32 *buf_pos;
void (*mix_fn)(struct mixer_voice *, int *, int, int, int, int, int, int, int);
mixer_set *mixers;
switch (s->interp) {
case XMP_INTERP_NEAREST:
mixers = &nearest_mixers;
break;
case XMP_INTERP_LINEAR:
mixers = &linear_mixers;
break;
case XMP_INTERP_SPLINE:
mixers = &spline_mixers;
break;
default:
mixers = &linear_mixers;
}
#ifdef LIBXMP_PAULA_SIMULATOR
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
if (p->filter) {
mixers = &a500led_mixers;
} else {
mixers = &a500_mixers;
}
}
}
#endif
libxmp_mixer_prepare(ctx);
for (voc = 0; voc < p->virt.maxvoc; voc++) {
int c5spd;
vi = &p->virt.voice_array[voc];
if (vi->flags & ANTICLICK) {
if (s->interp > XMP_INTERP_NEAREST) {
do_anticlick(ctx, voc, NULL, 0);
}
vi->flags &= ~ANTICLICK;
}
if (vi->chn < 0) {
continue;
}
if (vi->period < 1) {
libxmp_virt_resetvoice(ctx, voc, 1);
continue;
}
vi->pos0 = vi->pos;
buf_pos = s->buf32;
if (vi->pan == PAN_SURROUND) {
vol_r = vi->vol * 0x80;
vol_l = -vi->vol * 0x80;
} else {
vol_r = vi->vol * (0x80 - vi->pan);
vol_l = vi->vol * (0x80 + vi->pan);
}
if (vi->smp < mod->smp) {
xxs = &mod->xxs[vi->smp];
c5spd = m->xtra[vi->smp].c5spd;
} else {
xxs = &ctx->smix.xxs[vi->smp - mod->smp];
c5spd = m->c4rate;
}
step = C4_PERIOD * c5spd / s->freq / vi->period;
if (step < 0.001) { /* otherwise m5v-nwlf.it crashes */
continue;
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (xxs->flg & XMP_SAMPLE_SLOOP && vi->smp < mod->smp) {
if (~vi->flags & VOICE_RELEASE) {
if (vi->pos < m->xsmp[vi->smp].lpe) {
xxs = &m->xsmp[vi->smp];
}
}
}
adjust_voice_end(vi, xxs);
#endif
lps = xxs->lps;
lpe = xxs->lpe;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += lpe - lps;
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
}
int rampsize = s->ticksize >> ANTICLICK_SHIFT;
int delta_l = (vol_l - vi->old_vl) / rampsize;
int delta_r = (vol_r - vi->old_vr) / rampsize;
usmp = 0;
for (size = s->ticksize; size > 0; ) {
int split_noloop = 0;
if (p->xc_data[vi->chn].split) {
split_noloop = 1;
}
/* How many samples we can write before the loop break
* or sample end... */
if (vi->pos >= vi->end) {
samples = 0;
usmp = 1;
} else {
int s = ceil(((double)vi->end - vi->pos) / step);
/* ...inside the tick boundaries */
if (s > size) {
s = size;
}
samples = s;
if (samples > 0) {
usmp = 0;
}
}
if (vi->vol) {
int mix_size = samples;
int mixer = vi->fidx & FIDX_FLAGMASK;
if (~s->format & XMP_FORMAT_MONO) {
mix_size *= 2;
}
/* For Hipolito's anticlick routine */
if (samples > 0) {
if (~s->format & XMP_FORMAT_MONO) {
prev_r = buf_pos[mix_size - 2];
}
prev_l = buf_pos[mix_size - 1];
} else {
prev_r = prev_l = 0;
}
#ifndef LIBXMP_CORE_DISABLE_IT
/* See OpenMPT env-flt-max.it */
if (vi->filter.cutoff >= 0xfe &&
vi->filter.resonance == 0) {
mixer &= ~FLAG_FILTER;
}
#endif
mix_fn = (*mixers)[mixer];
/* Call the output handler */
if (samples > 0 && vi->sptr != NULL) {
int rsize = 0;
if (rampsize > samples) {
rampsize -= samples;
} else {
rsize = samples - rampsize;
rampsize = 0;
}
if (delta_l == 0 && delta_r == 0) {
/* no need to ramp */
rsize = samples;
}
if (mix_fn != NULL) {
mix_fn(vi, buf_pos, samples,
vol_l >> 8, vol_r >> 8, step * (1 << SMIX_SHIFT), rsize, delta_l, delta_r);
}
buf_pos += mix_size;
vi->old_vl += samples * delta_l;
vi->old_vr += samples * delta_r;
/* For Hipolito's anticlick routine */
if (~s->format & XMP_FORMAT_MONO) {
vi->sright = buf_pos[-2] - prev_r;
}
vi->sleft = buf_pos[-1] - prev_l;
}
}
vi->pos += step * samples;
/* No more samples in this tick */
size -= samples + usmp;
if (size <= 0) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
if (vi->pos + step > vi->end) {
vi->pos += step;
loop_reposition(ctx, vi, xxs);
}
}
continue;
}
/* First sample loop run */
if ((~xxs->flg & XMP_SAMPLE_LOOP) || split_noloop) {
do_anticlick(ctx, voc, buf_pos, size);
set_sample_end(ctx, voc, 1);
size = 0;
continue;
}
loop_reposition(ctx, vi, xxs);
}
vi->old_vl = vol_l;
vi->old_vr = vol_r;
}
/* Render final frame */
size = s->ticksize;
if (~s->format & XMP_FORMAT_MONO) {
size *= 2;
}
if (size > XMP_MAX_FRAMESIZE) {
size = XMP_MAX_FRAMESIZE;
}
if (s->format & XMP_FORMAT_8BIT) {
downmix_int_8bit(s->buffer, s->buf32, size, s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x80 : 0);
} else {
downmix_int_16bit((int16 *)s->buffer, s->buf32, size,s->amplify,
s->format & XMP_FORMAT_UNSIGNED ? 0x8000 : 0);
}
s->dtright = s->dtleft = 0;
}
void libxmp_mixer_voicepos(struct context_data *ctx, int voc, double pos, int ac)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
int lps;
if (vi->smp < m->mod.smp) {
xxs = &m->mod.xxs[vi->smp];
} else {
xxs = &ctx->smix.xxs[vi->smp - m->mod.smp];
}
if (xxs->flg & XMP_SAMPLE_SYNTH) {
return;
}
vi->pos = pos;
adjust_voice_end(vi, xxs);
if (vi->pos >= vi->end) {
if (xxs->flg & XMP_SAMPLE_LOOP) {
vi->pos = xxs->lps;
} else {
vi->pos = xxs->len;
}
}
lps = xxs->lps;
if (p->flags & XMP_FLAGS_FIXLOOP) {
lps >>= 1;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
vi->end += (xxs->lpe - lps);
#ifndef LIBXMP_CORE_DISABLE_IT
if (IS_PLAYER_MODE_IT()) {
vi->end--;
}
#endif
}
if (ac) {
anticlick(vi);
}
}
double libxmp_mixer_getvoicepos(struct context_data *ctx, int voc)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
xxs = libxmp_get_sample(ctx, vi->smp);
if (xxs->flg & XMP_SAMPLE_SYNTH) {
return 0;
}
if (xxs->flg & XMP_SAMPLE_LOOP_BIDIR) {
if (vi->pos >= xxs->lpe) {
return xxs->lpe - (vi->pos - xxs->lpe) - 1;
}
}
return vi->pos;
}
void libxmp_mixer_setpatch(struct context_data *ctx, int voc, int smp, int ac)
{
struct player_data *p = &ctx->p;
#ifndef LIBXMP_CORE_DISABLE_IT
struct module_data *m = &ctx->m;
#endif
struct mixer_data *s = &ctx->s;
struct mixer_voice *vi = &p->virt.voice_array[voc];
struct xmp_sample *xxs;
xxs = libxmp_get_sample(ctx, smp);
vi->smp = smp;
vi->vol = 0;
vi->pan = 0;
vi->flags &= ~SAMPLE_LOOP;
vi->fidx = 0;
if (~s->format & XMP_FORMAT_MONO) {
vi->fidx |= FLAG_STEREO;
}
set_sample_end(ctx, voc, 0);
/*mixer_setvol(ctx, voc, 0);*/
vi->sptr = xxs->data;
vi->fidx |= FLAG_ACTIVE;
#ifndef LIBXMP_CORE_DISABLE_IT
if (HAS_QUIRK(QUIRK_FILTER) && s->dsp & XMP_DSP_LOWPASS) {
vi->fidx |= FLAG_FILTER;
}
#endif
if (xxs->flg & XMP_SAMPLE_16BIT) {
vi->fidx |= FLAG_16_BITS;
}
libxmp_mixer_voicepos(ctx, voc, 0, ac);
}
void libxmp_mixer_setnote(struct context_data *ctx, int voc, int note)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
/* FIXME: Workaround for crash on notes that are too high
* see 6nations.it (+114 transposition on instrument 16)
*/
if (note > 149) {
note = 149;
}
vi->note = note;
vi->period = libxmp_note_to_period_mix(note, 0);
anticlick(vi);
}
void libxmp_mixer_setperiod(struct context_data *ctx, int voc, double period)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
vi->period = period;
}
void libxmp_mixer_setvol(struct context_data *ctx, int voc, int vol)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
if (vol == 0) {
anticlick(vi);
}
vi->vol = vol;
}
void libxmp_mixer_release(struct context_data *ctx, int voc, int rel)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
if (rel) {
vi->flags |= VOICE_RELEASE;
} else {
vi->flags &= ~VOICE_RELEASE;
}
}
void libxmp_mixer_seteffect(struct context_data *ctx, int voc, int type, int val)
{
#ifndef LIBXMP_CORE_DISABLE_IT
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
switch (type) {
case DSP_EFFECT_CUTOFF:
vi->filter.cutoff = val;
break;
case DSP_EFFECT_RESONANCE:
vi->filter.resonance = val;
break;
case DSP_EFFECT_FILTER_A0:
vi->filter.a0 = val;
break;
case DSP_EFFECT_FILTER_B0:
vi->filter.b0 = val;
break;
case DSP_EFFECT_FILTER_B1:
vi->filter.b1 = val;
break;
}
#endif
}
void libxmp_mixer_setpan(struct context_data *ctx, int voc, int pan)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
vi->pan = pan;
}
int libxmp_mixer_numvoices(struct context_data *ctx, int num)
{
struct mixer_data *s = &ctx->s;
if (num > s->numvoc || num < 0) {
return s->numvoc;
} else {
return num;
}
}
int libxmp_mixer_on(struct context_data *ctx, int rate, int format, int c4rate)
{
struct mixer_data *s = &ctx->s;
s->buffer = calloc(2, XMP_MAX_FRAMESIZE);
if (s->buffer == NULL)
goto err;
s->buf32 = calloc(sizeof(int), XMP_MAX_FRAMESIZE);
if (s->buf32 == NULL)
goto err1;
s->freq = rate;
s->format = format;
s->amplify = DEFAULT_AMPLIFY;
s->mix = DEFAULT_MIX;
/* s->pbase = C4_PERIOD * c4rate / s->freq; */
s->interp = XMP_INTERP_LINEAR; /* default interpolation type */
s->dsp = XMP_DSP_LOWPASS; /* enable filters by default */
/* s->numvoc = SMIX_NUMVOC; */
s->dtright = s->dtleft = 0;
return 0;
err1:
free(s->buffer);
err:
return -1;
}
void libxmp_mixer_off(struct context_data *ctx)
{
struct mixer_data *s = &ctx->s;
free(s->buffer);
free(s->buf32);
s->buf32 = NULL;
s->buffer = NULL;
}

View file

@ -0,0 +1,78 @@
#ifndef LIBXMP_MIXER_H
#define LIBXMP_MIXER_H
#define C4_PERIOD 428.0
#define SMIX_NUMVOC 128 /* default number of softmixer voices */
#define SMIX_SHIFT 16
#define SMIX_MASK 0xffff
#define FILTER_SHIFT 16
#define ANTICLICK_SHIFT 3
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define MIXER(f) void libxmp_mix_##f(struct mixer_voice *vi, int *buffer, \
int count, int vl, int vr, int step, int ramp, int delta_l, int delta_r)
struct mixer_voice {
int chn; /* channel number */
int root; /* */
int note; /* */
#define PAN_SURROUND 0x8000
int pan; /* */
int vol; /* */
double period; /* current period */
double pos; /* position in sample */
int pos0; /* position in sample before mixing */
int fidx; /* mixer function index */
int ins; /* instrument number */
int smp; /* sample number */
int end; /* loop end */
int act; /* nna info & status of voice */
int old_vl; /* previous volume, left channel */
int old_vr; /* previous volume, right channel */
int sleft; /* last left sample output, in 32bit */
int sright; /* last right sample output, in 32bit */
#define VOICE_RELEASE (1 << 0)
#define ANTICLICK (1 << 1)
#define SAMPLE_LOOP (1 << 2)
int flags; /* flags */
void *sptr; /* sample pointer */
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula; /* paula simulation state */
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
int r1; /* filter variables */
int r2;
int l1;
int l2;
int a0;
int b0;
int b1;
int cutoff;
int resonance;
} filter;
#endif
};
int libxmp_mixer_on (struct context_data *, int, int, int);
void libxmp_mixer_off (struct context_data *);
void libxmp_mixer_setvol (struct context_data *, int, int);
void libxmp_mixer_seteffect (struct context_data *, int, int, int);
void libxmp_mixer_setpan (struct context_data *, int, int);
int libxmp_mixer_numvoices (struct context_data *, int);
void libxmp_mixer_softmixer (struct context_data *);
void libxmp_mixer_reset (struct context_data *);
void libxmp_mixer_setpatch (struct context_data *, int, int, int);
void libxmp_mixer_voicepos (struct context_data *, int, double, int);
double libxmp_mixer_getvoicepos(struct context_data *, int);
void libxmp_mixer_setnote (struct context_data *, int, int);
void libxmp_mixer_setperiod (struct context_data *, int, double);
void libxmp_mixer_release (struct context_data *, int, int);
#endif /* LIBXMP_MIXER_H */

View file

@ -0,0 +1,264 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "common.h"
#include "period.h"
#include <math.h>
#ifndef M_LN2
#define M_LN2 0.693147180559945309417
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/*
* Period table from the Protracker V2.1A play routine
*/
static uint16 pt_period_table[16][36] = {
/* Tuning 0, Normal */
{
856,808,762,720,678,640,604,570,538,508,480,453,
428,404,381,360,339,320,302,285,269,254,240,226,
214,202,190,180,170,160,151,143,135,127,120,113
},
/* Tuning 1 */
{
850,802,757,715,674,637,601,567,535,505,477,450,
425,401,379,357,337,318,300,284,268,253,239,225,
213,201,189,179,169,159,150,142,134,126,119,113
},
/* Tuning 2 */
{
844,796,752,709,670,632,597,563,532,502,474,447,
422,398,376,355,335,316,298,282,266,251,237,224,
211,199,188,177,167,158,149,141,133,125,118,112
},
/* Tuning 3 */
{
838,791,746,704,665,628,592,559,528,498,470,444,
419,395,373,352,332,314,296,280,264,249,235,222,
209,198,187,176,166,157,148,140,132,125,118,111
},
/* Tuning 4 */
{
832,785,741,699,660,623,588,555,524,495,467,441,
416,392,370,350,330,312,294,278,262,247,233,220,
208,196,185,175,165,156,147,139,131,124,117,110
},
/* Tuning 5 */
{
826,779,736,694,655,619,584,551,520,491,463,437,
413,390,368,347,328,309,292,276,260,245,232,219,
206,195,184,174,164,155,146,138,130,123,116,109
},
/* Tuning 6 */
{
820,774,730,689,651,614,580,547,516,487,460,434,
410,387,365,345,325,307,290,274,258,244,230,217,
205,193,183,172,163,154,145,137,129,122,115,109
},
/* Tuning 7 */
{
814,768,725,684,646,610,575,543,513,484,457,431,
407,384,363,342,323,305,288,272,256,242,228,216,
204,192,181,171,161,152,144,136,128,121,114,108
},
/* Tuning -8 */
{
907,856,808,762,720,678,640,604,570,538,508,480,
453,428,404,381,360,339,320,302,285,269,254,240,
226,214,202,190,180,170,160,151,143,135,127,120
},
/* Tuning -7 */
{
900,850,802,757,715,675,636,601,567,535,505,477,
450,425,401,379,357,337,318,300,284,268,253,238,
225,212,200,189,179,169,159,150,142,134,126,119
},
/* Tuning -6 */
{
894,844,796,752,709,670,632,597,563,532,502,474,
447,422,398,376,355,335,316,298,282,266,251,237,
223,211,199,188,177,167,158,149,141,133,125,118
},
/* Tuning -5 */
{
887,838,791,746,704,665,628,592,559,528,498,470,
444,419,395,373,352,332,314,296,280,264,249,235,
222,209,198,187,176,166,157,148,140,132,125,118
},
/* Tuning -4 */
{
881,832,785,741,699,660,623,588,555,524,494,467,
441,416,392,370,350,330,312,294,278,262,247,233,
220,208,196,185,175,165,156,147,139,131,123,117
},
/* Tuning -3 */
{
875,826,779,736,694,655,619,584,551,520,491,463,
437,413,390,368,347,328,309,292,276,260,245,232,
219,206,195,184,174,164,155,146,138,130,123,116
},
/* Tuning -2 */
{
868,820,774,730,689,651,614,580,547,516,487,460,
434,410,387,365,345,325,307,290,274,258,244,230,
217,205,193,183,172,163,154,145,137,129,122,115
},
/* Tuning -1 */
{
862,814,768,725,684,646,610,575,543,513,484,457,
431,407,384,363,342,323,305,288,272,256,242,228,
216,203,192,181,171,161,152,144,136,128,121,114
}
};
#endif
#ifdef _MSC_VER
static inline double round(double val)
{
return floor(val + 0.5);
}
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/* Get period from note using Protracker tuning */
static inline int libxmp_note_to_period_pt(int n, int f)
{
if (n < MIN_NOTE_MOD || n > MAX_NOTE_MOD) {
return -1;
}
n -= 48;
f >>= 4;
if (f < -8 || f > 7) {
return 0;
}
if (f < 0) {
f += 16;
}
return (int)pt_period_table[f][n];
}
#endif
/* Get period from note */
double libxmp_note_to_period(struct context_data *ctx, int n, int f, double adj)
{
double d, per;
struct module_data *m = &ctx->m;
#ifdef LIBXMP_PAULA_SIMULATOR
struct player_data *p = &ctx->p;
/* If mod replayer, modrng and Amiga mixing are active */
if (p->flags & XMP_FLAGS_A500) {
if (IS_AMIGA_MOD()) {
return libxmp_note_to_period_pt(n, f);
}
}
#endif
d = (double)n + (double)f / 128;
switch (m->period_type) {
case PERIOD_LINEAR:
per = (240.0 - d) * 16; /* Linear */
break;
case PERIOD_CSPD:
per = 8363.0 * pow(2, n / 12) / 32 + f; /* Hz */
break;
default:
per = PERIOD_BASE / pow(2, d / 12); /* Amiga */
}
#ifndef LIBXMP_CORE_PLAYER
if (adj > 0.1) {
per *= adj;
}
#endif
return per;
}
/* For the software mixer */
double libxmp_note_to_period_mix(int n, int b)
{
double d = (double)n + (double)b / 12800;
return PERIOD_BASE / pow(2, d / 12);
}
/* Get note from period */
/* This function is used only by the MOD loader */
int libxmp_period_to_note(int p)
{
if (p <= 0) {
return 0;
}
return round(12.0 * log(PERIOD_BASE / p) / M_LN2) + 1;
}
/* Get pitchbend from base note and amiga period */
int libxmp_period_to_bend(struct context_data *ctx, double p, int n, double adj)
{
struct module_data *m = &ctx->m;
double d;
if (n == 0) {
return 0;
}
switch (m->period_type) {
case PERIOD_LINEAR:
return 100 * (8 * (((240 - n) << 4) - p));
case PERIOD_CSPD:
d = libxmp_note_to_period(ctx, n, 0, adj);
return round(100.0 * (1536.0 / M_LN2) * log(p / d));
default:
/* Amiga */
d = libxmp_note_to_period(ctx, n, 0, adj);
return round(100.0 * (1536.0 / M_LN2) * log(d / p));
}
}
/* Convert finetune = 1200 * log2(C2SPD/8363))
*
* c = (1200.0 * log(c2spd) - 1200.0 * log(c4_rate)) / M_LN2;
* xpo = c/100;
* fin = 128 * (c%100) / 100;
*/
void libxmp_c2spd_to_note(int c2spd, int *n, int *f)
{
int c;
if (c2spd == 0) {
*n = *f = 0;
return;
}
c = (int)(1536.0 * log((double)c2spd / 8363) / M_LN2);
*n = c / 128;
*f = c % 128;
}

View file

@ -0,0 +1,24 @@
#ifndef LIBXMP_PERIOD_H
#define LIBXMP_PERIOD_H
#define PERIOD_BASE 13696.0 /* C0 period */
/* Macros for period conversion */
#define NOTE_B0 11
#define NOTE_Bb0 (NOTE_B0 + 1)
#define MAX_NOTE (NOTE_B0 * 8)
#define MAX_PERIOD 0x1c56
#define MIN_PERIOD_A 0x0071
#define MAX_PERIOD_A 0x0358
#define MIN_PERIOD_L 0x0000
#define MAX_PERIOD_L 0x1e00
#define MIN_NOTE_MOD 48
#define MAX_NOTE_MOD 83
double libxmp_note_to_period (struct context_data *, int, int, double);
double libxmp_note_to_period_mix (int, int);
int libxmp_period_to_note (int);
int libxmp_period_to_bend (struct context_data *, double, int, double);
void libxmp_c2spd_to_note (int, int *, int *);
#endif /* LIBXMP_PERIOD_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,258 @@
#ifndef LIBXMP_PLAYER_H
#define LIBXMP_PLAYER_H
#include "lfo.h"
/* Quirk control */
#define HAS_QUIRK(x) (m->quirk & (x))
/* Channel flag control */
#define SET(f) SET_FLAG(xc->flags,(f))
#define RESET(f) RESET_FLAG(xc->flags,(f))
#define TEST(f) TEST_FLAG(xc->flags,(f))
/* Persistent effect flag control */
#define SET_PER(f) SET_FLAG(xc->per_flags,(f))
#define RESET_PER(f) RESET_FLAG(xc->per_flags,(f))
#define TEST_PER(f) TEST_FLAG(xc->per_flags,(f))
/* Note flag control */
#define SET_NOTE(f) SET_FLAG(xc->note_flags,(f))
#define RESET_NOTE(f) RESET_FLAG(xc->note_flags,(f))
#define TEST_NOTE(f) TEST_FLAG(xc->note_flags,(f))
struct retrig_control {
int s;
int m;
int d;
};
/* The following macros are used to set the flags for each channel */
#define VOL_SLIDE (1 << 0)
#define PAN_SLIDE (1 << 1)
#define TONEPORTA (1 << 2)
#define PITCHBEND (1 << 3)
#define VIBRATO (1 << 4)
#define TREMOLO (1 << 5)
#define FINE_VOLS (1 << 6)
#define FINE_BEND (1 << 7)
#define OFFSET (1 << 8)
#define TRK_VSLIDE (1 << 9)
#define TRK_FVSLIDE (1 << 10)
#define NEW_INS (1 << 11)
#define NEW_VOL (1 << 12)
#define VOL_SLIDE_2 (1 << 13)
#define NOTE_SLIDE (1 << 14)
#define FINE_NSLIDE (1 << 15)
#define NEW_NOTE (1 << 16)
#define FINE_TPORTA (1 << 17)
#define RETRIG (1 << 18)
#define PANBRELLO (1 << 19)
#define GVOL_SLIDE (1 << 20)
#define TEMPO_SLIDE (1 << 21)
#define VENV_PAUSE (1 << 22)
#define PENV_PAUSE (1 << 23)
#define FENV_PAUSE (1 << 24)
#define FINE_VOLS_2 (1 << 25)
#define KEY_OFF (1 << 26) /* for IT release on envloop end */
#define TREMOR (1 << 27) /* for XM tremor */
#define NOTE_FADEOUT (1 << 0)
#define NOTE_RELEASE (1 << 1)
#define NOTE_END (1 << 2)
#define NOTE_CUT (1 << 3)
#define NOTE_ENV_END (1 << 4)
#define NOTE_SAMPLE_END (1 << 5)
#define NOTE_SET (1 << 6) /* for IT portamento after keyoff */
#define NOTE_SUSEXIT (1 << 7) /* for delayed note release */
#define NOTE_KEY_CUT (1 << 8) /* note cut with XMP_KEY_CUT event */
#define NOTE_GLISSANDO (1 << 9)
#define IS_VALID_INSTRUMENT(x) ((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0)
#define IS_VALID_INSTRUMENT_OR_SFX(x) (((uint32)(x) < mod->ins && mod->xxi[(x)].nsm > 0) || (smix->ins > 0 && (uint32)(x) < mod->ins + smix->ins))
struct instrument_vibrato {
int phase;
int sweep;
};
struct channel_data {
int flags; /* Channel flags */
int per_flags; /* Persistent effect channel flags */
int note_flags; /* Note release, fadeout or end */
int note; /* Note number */
int key; /* Key number */
double period; /* Amiga or linear period */
double per_adj; /* MED period/pitch adjustment factor hack */
int finetune; /* Guess what */
int ins; /* Instrument number */
int old_ins; /* Last instruemnt */
int smp; /* Sample number */
int mastervol; /* Master vol -- for IT track vol effect */
int delay; /* Note delay in frames */
int keyoff; /* Key off counter */
int fadeout; /* Current fadeout (release) value */
int ins_fade; /* Instrument fadeout value */
int volume; /* Current volume */
int gvl; /* Global volume for instrument for IT */
int rvv; /* Random volume variation */
int rpv; /* Random pan variation */
uint8 split; /* Split channel */
uint8 pair; /* Split channel pair */
int v_idx; /* Volume envelope index */
int p_idx; /* Pan envelope index */
int f_idx; /* Freq envelope index */
int key_porta; /* Key number for portamento target
* -- needed to handle IT portamento xpo */
struct {
struct lfo lfo;
int memory;
} vibrato;
struct {
struct lfo lfo;
int memory;
} tremolo;
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
struct lfo lfo;
int memory;
} panbrello;
#endif
struct {
int8 val[16]; /* 16 for Smaksak MegaArps */
int size;
int count;
int memory;
} arpeggio;
struct {
struct lfo lfo;
int sweep;
} insvib;
struct {
int val;
int val2; /* For fx9 bug emulation */
int memory;
} offset;
struct {
int val; /* Retrig value */
int count; /* Retrig counter */
int type; /* Retrig type */
} retrig;
struct {
uint8 up,down; /* Tremor value */
uint8 count; /* Tremor counter */
uint8 memory; /* Tremor memory */
} tremor;
struct {
int slide; /* Volume slide value */
int fslide; /* Fine volume slide value */
int slide2; /* Volume slide value */
int memory; /* Volume slide effect memory */
#ifndef LIBXMP_CORE_DISABLE_IT
int fslide2;
int memory2; /* Volume slide effect memory */
#endif
} vol;
struct {
int up_memory; /* Fine volume slide up memory (XM) */
int down_memory;/* Fine volume slide up memory (XM) */
} fine_vol;
struct {
int slide; /* Global volume slide value */
int fslide; /* Fine global volume slide value */
int memory; /* Global volume memory is saved per channel */
} gvol;
struct {
int slide; /* Track volume slide value */
int fslide; /* Track fine volume slide value */
int memory; /* Track volume slide effect memory */
} trackvol;
struct {
int slide; /* Frequency slide value */
double fslide; /* Fine frequency slide value */
int memory; /* Portamento effect memory */
} freq;
struct {
double target; /* Target period for tone portamento */
int dir; /* Tone portamento up/down directionh */
int slide; /* Delta for tone portamento */
int memory; /* Tone portamento effect memory */
} porta;
struct {
int up_memory; /* FT2 has separate memories for these */
int down_memory;/* cases (see Porta-LinkMem.xm) */
} fine_porta;
struct {
int val; /* Current pan value */
int slide; /* Pan slide value */
int fslide; /* Pan fine slide value */
int memory; /* Pan slide effect memory */
int surround; /* Surround channel flag */
} pan;
struct {
int speed;
int count;
int pos;
} invloop;
#ifndef LIBXMP_CORE_DISABLE_IT
struct {
int slide; /* IT tempo slide */
} tempo;
struct {
int cutoff; /* IT filter cutoff frequency */
int resonance; /* IT filter resonance */
int envelope; /* IT filter envelope */
} filter;
#endif
#ifndef LIBXMP_CORE_PLAYER
struct {
int slide; /* PTM note slide amount */
int fslide; /* OKT fine note slide amount */
int speed; /* PTM note slide speed */
int count; /* PTM note slide counter */
} noteslide;
void *extra;
#endif
struct xmp_event delayed_event;
int delayed_ins; /* IT save instrument emulation */
int info_period; /* Period */
int info_pitchbend; /* Linear pitchbend */
int info_position; /* Position before mixing */
int info_finalvol; /* Final volume including envelopes */
int info_finalpan; /* Final pan including envelopes */
};
void libxmp_process_fx (struct context_data *, struct channel_data *,
int, struct xmp_event *, int);
void libxmp_filter_setup (int, int, int, int*, int*, int *);
int libxmp_read_event (struct context_data *, struct xmp_event *, int);
#endif /* LIBXMP_PLAYER_H */

View file

@ -0,0 +1,524 @@
static int16 cubic_spline_lut0[1024] = {
0, -8, -16, -24, -32, -40, -47, -55,
-63, -71, -78, -86, -94, -101, -109, -117,
-124, -132, -139, -146, -154, -161, -169, -176,
-183, -190, -198, -205, -212, -219, -226, -233,
-240, -247, -254, -261, -268, -275, -282, -289,
-295, -302, -309, -316, -322, -329, -336, -342,
-349, -355, -362, -368, -375, -381, -388, -394,
-400, -407, -413, -419, -425, -432, -438, -444,
-450, -456, -462, -468, -474, -480, -486, -492,
-498, -504, -510, -515, -521, -527, -533, -538,
-544, -550, -555, -561, -566, -572, -577, -583,
-588, -594, -599, -604, -610, -615, -620, -626,
-631, -636, -641, -646, -651, -656, -662, -667,
-672, -677, -682, -686, -691, -696, -701, -706,
-711, -715, -720, -725, -730, -734, -739, -744,
-748, -753, -757, -762, -766, -771, -775, -780,
-784, -788, -793, -797, -801, -806, -810, -814,
-818, -822, -826, -831, -835, -839, -843, -847,
-851, -855, -859, -863, -866, -870, -874, -878,
-882, -886, -889, -893, -897, -900, -904, -908,
-911, -915, -918, -922, -925, -929, -932, -936,
-939, -943, -946, -949, -953, -956, -959, -962,
-966, -969, -972, -975, -978, -981, -984, -987,
-991, -994, -997, -999, -1002, -1005, -1008, -1011,
-1014, -1017, -1020, -1022, -1025, -1028, -1031, -1033,
-1036, -1039, -1041, -1044, -1047, -1049, -1052, -1054,
-1057, -1059, -1062, -1064, -1066, -1069, -1071, -1074,
-1076, -1078, -1080, -1083, -1085, -1087, -1089, -1092,
-1094, -1096, -1098, -1100, -1102, -1104, -1106, -1108,
-1110, -1112, -1114, -1116, -1118, -1120, -1122, -1124,
-1125, -1127, -1129, -1131, -1133, -1134, -1136, -1138,
-1139, -1141, -1143, -1144, -1146, -1147, -1149, -1150,
-1152, -1153, -1155, -1156, -1158, -1159, -1161, -1162,
-1163, -1165, -1166, -1167, -1169, -1170, -1171, -1172,
-1174, -1175, -1176, -1177, -1178, -1179, -1180, -1181,
-1182, -1184, -1185, -1186, -1187, -1187, -1188, -1189,
-1190, -1191, -1192, -1193, -1194, -1195, -1195, -1196,
-1197, -1198, -1198, -1199, -1200, -1200, -1201, -1202,
-1202, -1203, -1204, -1204, -1205, -1205, -1206, -1206,
-1207, -1207, -1208, -1208, -1208, -1209, -1209, -1210,
-1210, -1210, -1211, -1211, -1211, -1212, -1212, -1212,
-1212, -1212, -1213, -1213, -1213, -1213, -1213, -1213,
-1213, -1213, -1214, -1214, -1214, -1214, -1214, -1214,
-1214, -1214, -1213, -1213, -1213, -1213, -1213, -1213,
-1213, -1213, -1212, -1212, -1212, -1212, -1211, -1211,
-1211, -1211, -1210, -1210, -1210, -1209, -1209, -1209,
-1208, -1208, -1207, -1207, -1207, -1206, -1206, -1205,
-1205, -1204, -1204, -1203, -1202, -1202, -1201, -1201,
-1200, -1199, -1199, -1198, -1197, -1197, -1196, -1195,
-1195, -1194, -1193, -1192, -1192, -1191, -1190, -1189,
-1188, -1187, -1187, -1186, -1185, -1184, -1183, -1182,
-1181, -1180, -1179, -1178, -1177, -1176, -1175, -1174,
-1173, -1172, -1171, -1170, -1169, -1168, -1167, -1166,
-1165, -1163, -1162, -1161, -1160, -1159, -1158, -1156,
-1155, -1154, -1153, -1151, -1150, -1149, -1148, -1146,
-1145, -1144, -1142, -1141, -1140, -1138, -1137, -1135,
-1134, -1133, -1131, -1130, -1128, -1127, -1125, -1124,
-1122, -1121, -1119, -1118, -1116, -1115, -1113, -1112,
-1110, -1109, -1107, -1105, -1104, -1102, -1101, -1099,
-1097, -1096, -1094, -1092, -1091, -1089, -1087, -1085,
-1084, -1082, -1080, -1079, -1077, -1075, -1073, -1071,
-1070, -1068, -1066, -1064, -1062, -1061, -1059, -1057,
-1055, -1053, -1051, -1049, -1047, -1046, -1044, -1042,
-1040, -1038, -1036, -1034, -1032, -1030, -1028, -1026,
-1024, -1022, -1020, -1018, -1016, -1014, -1012, -1010,
-1008, -1006, -1004, -1002, -999, -997, -995, -993,
-991, -989, -987, -985, -982, -980, -978, -976,
-974, -972, -969, -967, -965, -963, -961, -958,
-956, -954, -952, -950, -947, -945, -943, -941,
-938, -936, -934, -931, -929, -927, -924, -922,
-920, -918, -915, -913, -911, -908, -906, -903,
-901, -899, -896, -894, -892, -889, -887, -884,
-882, -880, -877, -875, -872, -870, -867, -865,
-863, -860, -858, -855, -853, -850, -848, -845,
-843, -840, -838, -835, -833, -830, -828, -825,
-823, -820, -818, -815, -813, -810, -808, -805,
-803, -800, -798, -795, -793, -790, -787, -785,
-782, -780, -777, -775, -772, -769, -767, -764,
-762, -759, -757, -754, -751, -749, -746, -744,
-741, -738, -736, -733, -730, -728, -725, -723,
-720, -717, -715, -712, -709, -707, -704, -702,
-699, -696, -694, -691, -688, -686, -683, -680,
-678, -675, -672, -670, -667, -665, -662, -659,
-657, -654, -651, -649, -646, -643, -641, -638,
-635, -633, -630, -627, -625, -622, -619, -617,
-614, -611, -609, -606, -603, -601, -598, -595,
-593, -590, -587, -585, -582, -579, -577, -574,
-571, -569, -566, -563, -561, -558, -555, -553,
-550, -547, -545, -542, -539, -537, -534, -531,
-529, -526, -523, -521, -518, -516, -513, -510,
-508, -505, -502, -500, -497, -495, -492, -489,
-487, -484, -481, -479, -476, -474, -471, -468,
-466, -463, -461, -458, -455, -453, -450, -448,
-445, -442, -440, -437, -435, -432, -430, -427,
-424, -422, -419, -417, -414, -412, -409, -407,
-404, -402, -399, -397, -394, -392, -389, -387,
-384, -382, -379, -377, -374, -372, -369, -367,
-364, -362, -359, -357, -354, -352, -349, -347,
-345, -342, -340, -337, -335, -332, -330, -328,
-325, -323, -320, -318, -316, -313, -311, -309,
-306, -304, -302, -299, -297, -295, -292, -290,
-288, -285, -283, -281, -278, -276, -274, -272,
-269, -267, -265, -263, -260, -258, -256, -254,
-251, -249, -247, -245, -243, -240, -238, -236,
-234, -232, -230, -228, -225, -223, -221, -219,
-217, -215, -213, -211, -209, -207, -205, -202,
-200, -198, -196, -194, -192, -190, -188, -186,
-184, -182, -180, -178, -176, -175, -173, -171,
-169, -167, -165, -163, -161, -159, -157, -156,
-154, -152, -150, -148, -146, -145, -143, -141,
-139, -137, -136, -134, -132, -130, -129, -127,
-125, -124, -122, -120, -119, -117, -115, -114,
-112, -110, -109, -107, -106, -104, -102, -101,
-99, -98, -96, -95, -93, -92, -90, -89,
-87, -86, -84, -83, -82, -80, -79, -77,
-76, -75, -73, -72, -70, -69, -68, -67,
-65, -64, -63, -61, -60, -59, -58, -57,
-55, -54, -53, -52, -51, -49, -48, -47,
-46, -45, -44, -43, -42, -41, -40, -39,
-38, -37, -36, -35, -34, -33, -32, -31,
-30, -29, -28, -27, -26, -26, -25, -24,
-23, -22, -22, -21, -20, -19, -19, -18,
-17, -16, -16, -15, -14, -14, -13, -13,
-12, -11, -11, -10, -10, -9, -9, -8,
-8, -7, -7, -6, -6, -6, -5, -5,
-4, -4, -4, -3, -3, -3, -2, -2,
-2, -2, -2, -1, -1, -1, -1, -1,
0, 0, 0, 0, 0, 0, 0, 0,
};
static int16 cubic_spline_lut1[1024] = {
16384, 16384, 16384, 16384, 16384, 16383, 16382, 16381,
16381, 16381, 16380, 16379, 16379, 16377, 16377, 16376,
16374, 16373, 16371, 16370, 16369, 16366, 16366, 16364,
16361, 16360, 16358, 16357, 16354, 16351, 16349, 16347,
16345, 16342, 16340, 16337, 16335, 16331, 16329, 16326,
16322, 16320, 16317, 16314, 16309, 16307, 16304, 16299,
16297, 16293, 16290, 16285, 16282, 16278, 16274, 16269,
16265, 16262, 16257, 16253, 16247, 16244, 16239, 16235,
16230, 16225, 16220, 16216, 16211, 16206, 16201, 16196,
16191, 16185, 16180, 16174, 16169, 16163, 16158, 16151,
16146, 16140, 16133, 16128, 16122, 16116, 16109, 16104,
16097, 16092, 16085, 16077, 16071, 16064, 16058, 16052,
16044, 16038, 16030, 16023, 16015, 16009, 16002, 15995,
15988, 15980, 15973, 15964, 15957, 15949, 15941, 15934,
15926, 15918, 15910, 15903, 15894, 15886, 15877, 15870,
15861, 15853, 15843, 15836, 15827, 15818, 15810, 15801,
15792, 15783, 15774, 15765, 15756, 15747, 15738, 15729,
15719, 15709, 15700, 15691, 15681, 15672, 15662, 15652,
15642, 15633, 15623, 15613, 15602, 15592, 15582, 15572,
15562, 15552, 15540, 15530, 15520, 15509, 15499, 15489,
15478, 15467, 15456, 15446, 15433, 15423, 15412, 15401,
15390, 15379, 15367, 15356, 15345, 15333, 15321, 15310,
15299, 15287, 15276, 15264, 15252, 15240, 15228, 15216,
15205, 15192, 15180, 15167, 15155, 15143, 15131, 15118,
15106, 15094, 15081, 15067, 15056, 15043, 15031, 15017,
15004, 14992, 14979, 14966, 14953, 14940, 14927, 14913,
14900, 14887, 14874, 14860, 14846, 14833, 14819, 14806,
14793, 14778, 14764, 14752, 14737, 14723, 14709, 14696,
14681, 14668, 14653, 14638, 14625, 14610, 14595, 14582,
14567, 14553, 14538, 14523, 14509, 14494, 14480, 14465,
14450, 14435, 14420, 14406, 14391, 14376, 14361, 14346,
14330, 14316, 14301, 14285, 14270, 14254, 14239, 14223,
14208, 14192, 14177, 14161, 14146, 14130, 14115, 14099,
14082, 14067, 14051, 14035, 14019, 14003, 13986, 13971,
13955, 13939, 13923, 13906, 13890, 13873, 13857, 13840,
13823, 13808, 13791, 13775, 13758, 13741, 13724, 13707,
13691, 13673, 13657, 13641, 13623, 13607, 13589, 13572,
13556, 13538, 13521, 13504, 13486, 13469, 13451, 13435,
13417, 13399, 13383, 13365, 13347, 13330, 13312, 13294,
13277, 13258, 13241, 13224, 13205, 13188, 13170, 13152,
13134, 13116, 13098, 13080, 13062, 13044, 13026, 13008,
12989, 12971, 12953, 12934, 12916, 12898, 12879, 12860,
12842, 12823, 12806, 12787, 12768, 12750, 12731, 12712,
12694, 12675, 12655, 12637, 12618, 12599, 12580, 12562,
12542, 12524, 12504, 12485, 12466, 12448, 12427, 12408,
12390, 12370, 12351, 12332, 12312, 12293, 12273, 12254,
12235, 12215, 12195, 12176, 12157, 12137, 12118, 12097,
12079, 12059, 12039, 12019, 11998, 11980, 11960, 11940,
11920, 11900, 11880, 11860, 11839, 11821, 11801, 11780,
11761, 11741, 11720, 11700, 11680, 11660, 11640, 11619,
11599, 11578, 11559, 11538, 11518, 11498, 11477, 11457,
11436, 11415, 11394, 11374, 11354, 11333, 11313, 11292,
11272, 11251, 11231, 11209, 11189, 11168, 11148, 11127,
11107, 11084, 11064, 11043, 11023, 11002, 10982, 10959,
10939, 10918, 10898, 10876, 10856, 10834, 10814, 10792,
10772, 10750, 10728, 10708, 10687, 10666, 10644, 10623,
10602, 10581, 10560, 10538, 10517, 10496, 10474, 10453,
10431, 10410, 10389, 10368, 10346, 10325, 10303, 10283,
10260, 10239, 10217, 10196, 10175, 10152, 10132, 10110,
10088, 10068, 10045, 10023, 10002, 9981, 9959, 9936,
9915, 9893, 9872, 9851, 9829, 9806, 9784, 9763,
9742, 9720, 9698, 9676, 9653, 9633, 9611, 9589,
9567, 9545, 9523, 9501, 9479, 9458, 9436, 9414,
9392, 9370, 9348, 9326, 9304, 9282, 9260, 9238,
9216, 9194, 9172, 9150, 9128, 9106, 9084, 9062,
9040, 9018, 8996, 8974, 8951, 8929, 8907, 8885,
8863, 8841, 8819, 8797, 8775, 8752, 8730, 8708,
8686, 8664, 8642, 8620, 8597, 8575, 8553, 8531,
8509, 8487, 8464, 8442, 8420, 8398, 8376, 8353,
8331, 8309, 8287, 8265, 8242, 8220, 8198, 8176,
8154, 8131, 8109, 8087, 8065, 8042, 8020, 7998,
7976, 7954, 7931, 7909, 7887, 7865, 7842, 7820,
7798, 7776, 7754, 7731, 7709, 7687, 7665, 7643,
7620, 7598, 7576, 7554, 7531, 7509, 7487, 7465,
7443, 7421, 7398, 7376, 7354, 7332, 7310, 7288,
7265, 7243, 7221, 7199, 7177, 7155, 7132, 7110,
7088, 7066, 7044, 7022, 7000, 6978, 6956, 6934,
6911, 6889, 6867, 6845, 6823, 6801, 6779, 6757,
6735, 6713, 6691, 6669, 6647, 6625, 6603, 6581,
6559, 6537, 6515, 6493, 6472, 6450, 6428, 6406,
6384, 6362, 6340, 6318, 6297, 6275, 6253, 6231,
6209, 6188, 6166, 6144, 6122, 6101, 6079, 6057,
6035, 6014, 5992, 5970, 5949, 5927, 5905, 5884,
5862, 5841, 5819, 5797, 5776, 5754, 5733, 5711,
5690, 5668, 5647, 5625, 5604, 5582, 5561, 5540,
5518, 5497, 5476, 5454, 5433, 5412, 5390, 5369,
5348, 5327, 5305, 5284, 5263, 5242, 5221, 5199,
5178, 5157, 5136, 5115, 5094, 5073, 5052, 5031,
5010, 4989, 4968, 4947, 4926, 4905, 4885, 4864,
4843, 4822, 4801, 4780, 4760, 4739, 4718, 4698,
4677, 4656, 4636, 4615, 4595, 4574, 4553, 4533,
4512, 4492, 4471, 4451, 4431, 4410, 4390, 4370,
4349, 4329, 4309, 4288, 4268, 4248, 4228, 4208,
4188, 4167, 4147, 4127, 4107, 4087, 4067, 4047,
4027, 4007, 3988, 3968, 3948, 3928, 3908, 3889,
3869, 3849, 3829, 3810, 3790, 3771, 3751, 3732,
3712, 3693, 3673, 3654, 3634, 3615, 3595, 3576,
3557, 3538, 3518, 3499, 3480, 3461, 3442, 3423,
3404, 3385, 3366, 3347, 3328, 3309, 3290, 3271,
3252, 3233, 3215, 3196, 3177, 3159, 3140, 3121,
3103, 3084, 3066, 3047, 3029, 3010, 2992, 2974,
2955, 2937, 2919, 2901, 2882, 2864, 2846, 2828,
2810, 2792, 2774, 2756, 2738, 2720, 2702, 2685,
2667, 2649, 2631, 2614, 2596, 2579, 2561, 2543,
2526, 2509, 2491, 2474, 2456, 2439, 2422, 2405,
2387, 2370, 2353, 2336, 2319, 2302, 2285, 2268,
2251, 2234, 2218, 2201, 2184, 2167, 2151, 2134,
2117, 2101, 2084, 2068, 2052, 2035, 2019, 2003,
1986, 1970, 1954, 1938, 1922, 1906, 1890, 1874,
1858, 1842, 1826, 1810, 1794, 1779, 1763, 1747,
1732, 1716, 1701, 1685, 1670, 1654, 1639, 1624,
1608, 1593, 1578, 1563, 1548, 1533, 1518, 1503,
1488, 1473, 1458, 1444, 1429, 1414, 1400, 1385,
1370, 1356, 1342, 1327, 1313, 1298, 1284, 1270,
1256, 1242, 1228, 1214, 1200, 1186, 1172, 1158,
1144, 1131, 1117, 1103, 1090, 1076, 1063, 1049,
1036, 1022, 1009, 996, 983, 970, 956, 943,
930, 917, 905, 892, 879, 866, 854, 841,
828, 816, 803, 791, 778, 766, 754, 742,
729, 717, 705, 693, 681, 669, 658, 646,
634, 622, 611, 599, 588, 576, 565, 553,
542, 531, 520, 508, 497, 486, 475, 464,
453, 443, 432, 421, 411, 400, 389, 379,
369, 358, 348, 338, 327, 317, 307, 297,
287, 277, 268, 258, 248, 238, 229, 219,
210, 200, 191, 182, 172, 163, 154, 145,
136, 127, 118, 109, 100, 92, 83, 75,
66, 58, 49, 41, 32, 24, 16, 8,
};
static int16 cubic_spline_lut2[1024] = {
0, 8, 16, 24, 32, 41, 49, 58,
66, 75, 83, 92, 100, 109, 118, 127,
136, 145, 154, 163, 172, 182, 191, 200,
210, 219, 229, 238, 248, 258, 268, 277,
287, 297, 307, 317, 327, 338, 348, 358,
369, 379, 389, 400, 411, 421, 432, 443,
453, 464, 475, 486, 497, 508, 520, 531,
542, 553, 565, 576, 588, 599, 611, 622,
634, 646, 658, 669, 681, 693, 705, 717,
729, 742, 754, 766, 778, 791, 803, 816,
828, 841, 854, 866, 879, 892, 905, 917,
930, 943, 956, 970, 983, 996, 1009, 1022,
1036, 1049, 1063, 1076, 1090, 1103, 1117, 1131,
1144, 1158, 1172, 1186, 1200, 1214, 1228, 1242,
1256, 1270, 1284, 1298, 1313, 1327, 1342, 1356,
1370, 1385, 1400, 1414, 1429, 1444, 1458, 1473,
1488, 1503, 1518, 1533, 1548, 1563, 1578, 1593,
1608, 1624, 1639, 1654, 1670, 1685, 1701, 1716,
1732, 1747, 1763, 1779, 1794, 1810, 1826, 1842,
1858, 1874, 1890, 1906, 1922, 1938, 1954, 1970,
1986, 2003, 2019, 2035, 2052, 2068, 2084, 2101,
2117, 2134, 2151, 2167, 2184, 2201, 2218, 2234,
2251, 2268, 2285, 2302, 2319, 2336, 2353, 2370,
2387, 2405, 2422, 2439, 2456, 2474, 2491, 2509,
2526, 2543, 2561, 2579, 2596, 2614, 2631, 2649,
2667, 2685, 2702, 2720, 2738, 2756, 2774, 2792,
2810, 2828, 2846, 2864, 2882, 2901, 2919, 2937,
2955, 2974, 2992, 3010, 3029, 3047, 3066, 3084,
3103, 3121, 3140, 3159, 3177, 3196, 3215, 3233,
3252, 3271, 3290, 3309, 3328, 3347, 3366, 3385,
3404, 3423, 3442, 3461, 3480, 3499, 3518, 3538,
3557, 3576, 3595, 3615, 3634, 3654, 3673, 3693,
3712, 3732, 3751, 3771, 3790, 3810, 3829, 3849,
3869, 3889, 3908, 3928, 3948, 3968, 3988, 4007,
4027, 4047, 4067, 4087, 4107, 4127, 4147, 4167,
4188, 4208, 4228, 4248, 4268, 4288, 4309, 4329,
4349, 4370, 4390, 4410, 4431, 4451, 4471, 4492,
4512, 4533, 4553, 4574, 4595, 4615, 4636, 4656,
4677, 4698, 4718, 4739, 4760, 4780, 4801, 4822,
4843, 4864, 4885, 4905, 4926, 4947, 4968, 4989,
5010, 5031, 5052, 5073, 5094, 5115, 5136, 5157,
5178, 5199, 5221, 5242, 5263, 5284, 5305, 5327,
5348, 5369, 5390, 5412, 5433, 5454, 5476, 5497,
5518, 5540, 5561, 5582, 5604, 5625, 5647, 5668,
5690, 5711, 5733, 5754, 5776, 5797, 5819, 5841,
5862, 5884, 5905, 5927, 5949, 5970, 5992, 6014,
6035, 6057, 6079, 6101, 6122, 6144, 6166, 6188,
6209, 6231, 6253, 6275, 6297, 6318, 6340, 6362,
6384, 6406, 6428, 6450, 6472, 6493, 6515, 6537,
6559, 6581, 6603, 6625, 6647, 6669, 6691, 6713,
6735, 6757, 6779, 6801, 6823, 6845, 6867, 6889,
6911, 6934, 6956, 6978, 7000, 7022, 7044, 7066,
7088, 7110, 7132, 7155, 7177, 7199, 7221, 7243,
7265, 7288, 7310, 7332, 7354, 7376, 7398, 7421,
7443, 7465, 7487, 7509, 7531, 7554, 7576, 7598,
7620, 7643, 7665, 7687, 7709, 7731, 7754, 7776,
7798, 7820, 7842, 7865, 7887, 7909, 7931, 7954,
7976, 7998, 8020, 8042, 8065, 8087, 8109, 8131,
8154, 8176, 8198, 8220, 8242, 8265, 8287, 8309,
8331, 8353, 8376, 8398, 8420, 8442, 8464, 8487,
8509, 8531, 8553, 8575, 8597, 8620, 8642, 8664,
8686, 8708, 8730, 8752, 8775, 8797, 8819, 8841,
8863, 8885, 8907, 8929, 8951, 8974, 8996, 9018,
9040, 9062, 9084, 9106, 9128, 9150, 9172, 9194,
9216, 9238, 9260, 9282, 9304, 9326, 9348, 9370,
9392, 9414, 9436, 9458, 9479, 9501, 9523, 9545,
9567, 9589, 9611, 9633, 9653, 9676, 9698, 9720,
9742, 9763, 9784, 9806, 9829, 9851, 9872, 9893,
9915, 9936, 9959, 9981, 10002, 10023, 10045, 10068,
10088, 10110, 10132, 10152, 10175, 10196, 10217, 10239,
10260, 10283, 10303, 10325, 10346, 10368, 10389, 10410,
10431, 10453, 10474, 10496, 10517, 10538, 10560, 10581,
10602, 10623, 10644, 10666, 10687, 10708, 10728, 10750,
10772, 10792, 10814, 10834, 10856, 10876, 10898, 10918,
10939, 10959, 10982, 11002, 11023, 11043, 11064, 11084,
11107, 11127, 11148, 11168, 11189, 11209, 11231, 11251,
11272, 11292, 11313, 11333, 11354, 11374, 11394, 11415,
11436, 11457, 11477, 11498, 11518, 11538, 11559, 11578,
11599, 11619, 11640, 11660, 11680, 11700, 11720, 11741,
11761, 11780, 11801, 11821, 11839, 11860, 11880, 11900,
11920, 11940, 11960, 11980, 11998, 12019, 12039, 12059,
12079, 12097, 12118, 12137, 12157, 12176, 12195, 12215,
12235, 12254, 12273, 12293, 12312, 12332, 12351, 12370,
12390, 12408, 12427, 12448, 12466, 12485, 12504, 12524,
12542, 12562, 12580, 12599, 12618, 12637, 12655, 12675,
12694, 12712, 12731, 12750, 12768, 12787, 12806, 12823,
12842, 12860, 12879, 12898, 12916, 12934, 12953, 12971,
12989, 13008, 13026, 13044, 13062, 13080, 13098, 13116,
13134, 13152, 13170, 13188, 13205, 13224, 13241, 13258,
13277, 13294, 13312, 13330, 13347, 13365, 13383, 13399,
13417, 13435, 13451, 13469, 13486, 13504, 13521, 13538,
13556, 13572, 13589, 13607, 13623, 13641, 13657, 13673,
13691, 13707, 13724, 13741, 13758, 13775, 13791, 13808,
13823, 13840, 13857, 13873, 13890, 13906, 13923, 13939,
13955, 13971, 13986, 14003, 14019, 14035, 14051, 14067,
14082, 14099, 14115, 14130, 14146, 14161, 14177, 14192,
14208, 14223, 14239, 14254, 14270, 14285, 14301, 14316,
14330, 14346, 14361, 14376, 14391, 14406, 14420, 14435,
14450, 14465, 14480, 14494, 14509, 14523, 14538, 14553,
14567, 14582, 14595, 14610, 14625, 14638, 14653, 14668,
14681, 14696, 14709, 14723, 14737, 14752, 14764, 14778,
14793, 14806, 14819, 14833, 14846, 14860, 14874, 14887,
14900, 14913, 14927, 14940, 14953, 14966, 14979, 14992,
15004, 15017, 15031, 15043, 15056, 15067, 15081, 15094,
15106, 15118, 15131, 15143, 15155, 15167, 15180, 15192,
15205, 15216, 15228, 15240, 15252, 15264, 15276, 15287,
15299, 15310, 15321, 15333, 15345, 15356, 15367, 15379,
15390, 15401, 15412, 15423, 15433, 15446, 15456, 15467,
15478, 15489, 15499, 15509, 15520, 15530, 15540, 15552,
15562, 15572, 15582, 15592, 15602, 15613, 15623, 15633,
15642, 15652, 15662, 15672, 15681, 15691, 15700, 15709,
15719, 15729, 15738, 15747, 15756, 15765, 15774, 15783,
15792, 15801, 15810, 15818, 15827, 15836, 15843, 15853,
15861, 15870, 15877, 15886, 15894, 15903, 15910, 15918,
15926, 15934, 15941, 15949, 15957, 15964, 15973, 15980,
15988, 15995, 16002, 16009, 16015, 16023, 16030, 16038,
16044, 16052, 16058, 16064, 16071, 16077, 16085, 16092,
16097, 16104, 16109, 16116, 16122, 16128, 16133, 16140,
16146, 16151, 16158, 16163, 16169, 16174, 16180, 16185,
16191, 16196, 16201, 16206, 16211, 16216, 16220, 16225,
16230, 16235, 16239, 16244, 16247, 16253, 16257, 16262,
16265, 16269, 16274, 16278, 16282, 16285, 16290, 16293,
16297, 16299, 16304, 16307, 16309, 16314, 16317, 16320,
16322, 16326, 16329, 16331, 16335, 16337, 16340, 16342,
16345, 16347, 16349, 16351, 16354, 16357, 16358, 16360,
16361, 16364, 16366, 16366, 16369, 16370, 16371, 16373,
16374, 16376, 16377, 16377, 16379, 16379, 16380, 16381,
16381, 16381, 16382, 16383, 16384, 16384, 16384, 16384,
};
static int16 cubic_spline_lut3[1024] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, -1, -1, -1, -1, -1, -2, -2,
-2, -2, -2, -3, -3, -3, -4, -4,
-4, -5, -5, -6, -6, -6, -7, -7,
-8, -8, -9, -9, -10, -10, -11, -11,
-12, -13, -13, -14, -14, -15, -16, -16,
-17, -18, -19, -19, -20, -21, -22, -22,
-23, -24, -25, -26, -26, -27, -28, -29,
-30, -31, -32, -33, -34, -35, -36, -37,
-38, -39, -40, -41, -42, -43, -44, -45,
-46, -47, -48, -49, -51, -52, -53, -54,
-55, -57, -58, -59, -60, -61, -63, -64,
-65, -67, -68, -69, -70, -72, -73, -75,
-76, -77, -79, -80, -82, -83, -84, -86,
-87, -89, -90, -92, -93, -95, -96, -98,
-99, -101, -102, -104, -106, -107, -109, -110,
-112, -114, -115, -117, -119, -120, -122, -124,
-125, -127, -129, -130, -132, -134, -136, -137,
-139, -141, -143, -145, -146, -148, -150, -152,
-154, -156, -157, -159, -161, -163, -165, -167,
-169, -171, -173, -175, -176, -178, -180, -182,
-184, -186, -188, -190, -192, -194, -196, -198,
-200, -202, -205, -207, -209, -211, -213, -215,
-217, -219, -221, -223, -225, -228, -230, -232,
-234, -236, -238, -240, -243, -245, -247, -249,
-251, -254, -256, -258, -260, -263, -265, -267,
-269, -272, -274, -276, -278, -281, -283, -285,
-288, -290, -292, -295, -297, -299, -302, -304,
-306, -309, -311, -313, -316, -318, -320, -323,
-325, -328, -330, -332, -335, -337, -340, -342,
-345, -347, -349, -352, -354, -357, -359, -362,
-364, -367, -369, -372, -374, -377, -379, -382,
-384, -387, -389, -392, -394, -397, -399, -402,
-404, -407, -409, -412, -414, -417, -419, -422,
-424, -427, -430, -432, -435, -437, -440, -442,
-445, -448, -450, -453, -455, -458, -461, -463,
-466, -468, -471, -474, -476, -479, -481, -484,
-487, -489, -492, -495, -497, -500, -502, -505,
-508, -510, -513, -516, -518, -521, -523, -526,
-529, -531, -534, -537, -539, -542, -545, -547,
-550, -553, -555, -558, -561, -563, -566, -569,
-571, -574, -577, -579, -582, -585, -587, -590,
-593, -595, -598, -601, -603, -606, -609, -611,
-614, -617, -619, -622, -625, -627, -630, -633,
-635, -638, -641, -643, -646, -649, -651, -654,
-657, -659, -662, -665, -667, -670, -672, -675,
-678, -680, -683, -686, -688, -691, -694, -696,
-699, -702, -704, -707, -709, -712, -715, -717,
-720, -723, -725, -728, -730, -733, -736, -738,
-741, -744, -746, -749, -751, -754, -757, -759,
-762, -764, -767, -769, -772, -775, -777, -780,
-782, -785, -787, -790, -793, -795, -798, -800,
-803, -805, -808, -810, -813, -815, -818, -820,
-823, -825, -828, -830, -833, -835, -838, -840,
-843, -845, -848, -850, -853, -855, -858, -860,
-863, -865, -867, -870, -872, -875, -877, -880,
-882, -884, -887, -889, -892, -894, -896, -899,
-901, -903, -906, -908, -911, -913, -915, -918,
-920, -922, -924, -927, -929, -931, -934, -936,
-938, -941, -943, -945, -947, -950, -952, -954,
-956, -958, -961, -963, -965, -967, -969, -972,
-974, -976, -978, -980, -982, -985, -987, -989,
-991, -993, -995, -997, -999, -1002, -1004, -1006,
-1008, -1010, -1012, -1014, -1016, -1018, -1020, -1022,
-1024, -1026, -1028, -1030, -1032, -1034, -1036, -1038,
-1040, -1042, -1044, -1046, -1047, -1049, -1051, -1053,
-1055, -1057, -1059, -1061, -1062, -1064, -1066, -1068,
-1070, -1071, -1073, -1075, -1077, -1079, -1080, -1082,
-1084, -1085, -1087, -1089, -1091, -1092, -1094, -1096,
-1097, -1099, -1101, -1102, -1104, -1105, -1107, -1109,
-1110, -1112, -1113, -1115, -1116, -1118, -1119, -1121,
-1122, -1124, -1125, -1127, -1128, -1130, -1131, -1133,
-1134, -1135, -1137, -1138, -1140, -1141, -1142, -1144,
-1145, -1146, -1148, -1149, -1150, -1151, -1153, -1154,
-1155, -1156, -1158, -1159, -1160, -1161, -1162, -1163,
-1165, -1166, -1167, -1168, -1169, -1170, -1171, -1172,
-1173, -1174, -1175, -1176, -1177, -1178, -1179, -1180,
-1181, -1182, -1183, -1184, -1185, -1186, -1187, -1187,
-1188, -1189, -1190, -1191, -1192, -1192, -1193, -1194,
-1195, -1195, -1196, -1197, -1197, -1198, -1199, -1199,
-1200, -1201, -1201, -1202, -1202, -1203, -1204, -1204,
-1205, -1205, -1206, -1206, -1207, -1207, -1207, -1208,
-1208, -1209, -1209, -1209, -1210, -1210, -1210, -1211,
-1211, -1211, -1211, -1212, -1212, -1212, -1212, -1213,
-1213, -1213, -1213, -1213, -1213, -1213, -1213, -1214,
-1214, -1214, -1214, -1214, -1214, -1214, -1214, -1213,
-1213, -1213, -1213, -1213, -1213, -1213, -1213, -1212,
-1212, -1212, -1212, -1212, -1211, -1211, -1211, -1210,
-1210, -1210, -1209, -1209, -1208, -1208, -1208, -1207,
-1207, -1206, -1206, -1205, -1205, -1204, -1204, -1203,
-1202, -1202, -1201, -1200, -1200, -1199, -1198, -1198,
-1197, -1196, -1195, -1195, -1194, -1193, -1192, -1191,
-1190, -1189, -1188, -1187, -1187, -1186, -1185, -1184,
-1182, -1181, -1180, -1179, -1178, -1177, -1176, -1175,
-1174, -1172, -1171, -1170, -1169, -1167, -1166, -1165,
-1163, -1162, -1161, -1159, -1158, -1156, -1155, -1153,
-1152, -1150, -1149, -1147, -1146, -1144, -1143, -1141,
-1139, -1138, -1136, -1134, -1133, -1131, -1129, -1127,
-1125, -1124, -1122, -1120, -1118, -1116, -1114, -1112,
-1110, -1108, -1106, -1104, -1102, -1100, -1098, -1096,
-1094, -1092, -1089, -1087, -1085, -1083, -1080, -1078,
-1076, -1074, -1071, -1069, -1066, -1064, -1062, -1059,
-1057, -1054, -1052, -1049, -1047, -1044, -1041, -1039,
-1036, -1033, -1031, -1028, -1025, -1022, -1020, -1017,
-1014, -1011, -1008, -1005, -1002, -999, -997, -994,
-991, -987, -984, -981, -978, -975, -972, -969,
-966, -962, -959, -956, -953, -949, -946, -943,
-939, -936, -932, -929, -925, -922, -918, -915,
-911, -908, -904, -900, -897, -893, -889, -886,
-882, -878, -874, -870, -866, -863, -859, -855,
-851, -847, -843, -839, -835, -831, -826, -822,
-818, -814, -810, -806, -801, -797, -793, -788,
-784, -780, -775, -771, -766, -762, -757, -753,
-748, -744, -739, -734, -730, -725, -720, -715,
-711, -706, -701, -696, -691, -686, -682, -677,
-672, -667, -662, -656, -651, -646, -641, -636,
-631, -626, -620, -615, -610, -604, -599, -594,
-588, -583, -577, -572, -566, -561, -555, -550,
-544, -538, -533, -527, -521, -515, -510, -504,
-498, -492, -486, -480, -474, -468, -462, -456,
-450, -444, -438, -432, -425, -419, -413, -407,
-400, -394, -388, -381, -375, -368, -362, -355,
-349, -342, -336, -329, -322, -316, -309, -302,
-295, -289, -282, -275, -268, -261, -254, -247,
-240, -233, -226, -219, -212, -205, -198, -190,
-183, -176, -169, -161, -154, -146, -139, -132,
-124, -117, -109, -101, -94, -86, -78, -71,
-63, -55, -47, -40, -32, -24, -16, -8,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,549 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Sun, 31 May 1998 17:50:02 -0600
* Reported by ToyKeeper <scriven@CS.ColoState.EDU>:
* For loop-prevention, I know a way to do it which lets most songs play
* fine once through even if they have backward-jumps. Just keep a small
* array (256 bytes, or even bits) of flags, each entry determining if a
* pattern in the song order has been played. If you get to an entry which
* is already non-zero, skip to the next song (assuming looping is off).
*/
/*
* Tue, 6 Oct 1998 21:23:17 +0200 (CEST)
* Reported by John v/d Kamp <blade_@dds.nl>:
* scan.c was hanging when it jumps to an invalid restart value.
* (Fixed by hipolito)
*/
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "effects.h"
#include "mixer.h"
#define S3M_END 0xff
#define S3M_SKIP 0xfe
static int scan_module(struct context_data *ctx, int ep, int chain)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int parm, gvol_memory, f1, f2, p1, p2, ord, ord2;
int row, last_row, break_row, row_count;
int gvl, bpm, speed, base_time, chn;
int frame_count;
double time, start_time;
int loop_chn, loop_num, inside_loop;
int pdelay = 0;
int loop_count[XMP_MAX_CHANNELS];
int loop_row[XMP_MAX_CHANNELS];
struct xmp_event* event;
int i, pat;
int has_marker;
struct ord_data *info;
#ifndef LIBXMP_CORE_PLAYER
int st26_speed;
#endif
if (mod->len == 0)
return 0;
for (i = 0; i < mod->len; i++) {
int pat = mod->xxo[i];
memset(m->scan_cnt[i], 0, pat >= mod->pat ? 1 :
mod->xxp[pat]->rows ? mod->xxp[pat]->rows : 1);
}
for (i = 0; i < mod->chn; i++) {
loop_count[i] = 0;
loop_row[i] = -1;
}
loop_num = 0;
loop_chn = -1;
gvl = mod->gvl;
bpm = mod->bpm;
speed = mod->spd;
base_time = m->rrate;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
has_marker = HAS_QUIRK(QUIRK_MARKER);
/* By erlk ozlr <erlk.ozlr@gmail.com>
*
* xmp doesn't handle really properly the "start" option (-s for the
* command-line) for DeusEx's .umx files. These .umx files contain
* several loop "tracks" that never join together. That's how they put
* multiple musics on each level with a file per level. Each "track"
* starts at the same order in all files. The problem is that xmp starts
* decoding the module at order 0 and not at the order specified with
* the start option. If we have a module that does "0 -> 2 -> 0 -> ...",
* we cannot play order 1, even with the supposed right option.
*
* was: ord2 = ord = -1;
*
* CM: Fixed by using different "sequences" for each loop or subsong.
* Each sequence has its entry point. Sequences don't overlap.
*/
ord2 = -1;
ord = ep - 1;
gvol_memory = break_row = row_count = frame_count = 0;
start_time = time = 0.0;
inside_loop = 0;
while (42) {
if ((uint32)++ord >= mod->len) {
if (mod->rst > mod->len || mod->xxo[mod->rst] >= mod->pat) {
ord = ep;
} else {
if (libxmp_get_sequence(ctx, mod->rst) == chain) {
ord = mod->rst;
} else {
ord = ep;
}
}
pat = mod->xxo[ord];
if (has_marker && pat == S3M_END) {
break;
}
}
pat = mod->xxo[ord];
info = &m->xxo_info[ord];
/* Allow more complex order reuse only in main sequence */
if (ep != 0 && p->sequence_control[ord] != 0xff) {
break;
}
p->sequence_control[ord] = chain;
/* All invalid patterns skipped, only S3M_END aborts replay */
if (pat >= mod->pat) {
if (has_marker && pat == S3M_END) {
ord = mod->len;
continue;
}
continue;
}
if (break_row >= mod->xxp[pat]->rows) {
break_row = 0;
}
/* Loops can cross pattern boundaries, so check if we're not looping */
if (m->scan_cnt[ord][break_row] && !inside_loop) {
break;
}
/* Don't update pattern information if we're inside a loop, otherwise
* a loop containing e.g. a global volume fade can make the pattern
* start with the wrong volume.
*/
if (!inside_loop && info->gvl < 0) {
info->gvl = gvl;
info->bpm = bpm;
info->speed = speed;
info->time = time + m->time_factor * frame_count * base_time / bpm;
#ifndef LIBXMP_CORE_PLAYER
info->st26_speed = st26_speed;
#endif
}
if (info->start_row == 0 && ord != 0) {
if (ord == ep) {
start_time = time + m->time_factor * frame_count * base_time / bpm;
}
info->start_row = break_row;
}
last_row = mod->xxp[pat]->rows;
for (row = break_row, break_row = 0; row < last_row; row++, row_count++) {
/* Prevent crashes caused by large softmixer frames */
if (bpm < XMP_MIN_BPM) {
bpm = XMP_MIN_BPM;
}
/* Date: Sat, 8 Sep 2007 04:01:06 +0200
* Reported by Zbigniew Luszpinski <zbiggy@o2.pl>
* The scan routine falls into infinite looping and doesn't let
* xmp play jos-dr4k.xm.
* Claudio's workaround: we'll break infinite loops here.
*
* Date: Oct 27, 2007 8:05 PM
* From: Adric Riedel <adric.riedel@gmail.com>
* Jesper Kyd: Global Trash 3.mod (the 'Hardwired' theme) only
* plays the first 4:41 of what should be a 10 minute piece.
* (...) it dies at the end of position 2F
*/
if (row_count > 512) /* was 255, but Global trash goes to 318 */
goto end_module;
if (!loop_num && m->scan_cnt[ord][row]) {
row_count--;
goto end_module;
}
m->scan_cnt[ord][row]++;
pdelay = 0;
for (chn = 0; chn < mod->chn; chn++) {
if (row >= mod->xxt[mod->xxp[pat]->index[chn]]->rows)
continue;
event = &EVENT(mod->xxo[ord], chn, row);
f1 = event->fxt;
p1 = event->fxp;
f2 = event->f2t;
p2 = event->f2p;
if (f1 == FX_GLOBALVOL || f2 == FX_GLOBALVOL) {
gvl = (f1 == FX_GLOBALVOL) ? p1 : p2;
gvl = gvl > m->gvolbase ? m->gvolbase : gvl < 0 ? 0 : gvl;
}
/* Process fine global volume slide */
if (f1 == FX_GVOL_SLIDE || f2 == FX_GVOL_SLIDE) {
int h, l;
parm = (f1 == FX_GVOL_SLIDE) ? p1 : p2;
process_gvol:
if (parm) {
gvol_memory = parm;
h = MSN(parm);
l = LSN(parm);
if (HAS_QUIRK(QUIRK_FINEFX)) {
if (l == 0xf && h != 0) {
gvl += h;
} else if (h == 0xf && l != 0) {
gvl -= l;
} else {
if (m->quirk & QUIRK_VSALL) {
gvl += (h - l) * speed;
} else {
gvl += (h - l) * (speed - 1);
}
}
} else {
if (m->quirk & QUIRK_VSALL) {
gvl += (h - l) * speed;
} else {
gvl += (h - l) * (speed - 1);
}
}
} else {
if ((parm = gvol_memory) != 0)
goto process_gvol;
}
}
if ((f1 == FX_SPEED && p1) || (f2 == FX_SPEED && p2)) {
parm = (f1 == FX_SPEED) ? p1 : p2;
frame_count += row_count * speed;
row_count = 0;
if (parm) {
if (HAS_QUIRK(QUIRK_NOBPM) || p->flags & XMP_FLAGS_VBLANK || parm < 0x20) {
if (parm > 0) {
speed = parm;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
}
} else {
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
}
}
#ifndef LIBXMP_CORE_PLAYER
if (f1 == FX_SPEED_CP) {
f1 = FX_S3M_SPEED;
}
if (f2 == FX_SPEED_CP) {
f2 = FX_S3M_SPEED;
}
/* ST2.6 speed processing */
if (f1 == FX_ICE_SPEED && p1) {
if (LSN(p1)) {
st26_speed = (MSN(p1) << 8) | LSN(p1);
} else {
st26_speed = MSN(p1);
}
}
#endif
if ((f1 == FX_S3M_SPEED && p1) || (f2 == FX_S3M_SPEED && p2)) {
parm = (f1 == FX_S3M_SPEED) ? p1 : p2;
if (parm > 0) {
frame_count += row_count * speed;
row_count = 0;
speed = parm;
#ifndef LIBXMP_CORE_PLAYER
st26_speed = 0;
#endif
}
}
if ((f1 == FX_S3M_BPM && p1) || (f2 == FX_S3M_BPM && p2)) {
parm = (f1 == FX_S3M_BPM) ? p1 : p2;
if (parm >= 0x20) {
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
bpm = parm;
}
}
#ifndef LIBXMP_CORE_DISABLE_IT
if ((f1 == FX_IT_BPM && p1) || (f2 == FX_IT_BPM && p2)) {
parm = (f1 == FX_IT_BPM) ? p1 : p2;
frame_count += row_count * speed;
row_count = 0;
time += m->time_factor * frame_count * base_time / bpm;
frame_count = 0;
if (MSN(parm) == 0) {
time += m->time_factor * base_time / bpm;
for (i = 1; i < speed; i++) {
bpm -= LSN(parm);
if (bpm < 0x20)
bpm = 0x20;
time += m->time_factor * base_time / bpm;
}
/* remove one row at final bpm */
time -= m->time_factor * speed * base_time / bpm;
} else if (MSN(parm) == 1) {
time += m->time_factor * base_time / bpm;
for (i = 1; i < speed; i++) {
bpm += LSN(parm);
if (bpm > 0xff)
bpm = 0xff;
time += m->time_factor * base_time / bpm;
}
/* remove one row at final bpm */
time -= m->time_factor * speed * base_time / bpm;
} else {
bpm = parm;
}
}
if (f1 == FX_IT_ROWDELAY) {
m->scan_cnt[ord][row] += p1 & 0x0f;
frame_count += (p1 & 0x0f) * speed;
}
if (f1 == FX_IT_BREAK) {
break_row = p1;
last_row = 0;
}
#endif
if (f1 == FX_JUMP || f2 == FX_JUMP) {
ord2 = (f1 == FX_JUMP) ? p1 : p2;
break_row = 0;
last_row = 0;
/* prevent infinite loop, see OpenMPT PatLoop-Various.xm */
inside_loop = 0;
}
if (f1 == FX_BREAK || f2 == FX_BREAK) {
parm = (f1 == FX_BREAK) ? p1 : p2;
break_row = 10 * MSN(parm) + LSN(parm);
last_row = 0;
}
if (f1 == FX_EXTENDED || f2 == FX_EXTENDED) {
parm = (f1 == FX_EXTENDED) ? p1 : p2;
if ((parm >> 4) == EX_PATT_DELAY) {
if (m->read_event_type != READ_EVENT_ST3 || !pdelay) {
pdelay = parm & 0x0f;
frame_count += pdelay * speed;
}
}
if ((parm >> 4) == EX_PATTERN_LOOP) {
if (parm &= 0x0f) {
/* Loop end */
if (loop_count[chn]) {
if (--loop_count[chn]) {
/* next iteraction */
loop_chn = chn;
} else {
/* finish looping */
loop_num--;
inside_loop = 0;
if (m->quirk & QUIRK_S3MLOOP)
loop_row[chn] = row;
}
} else {
loop_count[chn] = parm;
loop_chn = chn;
loop_num++;
}
} else {
/* Loop start */
loop_row[chn] = row - 1;
inside_loop = 1;
if (HAS_QUIRK(QUIRK_FT2BUGS))
break_row = row;
}
}
}
}
if (loop_chn >= 0) {
row = loop_row[loop_chn];
loop_chn = -1;
}
#ifndef LIBXMP_CORE_PLAYER
if (st26_speed) {
frame_count += row_count * speed;
row_count = 0;
if (st26_speed & 0x10000) {
speed = (st26_speed & 0xff00) >> 8;
} else {
speed = st26_speed & 0xff;
}
st26_speed ^= 0x10000;
}
#endif
}
if (break_row && pdelay) {
break_row++;
}
if (ord2 >= 0) {
ord = ord2 - 1;
ord2 = -1;
}
frame_count += row_count * speed;
row_count = 0;
}
row = break_row;
end_module:
/* Sanity check */
{
pat = mod->xxo[ord];
if (pat >= mod->pat || row >= mod->xxp[pat]->rows) {
row = 0;
}
}
p->scan[chain].num = m->scan_cnt[ord][row];
p->scan[chain].row = row;
p->scan[chain].ord = ord;
time -= start_time;
frame_count += row_count * speed;
return (time + m->time_factor * frame_count * base_time / bpm);
}
int libxmp_get_sequence(struct context_data *ctx, int ord)
{
struct player_data *p = &ctx->p;
return p->sequence_control[ord];
}
int libxmp_scan_sequences(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
int i, ep;
int seq;
unsigned char temp_ep[XMP_MAX_MOD_LENGTH];
/* Initialize order data to prevent overwrite when a position is used
* multiple times at different starting points (see janosik.xm).
*/
for (i = 0; i < XMP_MAX_MOD_LENGTH; i++) {
m->xxo_info[i].gvl = -1;
}
ep = 0;
memset(p->sequence_control, 0xff, XMP_MAX_MOD_LENGTH);
temp_ep[0] = 0;
p->scan[0].time = scan_module(ctx, ep, 0);
seq = 1;
while (1) {
/* Scan song starting at given entry point */
/* Check if any patterns left */
for (i = 0; i < mod->len; i++) {
if (p->sequence_control[i] == 0xff) {
break;
}
}
if (i != mod->len && seq < MAX_SEQUENCES) {
/* New entry point */
ep = i;
temp_ep[seq] = ep;
p->scan[seq].time = scan_module(ctx, ep, seq);
if (p->scan[seq].time > 0)
seq++;
} else {
break;
}
}
m->num_sequences = seq;
/* Now place entry points in the public accessible array */
for (i = 0; i < m->num_sequences; i++) {
m->seq_data[i].entry_point = temp_ep[i];
m->seq_data[i].duration = p->scan[i].time;
}
return 0;
}

View file

@ -0,0 +1,322 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include "common.h"
#include "period.h"
#include "player.h"
#include "hio.h"
struct xmp_instrument *libxmp_get_instrument(struct context_data *ctx, int ins)
{
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_instrument *xxi;
if (ins < mod->ins) {
xxi = &mod->xxi[ins];
} else if (ins < mod->ins + smix->ins) {
xxi = &smix->xxi[ins - mod->ins];
} else {
xxi = NULL;
}
return xxi;
}
struct xmp_sample *libxmp_get_sample(struct context_data *ctx, int smp)
{
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_sample *xxs;
if (smp < mod->smp) {
xxs = &mod->xxs[smp];
} else if (smp < mod->smp + smix->smp) {
xxs = &smix->xxs[smp - mod->smp];
} else {
xxs = NULL;
}
return xxs;
}
int xmp_start_smix(xmp_context opaque, int chn, int smp)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
if (ctx->state > XMP_STATE_LOADED) {
return -XMP_ERROR_STATE;
}
smix->xxi = calloc(sizeof (struct xmp_instrument), smp);
if (smix->xxi == NULL) {
goto err;
}
smix->xxs = calloc(sizeof (struct xmp_sample), smp);
if (smix->xxs == NULL) {
goto err1;
}
smix->chn = chn;
smix->ins = smix->smp = smp;
return 0;
err1:
free(smix->xxi);
err:
return -XMP_ERROR_INTERNAL;
}
int xmp_smix_play_instrument(xmp_context opaque, int ins, int note, int vol, int chn)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= mod->ins) {
return -XMP_ERROR_INVALID;
}
if (note == 0) {
note = 60; /* middle C note number */
}
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->ins = ins + 1;
event->vol = vol + 1;
event->_flag = 1;
return 0;
}
int xmp_smix_play_sample(xmp_context opaque, int ins, int note, int vol, int chn)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_module *mod = &m->mod;
struct xmp_event *event;
if (ctx->state < XMP_STATE_PLAYING) {
return -XMP_ERROR_STATE;
}
if (chn >= smix->chn || ins >= smix->ins) {
return -XMP_ERROR_INVALID;
}
if (note == 0) {
note = 60; /* middle C note number */
}
event = &p->inject_event[mod->chn + chn];
memset(event, 0, sizeof (struct xmp_event));
event->note = note + 1;
event->ins = mod->ins + ins + 1;
event->vol = vol + 1;
event->_flag = 1;
return 0;
}
int xmp_smix_channel_pan(xmp_context opaque, int chn, int pan)
{
struct context_data *ctx = (struct context_data *)opaque;
struct player_data *p = &ctx->p;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct channel_data *xc;
if (chn >= smix->chn || pan < 0 || pan > 255) {
return -XMP_ERROR_INVALID;
}
xc = &p->xc_data[m->mod.chn + chn];
xc->pan.val = pan;
return 0;
}
int xmp_smix_load_sample(xmp_context opaque, int num, char *path)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
struct module_data *m = &ctx->m;
struct xmp_instrument *xxi;
struct xmp_sample *xxs;
HIO_HANDLE *h;
uint32 magic;
int chn, rate, bits, size;
int retval = -XMP_ERROR_INTERNAL;
if (num >= smix->ins) {
retval = -XMP_ERROR_INVALID;
goto err;
}
xxi = &smix->xxi[num];
xxs = &smix->xxs[num];
h = hio_open(path, "rb");
if (h == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err;
}
/* Init instrument */
xxi->sub = calloc(sizeof(struct xmp_subinstrument), 1);
if (xxi->sub == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err1;
}
xxi->vol = m->volbase;
xxi->nsm = 1;
xxi->sub[0].sid = num;
xxi->sub[0].vol = xxi->vol;
xxi->sub[0].pan = 0x80;
/* Load sample */
magic = hio_read32b(h);
if (magic != 0x52494646) { /* RIFF */
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 22, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
chn = hio_read16l(h);
if (chn != 1) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
rate = hio_read32l(h);
if (rate == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 34, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
bits = hio_read16l(h);
if (bits == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
if (hio_seek(h, 40, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
size = hio_read32l(h) / (bits / 8);
if (size == 0) {
retval = -XMP_ERROR_FORMAT;
goto err2;
}
libxmp_c2spd_to_note(rate, &xxi->sub[0].xpo, &xxi->sub[0].fin);
xxs->len = 8 * size / bits;
xxs->lps = 0;
xxs->lpe = 0;
xxs->flg = bits == 16 ? XMP_SAMPLE_16BIT : 0;
xxs->data = malloc(size);
if (xxs->data == NULL) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
if (hio_seek(h, 44, SEEK_SET) < 0) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
if (hio_read(xxs->data, 1, size, h) != size) {
retval = -XMP_ERROR_SYSTEM;
goto err2;
}
hio_close(h);
return 0;
err2:
free(xxi->sub);
xxi->sub = NULL;
err1:
hio_close(h);
err:
return retval;
}
int xmp_smix_release_sample(xmp_context opaque, int num)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
if (num >= smix->ins) {
return -XMP_ERROR_INVALID;
}
free(smix->xxs[num].data);
free(smix->xxi[num].sub);
smix->xxs[num].data = NULL;
smix->xxi[num].sub = NULL;
return 0;
}
void xmp_end_smix(xmp_context opaque)
{
struct context_data *ctx = (struct context_data *)opaque;
struct smix_data *smix = &ctx->smix;
int i;
for (i = 0; i < smix->smp; i++) {
xmp_smix_release_sample(opaque, i);
}
free(smix->xxs);
free(smix->xxi);
}

View file

@ -0,0 +1,7 @@
#ifndef XMP_PLATFORM_H
#define XMP_PLATFORM_H
FILE *make_temp_file(char **);
void unlink_temp_file(char *);
#endif

View file

@ -0,0 +1,597 @@
/* Extended Module Player
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "common.h"
#include "virtual.h"
#include "mixer.h"
#ifdef LIBXMP_PAULA_SIMULATOR
#include "paula.h"
#endif
#define FREE -1
/* For virt_pastnote() */
void libxmp_player_set_release(struct context_data *, int);
void libxmp_player_set_fadeout(struct context_data *, int);
/* Get parent channel */
int libxmp_virt_getroot(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
int voc;
voc = p->virt.virt_channel[chn].map;
if (voc < 0) {
return -1;
}
vi = &p->virt.voice_array[voc];
return vi->root;
}
void libxmp_virt_resetvoice(struct context_data *ctx, int voc, int mute)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula;
#endif
if ((uint32)voc >= p->virt.maxvoc) {
return;
}
if (mute) {
libxmp_mixer_setvol(ctx, voc, 0);
}
p->virt.virt_used--;
p->virt.virt_channel[vi->root].count--;
p->virt.virt_channel[vi->chn].map = FREE;
#ifdef LIBXMP_PAULA_SIMULATOR
paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = vi->root = FREE;
}
/* virt_on (number of tracks) */
int libxmp_virt_on(struct context_data *ctx, int num)
{
struct player_data *p = &ctx->p;
struct module_data *m = &ctx->m;
int i;
p->virt.num_tracks = num;
num = libxmp_mixer_numvoices(ctx, -1);
p->virt.virt_channels = p->virt.num_tracks;
if (HAS_QUIRK(QUIRK_VIRTUAL)) {
p->virt.virt_channels += num;
} else if (num > p->virt.virt_channels) {
num = p->virt.virt_channels;
}
p->virt.maxvoc = libxmp_mixer_numvoices(ctx, num);
p->virt.voice_array = calloc(p->virt.maxvoc,
sizeof(struct mixer_voice));
if (p->virt.voice_array == NULL)
goto err;
for (i = 0; i < p->virt.maxvoc; i++) {
p->virt.voice_array[i].chn = FREE;
p->virt.voice_array[i].root = FREE;
}
#ifdef LIBXMP_PAULA_SIMULATOR
/* Initialize Paula simulator */
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
p->virt.voice_array[i].paula = calloc(1, sizeof (struct paula_state));
if (p->virt.voice_array[i].paula == NULL) {
goto err2;
}
libxmp_paula_init(ctx, p->virt.voice_array[i].paula);
}
}
#endif
p->virt.virt_channel = malloc(p->virt.virt_channels *
sizeof(struct virt_channel));
if (p->virt.virt_channel == NULL)
goto err2;
for (i = 0; i < p->virt.virt_channels; i++) {
p->virt.virt_channel[i].map = FREE;
p->virt.virt_channel[i].count = 0;
}
p->virt.virt_used = 0;
return 0;
err2:
#ifdef LIBXMP_PAULA_SIMULATOR
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
free(p->virt.voice_array[i].paula);
}
}
#endif
free(p->virt.voice_array);
err:
return -1;
}
void libxmp_virt_off(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
#ifdef LIBXMP_PAULA_SIMULATOR
struct module_data *m = &ctx->m;
int i;
#endif
#ifdef LIBXMP_PAULA_SIMULATOR
/* Free Paula simulator state */
if (IS_AMIGA_MOD()) {
for (i = 0; i < p->virt.maxvoc; i++) {
free(p->virt.voice_array[i].paula);
}
}
#endif
p->virt.virt_used = p->virt.maxvoc = 0;
p->virt.virt_channels = 0;
p->virt.num_tracks = 0;
free(p->virt.voice_array);
free(p->virt.virt_channel);
}
void libxmp_virt_reset(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
int i;
if (p->virt.virt_channels < 1) {
return;
}
/* CID 129203 (#1 of 1): Useless call (USELESS_CALL)
* Call is only useful for its return value, which is ignored.
*
* libxmp_mixer_numvoices(ctx, p->virt.maxvoc);
*/
for (i = 0; i < p->virt.maxvoc; i++) {
struct mixer_voice *vi = &p->virt.voice_array[i];
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = FREE;
vi->root = FREE;
}
for (i = 0; i < p->virt.virt_channels; i++) {
p->virt.virt_channel[i].map = FREE;
p->virt.virt_channel[i].count = 0;
}
p->virt.virt_used = 0;
}
static int free_voice(struct context_data *ctx)
{
struct player_data *p = &ctx->p;
int i, num, vol;
/* Find background voice with lowest volume*/
num = FREE;
vol = INT_MAX;
for (i = 0; i < p->virt.maxvoc; i++) {
struct mixer_voice *vi = &p->virt.voice_array[i];
if (vi->chn >= p->virt.num_tracks && vi->vol < vol) {
num = i;
vol = vi->vol;
}
}
/* Free voice */
if (num >= 0) {
p->virt.virt_channel[p->virt.voice_array[num].chn].map = FREE;
p->virt.virt_channel[p->virt.voice_array[num].root].count--;
p->virt.virt_used--;
}
return num;
}
static int alloc_voice(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int i;
/* Find free voice */
for (i = 0; i < p->virt.maxvoc; i++) {
if (p->virt.voice_array[i].chn == FREE)
break;
}
/* not found */
if (i == p->virt.maxvoc) {
i = free_voice(ctx);
}
if (i >= 0) {
p->virt.virt_channel[chn].count++;
p->virt.virt_used++;
p->virt.voice_array[i].chn = chn;
p->virt.voice_array[i].root = chn;
p->virt.virt_channel[chn].map = i;
}
return i;
}
static int map_virt_channel(struct player_data *p, int chn)
{
int voc;
if ((uint32)chn >= p->virt.virt_channels)
return -1;
voc = p->virt.virt_channel[chn].map;
if ((uint32)voc >= p->virt.maxvoc)
return -1;
return voc;
}
int libxmp_virt_mapchannel(struct context_data *ctx, int chn)
{
return map_virt_channel(&ctx->p, chn);
}
void libxmp_virt_resetchannel(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
#ifdef LIBXMP_PAULA_SIMULATOR
struct paula_state *paula;
#endif
int voc;
if ((voc = map_virt_channel(p, chn)) < 0)
return;
libxmp_mixer_setvol(ctx, voc, 0);
p->virt.virt_used--;
p->virt.virt_channel[p->virt.voice_array[voc].root].count--;
p->virt.virt_channel[chn].map = FREE;
vi = &p->virt.voice_array[voc];
#ifdef LIBXMP_PAULA_SIMULATOR
paula = vi->paula;
#endif
memset(vi, 0, sizeof(struct mixer_voice));
#ifdef LIBXMP_PAULA_SIMULATOR
vi->paula = paula;
#endif
vi->chn = vi->root = FREE;
}
void libxmp_virt_setvol(struct context_data *ctx, int chn, int vol)
{
struct player_data *p = &ctx->p;
int voc, root;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
root = p->virt.voice_array[voc].root;
if (root < XMP_MAX_CHANNELS && p->channel_mute[root]) {
vol = 0;
}
libxmp_mixer_setvol(ctx, voc, vol);
if (vol == 0 && chn >= p->virt.num_tracks) {
libxmp_virt_resetvoice(ctx, voc, 1);
}
}
void libxmp_virt_release(struct context_data *ctx, int chn, int rel)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_release(ctx, voc, rel);
}
void libxmp_virt_setpan(struct context_data *ctx, int chn, int pan)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setpan(ctx, voc, pan);
}
void libxmp_virt_seteffect(struct context_data *ctx, int chn, int type, int val)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_seteffect(ctx, voc, type, val);
}
double libxmp_virt_getvoicepos(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return -1;
}
return libxmp_mixer_getvoicepos(ctx, voc);
}
#ifndef LIBXMP_CORE_PLAYER
void libxmp_virt_setsmp(struct context_data *ctx, int chn, int smp)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi;
double pos;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
vi = &p->virt.voice_array[voc];
if (vi->smp == smp) {
return;
}
pos = libxmp_mixer_getvoicepos(ctx, voc);
libxmp_mixer_setpatch(ctx, voc, smp, 0);
libxmp_mixer_voicepos(ctx, voc, pos, 0); /* Restore old position */
}
#endif
#ifndef LIBXMP_CORE_DISABLE_IT
void libxmp_virt_setnna(struct context_data *ctx, int chn, int nna)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
p->virt.voice_array[voc].act = nna;
}
static void check_dct(struct context_data *ctx, int i, int chn, int ins,
int smp, int note, int dct, int dca)
{
struct player_data *p = &ctx->p;
struct mixer_voice *vi = &p->virt.voice_array[i];
int voc;
voc = p->virt.virt_channel[chn].map;
if (vi->root == chn && vi->ins == ins) {
if (dct == XMP_INST_DCT_INST ||
(dct == XMP_INST_DCT_SMP && vi->smp == smp) ||
(dct == XMP_INST_DCT_NOTE && vi->note == note)) {
if (dca) {
if (i != voc || vi->act)
vi->act = dca;
} else {
libxmp_virt_resetvoice(ctx, i, 1);
}
}
}
}
#endif
/* For note slides */
void libxmp_virt_setnote(struct context_data *ctx, int chn, int note)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setnote(ctx, voc, note);
}
int libxmp_virt_setpatch(struct context_data *ctx, int chn, int ins, int smp,
int note, int nna, int dct, int dca)
{
struct player_data *p = &ctx->p;
int voc, vfree;
if ((uint32)chn >= p->virt.virt_channels) {
return -1;
}
if (ins < 0) {
smp = -1;
}
#ifndef LIBXMP_CORE_DISABLE_IT
if (dct) {
int i;
for (i = 0; i < p->virt.maxvoc; i++) {
check_dct(ctx, i, chn, ins, smp, note, dct, dca);
}
}
#endif
voc = p->virt.virt_channel[chn].map;
if (voc > FREE) {
if (p->virt.voice_array[voc].act) {
vfree = alloc_voice(ctx, chn);
if (vfree < 0) {
return -1;
}
for (chn = p->virt.num_tracks;
p->virt.virt_channel[chn++].map > FREE;) ;
p->virt.voice_array[voc].chn = --chn;
p->virt.virt_channel[chn].map = voc;
voc = vfree;
}
} else {
voc = alloc_voice(ctx, chn);
if (voc < 0) {
return -1;
}
}
if (smp < 0) {
libxmp_virt_resetvoice(ctx, voc, 1);
return chn; /* was -1 */
}
libxmp_mixer_setpatch(ctx, voc, smp, 1);
libxmp_mixer_setnote(ctx, voc, note);
p->virt.voice_array[voc].ins = ins;
p->virt.voice_array[voc].act = nna;
return chn;
}
void libxmp_virt_setperiod(struct context_data *ctx, int chn, double period)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_setperiod(ctx, voc, period);
}
void libxmp_virt_voicepos(struct context_data *ctx, int chn, double pos)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return;
}
libxmp_mixer_voicepos(ctx, voc, pos, 1);
}
#ifndef LIBXMP_CORE_DISABLE_IT
void libxmp_virt_pastnote(struct context_data *ctx, int chn, int act)
{
struct player_data *p = &ctx->p;
int c, voc;
for (c = p->virt.num_tracks; c < p->virt.virt_channels; c++) {
if ((voc = map_virt_channel(p, c)) < 0)
continue;
if (p->virt.voice_array[voc].root == chn) {
switch (act) {
case VIRT_ACTION_CUT:
libxmp_virt_resetvoice(ctx, voc, 1);
break;
case VIRT_ACTION_OFF:
libxmp_player_set_release(ctx, c);
break;
case VIRT_ACTION_FADE:
libxmp_player_set_fadeout(ctx, c);
break;
}
}
}
}
#endif
int libxmp_virt_cstat(struct context_data *ctx, int chn)
{
struct player_data *p = &ctx->p;
int voc;
if ((voc = map_virt_channel(p, chn)) < 0) {
return VIRT_INVALID;
}
if (chn < p->virt.num_tracks) {
return VIRT_ACTIVE;
}
return p->virt.voice_array[voc].act;
}

View file

@ -0,0 +1,38 @@
#ifndef LIBXMP_VIRTUAL_H
#define LIBXMP_VIRTUAL_H
#include "common.h"
#define VIRT_ACTION_CUT XMP_INST_NNA_CUT
#define VIRT_ACTION_CONT XMP_INST_NNA_CONT
#define VIRT_ACTION_OFF XMP_INST_NNA_OFF
#define VIRT_ACTION_FADE XMP_INST_NNA_FADE
#define VIRT_ACTIVE 0x100
#define VIRT_INVALID -1
int libxmp_virt_on (struct context_data *, int);
void libxmp_virt_off (struct context_data *);
int libxmp_virt_mute (struct context_data *, int, int);
int libxmp_virt_setpatch (struct context_data *, int, int, int, int,
int, int, int);
int libxmp_virt_cvt8bit (void);
void libxmp_virt_setnote (struct context_data *, int, int);
void libxmp_virt_setsmp (struct context_data *, int, int);
void libxmp_virt_setnna (struct context_data *, int, int);
void libxmp_virt_pastnote (struct context_data *, int, int);
void libxmp_virt_setvol (struct context_data *, int, int);
void libxmp_virt_voicepos (struct context_data *, int, double);
double libxmp_virt_getvoicepos (struct context_data *, int);
void libxmp_virt_setperiod (struct context_data *, int, double);
void libxmp_virt_setpan (struct context_data *, int, int);
void libxmp_virt_seteffect (struct context_data *, int, int, int);
int libxmp_virt_cstat (struct context_data *, int);
int libxmp_virt_mapchannel (struct context_data *, int);
void libxmp_virt_resetchannel(struct context_data *, int);
void libxmp_virt_resetvoice (struct context_data *, int, int);
void libxmp_virt_reset (struct context_data *);
void libxmp_virt_release (struct context_data *, int, int);
int libxmp_virt_getroot (struct context_data *, int);
#endif /* LIBXMP_VIRTUAL_H */

View file

@ -0,0 +1,22 @@
TEST_OBJS = test.o md5.o
TEST_DFILES = Makefile $(TEST_OBJS:.o=.c) test.it md5.h
TEST_PATH = test
T_OBJS = $(addprefix $(TEST_PATH)/,$(TEST_OBJS))
default:
$(MAKE) -C .. check
dist-test:
mkdir -p $(DIST)/$(TEST_PATH)
cp -RPp $(addprefix $(TEST_PATH)/,$(TEST_DFILES)) $(DIST)/$(TEST_PATH)
check: $(TEST_PATH)/libxmp-test
cd $(TEST_PATH); LD_LIBRARY_PATH=../lib DYLD_LIBRARY_PATH=../lib LIBRARY_PATH=../lib:$$LIBRARY_PATH PATH=$$PATH:../lib ./libxmp-test
$(TEST_PATH)/libxmp-test: $(T_OBJS)
@CMD='$(LD) -o $@ $(T_OBJS) $(LIBS) -Llib -lxmp-lite'; \
if [ "$(V)" -gt 0 ]; then echo $$CMD; else echo LD $@ ; fi; \
eval $$CMD

Some files were not shown because too many files have changed in this diff Show more