diff --git a/CMakeLists.txt b/CMakeLists.txt index eed0ea4b..9c669b4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ OPTION(WANT_PLAYER "Build WildMIDI player in addition to the libraries" ON) OPTION(WANT_STATIC "Build static library in addition to dynamic library" OFF) OPTION(WANT_ALSA "Include ALSA (Advanced Linux Sound Architecture) support" OFF) OPTION(WANT_OSS "Include OSS (Open Sound System) support" OFF) -OPTION(WANT_COREAUDIO "Include CoreAudio support (Driver support for Mac OS X" OFF) +#OPTION(WANT_COREAUDIO "Include CoreAudio support (Driver support for Mac OS X" OFF) OPTION(WANT_OPENAL "Include OpenAL suport (Cross Platform) support" OFF) IF(UNIX AND NOT APPLE) SET(WILDMIDI_CFG "/etc/wildmidi/wildmidi.cfg" CACHE STRING "default config location") @@ -43,31 +43,28 @@ ELSE() SET(WILDMIDI_CFG "wildmidi.cfg" CACHE STRING "default config location") ENDIF() -# Compiler specific settings -IF(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" MATCHES "Clang") +# Platform specific defines +IF(UNIX) + # allow 'large' files in 32 bit builds ADD_DEFINITIONS( - -D_POSIX_C_SOURCE=200809L - -D_BSD_SOURCE=1 - -D_LARGEFILE_SOURCE - -D_FILE_OFFSET_BITS=64 + -D_LARGEFILE_SOURCE + -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES - -pedantic - -Wall -W ) +ENDIF() - CHECK_C_COMPILER_FLAG("-std=gnu99" HAVE_STD_GNU99) - CHECK_C_COMPILER_FLAG("-std=c99" HAVE_STD_C99) - IF(HAVE_STD_GNU99) - ADD_DEFINITIONS(-std=gnu99) - ELSEIF(HAVE_STD_C99) - ADD_DEFINITIONS(-std=c99) - ENDIF() +IF(WIN32) + ADD_DEFINITIONS( + -DWIN32_LEAN_AND_MEAN + -D_CRT_SECURE_NO_WARNINGS + ) +ENDIF() - IF(CMAKE_COMPILER_IS_MINGW) - # some mingw.org headers are overzealous with __STRICT_ANSI__ - # and -std=c99 (not gnu99) defines it - ADD_DEFINITIONS(-U__STRICT_ANSI__) - ENDIF() +# Compiler specific settings +IF(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + ADD_DEFINITIONS( + -Wall -W + ) IF(NOT WIN32 AND NOT CYGWIN) SET(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") @@ -122,14 +119,12 @@ SET(AUDIODRV_OPENAL) # UNIX-like environments IF(UNIX AND NOT APPLE) - SET(AUDIO_LIBRARY "") # Go looking for available sound packages for WildMIDI player IF(WANT_PLAYER) # Set our preferred output - FIND_PACKAGE(ALSA) + FIND_PACKAGE(OpenAL) FIND_PACKAGE(OSS) - INCLUDE(FindOpenAL) IF(WANT_ALSA) IF(NOT ALSA_FOUND) @@ -137,19 +132,21 @@ IF(UNIX AND NOT APPLE) ENDIF() SET(AUDIODRV_ALSA 1) SET(AUDIO_LIBRARY ${ALSA_LIBRARY}) + ELSEIF(WANT_OSS) IF(NOT OSS_FOUND) MESSAGE(FATAL_ERROR "OSS required but not found.") ENDIF() + # no special header paths SET(AUDIODRV_OSS 1) SET(AUDIO_LIBRARY ${OSS_LIBRARY}) + ELSEIF(WANT_OPENAL) IF(NOT OPENAL_FOUND) MESSAGE(FATAL_ERROR "OpenAL required but not found.") ENDIF() SET(AUDIODRV_OPENAL 1) SET(AUDIO_LIBRARY ${OPENAL_LIBRARY}) - INCLUDE_DIRECTORIES(${OPENAL_INCLUDE_DIR}) ELSE() # Try to auto-detect @@ -158,6 +155,7 @@ IF(UNIX AND NOT APPLE) SET(AUDIODRV_ALSA 1) ELSEIF(OSS_FOUND) + # no special header paths SET(AUDIO_LIBRARY ${OSS_LIBRARY}) SET(AUDIODRV_OSS 1) @@ -167,6 +165,7 @@ IF(UNIX AND NOT APPLE) ELSE() MESSAGE(WARNING "Could not find an audio sub-system!") + SET(AUDIO_LIBRARY "") ENDIF() ENDIF() @@ -184,7 +183,7 @@ IF(UNIX AND NOT APPLE) ENDIF(UNIX AND NOT APPLE) IF (APPLE AND WANT_PLAYER) - INCLUDE(FindOpenAL) + FIND_PACKAGE(OpenAL) IF(WANT_OPENAL) IF(NOT OPENAL_FOUND) @@ -199,6 +198,7 @@ IF (APPLE AND WANT_PLAYER) ELSE() MESSAGE(WARNING "Could not find an audio sub-system!") + SET(AUDIO_LIBRARY "") ENDIF() ENDIF() diff --git a/README.md b/README.md index 76284225..54ce5b1e 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,20 @@ Version: 0.4.0 Licenses: GPLv3+ and LGPLv3 Website: http://www.mindwerks.net/projects/wildmidi -PLATFORMS: +PLAYER AND LIB PLATFORMS: * Hurd: Debian * kFreeBSD: Debian, FreeBSD * Linux: Arch, Debian, Fedora, Ubuntu * Windows: x32 and x64 +* DOS: djgpp + +PLAYER SOUND SYSTEMS: + +* ALSA +* OSS +* OpenAL +* SB16 (DOS) BUILD FROM SOURCE: @@ -39,11 +47,32 @@ CHANGELOG * API change: WildMidi_OpenBuffer() and WildMidi_GetOutput() changed to accept strictly 32bit size parameters, i.e. uint32_t, instead of unsigned long. +* Support for loading XMI (XMIDI format) files, thanks Ryan Nunn for + releasing his code under the LGPL. +* support for loading MUS (MUS Id format) files, such as from Doom. +* DOS (DJGPP) port, player: Support for Sound Blaster output. * Build requires cmake-2.8.11 or newer now. +0.3.7 +* Plug a memory leak in case of broken midis. + +0.3.6 +* Fix some portability issues. +* Fix a double-free issue during library shutdown when several midis + were alive. +* Fix the invalid option checking in WildMidi_Init(). +* Fix the roundtempo option which had been broken since its invention + in 0.2.3.5 (WM_MO_ROUNDTEMPO: was 0xA000 instead of 0x2000.) +* Fix cfg files without a newline at the end weren't parsed correctly. +* Handle cfg files with mac line-endings. +* Refuse loading suspiciously long files. + 0.3.5 * Greatly reduced the heap usage (was a regression introduced in 0.2.3) -* OpenAL support: Fixed audio output on big-endian systems. +* OpenAL support: Fixed audio output on big-endian systems. Fixed audio + skips at song start. +* OSS support: No longer uses mmap mode for better compatibility. This + gains us NetBSD and OpenBSD support. * Worked around an invalid memory read found by valgrind when playing Beethoven's Fur Elise.rmi at 44100 Hz using the old MIDIA patch-set from 1994. diff --git a/cmake/FindALSA.cmake b/cmake/FindALSA.cmake deleted file mode 100644 index c1354abb..00000000 --- a/cmake/FindALSA.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# - Try to find ALSA -# Once done, this will define -# -# ALSA_FOUND - system has ALSA (GL and GLU) -# ALSA_INCLUDE_DIRS - the ALSA include directories -# ALSA_LIBRARIES - link these to use ALSA -# ALSA_GL_LIBRARY - only GL -# ALSA_GLU_LIBRARY - only GLU -# -# See documentation on how to write CMake scripts at -# http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries - -INCLUDE(LibFindMacros) -INCLUDE(CheckIncludeFiles) - -MESSAGE(STATUS "Looking for ALSA...") -FIND_PATH(ALSA_INCLUDE_DIR - NAMES alsa/version.h - PATHS ${ALSA_PKGCONF_INCLUDE_DIRS} -) -CHECK_INCLUDE_FILES(alsa/version.h HAVE_ALSA_H) - -FIND_LIBRARY(ALSA_LIBRARY - NAMES asound - PATHS ${ALSA_PKGCONF_LIBRARY_DIRS} -) - -# Extract the version number -IF(ALSA_INCLUDE_DIR) -FILE(READ "${ALSA_INCLUDE_DIR}/alsa/version.h" _ALSA_VERSION_H_CONTENTS) -STRING(REGEX REPLACE ".*#define SND_LIB_VERSION_STR[ \t]*\"([^\n]*)\".*" "\\1" ALSA_VERSION "${_ALSA_VERSION_H_CONTENTS}") -ENDIF(ALSA_INCLUDE_DIR) - -SET(ALSA_PROCESS_INCLUDES ALSA_INCLUDE_DIR) -SET(ALSA_PROCESS_LIBS ALSA_LIBRARY) -LIBFIND_PROCESS(ALSA) - diff --git a/cmake/FindOSS.cmake b/cmake/FindOSS.cmake index 45a4b46f..76c3c2f1 100644 --- a/cmake/FindOSS.cmake +++ b/cmake/FindOSS.cmake @@ -1,57 +1,67 @@ -# - Find Oss -# Find Oss headers and libraries. +# - Find OSS +# Find OSS headers and libraries. # -# OSS_INCLUDE_DIR - where to find soundcard.h, etc. -# OSS_FOUND - True if Oss found. +# OSS_INCLUDE_DIR - where to find soundcard.h, etc. +# OSS_LIBRARY - link library, if any, needed for OSS. +# OSS_FOUND - True if OSS found. - -INCLUDE(LibFindMacros) INCLUDE(CheckIncludeFiles) +INCLUDE(CheckCSourceCompiles) + +SET(OSS_LIBRARY "") +SET(OSS_INCLUDE_DIR) # system header must suffice +SET(OSS_FOUND) MESSAGE(STATUS "Looking for OSS...") -CHECK_INCLUDE_FILES(linux/soundcard.h HAVE_LINUX_SOUNDCARD_H) + +#CHECK_INCLUDE_FILES(linux/soundcard.h HAVE_LINUX_SOUNDCARD_H) # Linux does provide CHECK_INCLUDE_FILES(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) CHECK_INCLUDE_FILES(machine/soundcard.h HAVE_MACHINE_SOUNDCARD_H) +CHECK_INCLUDE_FILES(soundcard.h HAVE_SOUNDCARD_H) # less common, but exists. -FIND_PATH(LINUX_OSS_INCLUDE_DIR "linux/soundcard.h" - "/usr/include" "/usr/local/include" -) - -FIND_PATH(SYS_OSS_INCLUDE_DIR "sys/soundcard.h" - "/usr/include" "/usr/local/include" -) - -FIND_PATH(MACHINE_OSS_INCLUDE_DIR "machine/soundcard.h" - "/usr/include" "/usr/local/include" -) - -SET(OSS_FOUND FALSE) - -IF(LINUX_OSS_INCLUDE_DIR) - SET(OSS_FOUND TRUE) - SET(OSS_INCLUDE_DIR ${LINUX_OSS_INCLUDE_DIR}) +# NetBSD and OpenBSD uses ossaudio emulation layer, +# otherwise no link library is needed. +IF(CMAKE_SYSTEM_NAME MATCHES "kNetBSD.*|NetBSD.*|kOpenBSD.*|OpenBSD.*") # AND HAVE_SOUNDCARD_H ??? + FIND_LIBRARY(OSSAUDIO_LIBRARIES "ossaudio") + IF(OSSAUDIO_LIBRARIES STREQUAL "OSSAUDIO_LIBRARIES-NOTFOUND") + SET(OSSAUDIO_LIBRARIES) + ELSE() + MESSAGE(STATUS "Found libossaudio: ${OSSAUDIO_LIBRARIES}") + SET(OSS_LIBRARY ${OSSAUDIO_LIBRARIES}) + ENDIF() +ELSE() + SET(OSSAUDIO_LIBRARIES) ENDIF() -IF(SYS_OSS_INCLUDE_DIR) - SET(OSS_FOUND TRUE) - SET(OSS_INCLUDE_DIR ${SYS_OSS_INCLUDE_DIR}) +SET(OLD_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") +IF(OSSAUDIO_LIBRARIES) + SET(CMAKE_REQUIRED_LIBRARIES ${OSSAUDIO_LIBRARIES}) ENDIF() -IF(MACHINE_OSS_INCLUDE_DIR) - SET(OSS_FOUND TRUE) - SET(OSS_INCLUDE_DIR ${MACHINE_OSS_INCLUDE_DIR}) +IF(HAVE_SYS_SOUNDCARD_H) + CHECK_C_SOURCE_COMPILES("#include + #include + int main() {return SNDCTL_DSP_RESET;}" OSS_FOUND) +ELSEIF(HAVE_MACHINE_SOUNDCARD_H) + CHECK_C_SOURCE_COMPILES("#include + #include + int main() {return SNDCTL_DSP_RESET;}" OSS_FOUND) +ELSEIF(HAVE_SOUNDCARD_H) + CHECK_C_SOURCE_COMPILES("#include + #include + int main() {return SNDCTL_DSP_RESET;}" OSS_FOUND) ENDIF() +SET(CMAKE_REQUIRED_LIBRARIES "${OLD_REQUIRED_LIBRARIES}") + MARK_AS_ADVANCED ( OSS_FOUND OSS_INCLUDE_DIR - LINUX_OSS_INCLUDE_DIR - SYS_OSS_INCLUDE_DIR - MACHINE_OSS_INCLUDE_DIR -) + OSS_LIBRARY +) IF(OSS_FOUND) - MESSAGE(STATUS "Found OSS headers.") -ELSE(OSS_FOUND) - FATAL_ERROR(STATUS "Could not find OSS headers!") -ENDIF() \ No newline at end of file + MESSAGE(STATUS "Found OSS.") +ELSE() + MESSAGE(STATUS "Could not find OSS.") +ENDIF() diff --git a/cmake/LibFindMacros.cmake b/cmake/LibFindMacros.cmake deleted file mode 100644 index 69975c51..00000000 --- a/cmake/LibFindMacros.cmake +++ /dev/null @@ -1,99 +0,0 @@ -# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments -# used for the current package. For this to work, the first parameter must be the -# prefix of the current package, then the prefix of the new package etc, which are -# passed to find_package. -macro (libfind_package PREFIX) - set (LIBFIND_PACKAGE_ARGS ${ARGN}) - if (${PREFIX}_FIND_QUIETLY) - set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) - endif (${PREFIX}_FIND_QUIETLY) - if (${PREFIX}_FIND_REQUIRED) - set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) - endif (${PREFIX}_FIND_REQUIRED) - find_package(${LIBFIND_PACKAGE_ARGS}) -endmacro (libfind_package) - -# CMake developers made the UsePkgConfig system deprecated in the same release (2.6) -# where they added pkg_check_modules. Consequently I need to support both in my scripts -# to avoid those deprecated warnings. Here's a helper that does just that. -# Works identically to pkg_check_modules, except that no checks are needed prior to use. -macro (libfind_pkg_check_modules PREFIX PKGNAME) - if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) - include(UsePkgConfig) - pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) - else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(${PREFIX} ${PKGNAME}) - endif (PKG_CONFIG_FOUND) - endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) -endmacro (libfind_pkg_check_modules) - -# Do the final processing once the paths have been detected. -# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain -# all the variables, each of which contain one include directory. -# Ditto for ${PREFIX}_PROCESS_LIBS and library files. -# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. -# Also handles errors in case library detection was required, etc. -macro (libfind_process PREFIX) - # Skip processing if already processed during this run - if (NOT ${PREFIX}_FOUND) - # Start with the assumption that the library was found - set (${PREFIX}_FOUND TRUE) - - # Process all includes and set _FOUND to false if any are missing - foreach (i ${${PREFIX}_PROCESS_INCLUDES}) - if (${i}) - set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) - mark_as_advanced(${i}) - else (${i}) - set (${PREFIX}_FOUND FALSE) - endif (${i}) - endforeach (i) - - # Process all libraries and set _FOUND to false if any are missing - foreach (i ${${PREFIX}_PROCESS_LIBS}) - if (${i}) - set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) - mark_as_advanced(${i}) - else (${i}) - set (${PREFIX}_FOUND FALSE) - endif (${i}) - endforeach (i) - - # Print message and/or exit on fatal error - if (${PREFIX}_FOUND) - if (NOT ${PREFIX}_FIND_QUIETLY) - message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") - endif (NOT ${PREFIX}_FIND_QUIETLY) - else (${PREFIX}_FOUND) - if (${PREFIX}_FIND_REQUIRED) - foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) - message("${i}=${${i}}") - endforeach (i) - message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") - endif (${PREFIX}_FIND_REQUIRED) - endif (${PREFIX}_FOUND) - endif (NOT ${PREFIX}_FOUND) -endmacro (libfind_process) - -macro(libfind_library PREFIX basename) - set(TMP "") - if(MSVC80) - set(TMP -vc80) - endif(MSVC80) - if(MSVC90) - set(TMP -vc90) - endif(MSVC90) - set(${PREFIX}_LIBNAMES ${basename}${TMP}) - if(${ARGC} GREATER 2) - set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) - string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) - set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) - endif(${ARGC} GREATER 2) - find_library(${PREFIX}_LIBRARY - NAMES ${${PREFIX}_LIBNAMES} - PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} - ) -endmacro(libfind_library) - diff --git a/cmake/Toolchain-MinGW32.cmake b/cmake/Toolchain-MinGW32.cmake new file mode 100644 index 00000000..ff877aa4 --- /dev/null +++ b/cmake/Toolchain-MinGW32.cmake @@ -0,0 +1,19 @@ +# toolchain file I use to cross compile on Linux +# targetting Windows (x86/mingw). running: +# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Toolchain-MinGW32.cmake .... + +SET(CMAKE_SYSTEM_NAME Windows) + +SET(CMAKE_C_COMPILER /usr/local/cross-win32/bin/i686-pc-mingw32-gcc) +SET(CMAKE_CXX_COMPILER /usr/local/cross-win32/bin/i686-pc-mingw32-g++) +SET(CMAKE_RC_COMPILER /usr/local/cross-win32/bin/i686-pc-mingw32-windres) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /usr/local/cross-win32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/Toolchain-MinGW64.cmake b/cmake/Toolchain-MinGW64.cmake new file mode 100644 index 00000000..9de51c6f --- /dev/null +++ b/cmake/Toolchain-MinGW64.cmake @@ -0,0 +1,19 @@ +# toolchain file I use to cross compile on Linux +# targetting Windows (x64/mingw-w64). running: +# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Toolchain-MinGW64.cmake .... + +SET(CMAKE_SYSTEM_NAME Windows) + +SET(CMAKE_C_COMPILER /opt/cross_win64/bin/x86_64-w64-mingw32-gcc) +SET(CMAKE_CXX_COMPILER /opt/cross_win64/bin/x86_64-w64-mingw32-g++) +SET(CMAKE_RC_COMPILER /opt/cross_win64/bin/x86_64-w64-mingw32-windres) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /opt/cross_win64 /opt/cross_win64/x86_64-w64-mingw32) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/Toolchain-OSX-ppc.cmake b/cmake/Toolchain-OSX-ppc.cmake new file mode 100644 index 00000000..c10a3442 --- /dev/null +++ b/cmake/Toolchain-OSX-ppc.cmake @@ -0,0 +1,18 @@ +# toolchain file I use to cross compile on Linux +# targetting OSX/Darwin (powerpc). running: +# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Toolchain-OSX-ppc.cmake .... + +SET(CMAKE_SYSTEM_NAME Darwin) + +SET(CMAKE_C_COMPILER /opt/cross_osx-ppc/bin/powerpc-apple-darwin9-gcc) +SET(CMAKE_CXX_COMPILER /opt/cross_osx-ppc/bin/powerpc-apple-darwin9-g++) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /opt/cross_osx-ppc) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/Toolchain-OSX-x86.cmake b/cmake/Toolchain-OSX-x86.cmake new file mode 100644 index 00000000..b0c0290d --- /dev/null +++ b/cmake/Toolchain-OSX-x86.cmake @@ -0,0 +1,18 @@ +# toolchain file I use to cross compile on Linux +# targetting OSX/Darwin (x86). running: +# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Toolchain-OSX-x86.cmake .... + +SET(CMAKE_SYSTEM_NAME Darwin) + +SET(CMAKE_C_COMPILER /opt/cross_osx-x86/bin/i686-apple-darwin9-gcc) +SET(CMAKE_CXX_COMPILER /opt/cross_osx-x86/bin/i686-apple-darwin9-g++) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /opt/cross_osx-x86) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/Toolchain-OSX-x86_64.cmake b/cmake/Toolchain-OSX-x86_64.cmake new file mode 100644 index 00000000..a620247f --- /dev/null +++ b/cmake/Toolchain-OSX-x86_64.cmake @@ -0,0 +1,18 @@ +# toolchain file I use to cross compile on Linux +# targetting OSX/Darwin (x86_64). running: +# cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Toolchain-OSX-x86_64.cmake .... + +SET(CMAKE_SYSTEM_NAME Darwin) + +SET(CMAKE_C_COMPILER /opt/cross_osx-x86_64/usr/bin/x86_64-apple-darwin9-gcc) +SET(CMAKE_CXX_COMPILER /opt/cross_osx-x86_64/usr/bin/x86_64-apple-darwin9-g++) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /opt/cross_osx-x86_64) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/djgpp/Makefile b/djgpp/Makefile index 1179ad42..742780a3 100644 --- a/djgpp/Makefile +++ b/djgpp/Makefile @@ -27,23 +27,31 @@ INCLUDE = -I. -I../include/stdint -I../include CFLAGS = $(INCLUDE) -Wall -W ARFLAGS = cr +# SB output support (comment out if not wanted) +CFLAGS+= -DAUDIODRV_DOSSB +SB_OBJ = dosirq.o dosdma.o dossb.o + LD = $(CC) LDFLAGS = -L. -lWildMidi -lm +# build for pentium and newer (comment out if not wanted) +CFLAGS += -march=i586 ifeq ($(DEBUG),1) CFLAGS += -g else -CFLAGS += -O2 +CFLAGS += -O2 -fomit-frame-pointer -ffast-math LDFLAGS+= -s endif # Build rules +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< %.o: ../src/%.c $(CC) -c $(CFLAGS) -o $@ $< # Objects -LIB_OBJ= wm_error.o file_io.o lock.o wildmidi_lib.o reverb.o gus_pat.o -PLAYER_OBJ= getopt.o getopt_long.o wildmidi.o +LIB_OBJ= wm_error.o file_io.o lock.o wildmidi_lib.o reverb.o gus_pat.o xmidi.o mus.o +PLAYER_OBJ= $(SB_OBJ) getopt_long.o wm_tty.o wildmidi.o # Build targets TARGETS = libWildMidi.a wildmidi.exe diff --git a/djgpp/README b/djgpp/README index f399c133..90b70b3c 100644 --- a/djgpp/README +++ b/djgpp/README @@ -10,13 +10,22 @@ Cd in to the djgpp directory and run 'make', i.e.: cd djgpp make -.. which will generate libWildMidi.a and wildmidi.exe. +.. which will generate libWildMidi.a and wildmidi.exe. If necessary, +edit the Makefile to meet your needs/environment. -The library part (libWildMidi.a) is fully functional. The player -part (wildmidi.exe) only outputs a wav file. Run like: +The player (wildmidi.exe) can either output to a Sound Blaster (SB1, +SB2, SBPro, SB16, or compatible) hardware: + + wildmidi mymidi.mid + +... or, it can generate a wav file: wildmidi -o output.wav mymidi.mid -An actual sound output functionality might be added later. +Use -r to specify a sample rate, use -c to +point to your wildmidi.cfg config file: + + wildmidi -c c:\wildmidi.cfg -r 22050 mymidi.mid +Run "wildmidi -h" to see other command line switches. diff --git a/djgpp/config.h b/djgpp/config.h index e4c5817e..b7bc8b6d 100644 --- a/djgpp/config.h +++ b/djgpp/config.h @@ -10,3 +10,7 @@ #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR >= 96) #define HAVE___BUILTIN_EXPECT #endif +#ifndef HAVE___BUILTIN_EXPECT +#define __builtin_expect(x,c) x +#endif + diff --git a/djgpp/dosdma.c b/djgpp/dosdma.c new file mode 100644 index 00000000..7ad77380 --- /dev/null +++ b/djgpp/dosdma.c @@ -0,0 +1,213 @@ +/* Implementation of DMA routines on DOS - from libMikMod. + Copyright (C) 1999 by Andrew Zabolotny, + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#include "dosdma.h" + +#include /* includes sys/version.h (djgpp >= 2.02) */ +#include +#include +#include +#include + +/* BUG WARNING: there is an error in DJGPP libraries <= 2.01: + * src/libc/dpmi/api/d0102.s loads the selector and allocsize + * arguments in the wrong order. DJGPP >= 2.02 have it fixed. */ +#if !defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__-0 < 2) +#warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken! +#endif + +__dma_regs dma[8] = { +/* *INDENT-OFF* */ + {DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + {DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + + {DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + {DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3, + DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG}, + + {DMA_ADDR_4, 0, DMA_SIZE_4, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + {DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + + {DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}, + {DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7, + DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG} +/* *INDENT-ON* */ +}; + +static int __initialized = 0; +static int __buffer_count = 0; +static __dpmi_meminfo __locked_data; + +int dma_initialize() +{ + if (!__djgpp_nearptr_enable()) + return 0; + + /* Trick: Avoid re-setting DS selector limit on each memory allocation + call */ + __djgpp_selector_limit = 0xffffffff; + + __locked_data.address = __djgpp_base_address + (unsigned long)&dma; + __locked_data.size = sizeof(dma); + if (__dpmi_lock_linear_region(&__locked_data)) + return 0; + + return (__initialized = 1); +} + +void dma_finalize() +{ + if (!__initialized) + return; + __dpmi_unlock_linear_region(&__locked_data); + __djgpp_nearptr_disable(); +} + +dma_buffer *dma_allocate(unsigned int channel, unsigned int size) +{ + int parsize = (size + 15) >> 4; /* size in paragraphs */ + int par = 0; /* Real-mode paragraph */ + int selector = 0; /* Protected-mode selector */ + int mask = channel <= 3 ? 0xfff : 0x1fff; /* Alignment mask in para. */ + int allocsize = parsize; /* Allocated size in paragraphs */ + int count; /* Try count */ + int bound = 0; /* Nearest bound address */ + int maxsize; /* Maximal possible block size */ + dma_buffer *buffer = NULL; + __dpmi_meminfo buff_info, struct_info; + + if (!dma_initialize()) + return NULL; + + /* Loop until we'll get a properly aligned memory block */ + for (count = 8; count; count--) { + int resize = (selector != 0); + + /* Try first to resize (possibly previously) allocated block */ + if (resize) { + maxsize = (bound + parsize) - par; + if (maxsize > parsize * 2) + maxsize = parsize * 2; + if (maxsize == allocsize) + resize = 0; + else { + allocsize = maxsize; + if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) != + 0) resize = 0; + } + } + + if (!resize) { + if (selector) + __dpmi_free_dos_memory(selector), selector = 0; + par = __dpmi_allocate_dos_memory(allocsize, &selector); + } + + if ((par == 0) || (par == -1)) + goto exit; + + /* If memory block contains a properly aligned portion, quit loop */ + bound = (par + mask + 1) & ~mask; + if (par + parsize <= bound) + break; + if (bound + parsize <= par + allocsize) { + par = bound; + break; + } + } + if (!count) { + __dpmi_free_dos_memory(selector); + goto exit; + } + + buffer = (dma_buffer *) malloc(sizeof(dma_buffer)); + buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16); + buffer->physical = bound * 16; + buffer->size = parsize * 16; + buffer->selector = selector; + buffer->channel = channel; + + buff_info.address = buffer->physical; + buff_info.size = buffer->size; + /* + Don't pay attention to return code since under plain DOS it often + returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI) + */ + __dpmi_lock_linear_region(&buff_info); + + /* Lock the DMA buffer control structure as well */ + struct_info.address = __djgpp_base_address + (unsigned long)buffer; + struct_info.size = sizeof(dma_buffer); + if (__dpmi_lock_linear_region(&struct_info)) { + __dpmi_unlock_linear_region(&buff_info); + __dpmi_free_dos_memory(selector); + free(buffer); + buffer = NULL; + goto exit; + } + + exit: + if (buffer) + __buffer_count++; + else if (--__buffer_count == 0) + dma_finalize(); + return buffer; +} + +void dma_free(dma_buffer * buffer) +{ + __dpmi_meminfo buff_info; + + if (!buffer) + return; + + buff_info.address = buffer->physical; + buff_info.size = buffer->size; + __dpmi_unlock_linear_region(&buff_info); + + __dpmi_free_dos_memory(buffer->selector); + free(buffer); + + if (--__buffer_count == 0) + dma_finalize(); +} + +void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode) +{ + /* Disable interrupts */ + int old_ints = disable(); + dma_disable(buffer->channel); + dma_set_mode(buffer->channel, mode); + dma_clear_ff(buffer->channel); + dma_set_addr(buffer->channel, buffer->physical); + dma_clear_ff(buffer->channel); + dma_set_count(buffer->channel, count); + dma_enable(buffer->channel); + /* Re-enable interrupts */ + if (old_ints) + enable(); +} diff --git a/djgpp/dosdma.h b/djgpp/dosdma.h new file mode 100644 index 00000000..d467fa0b --- /dev/null +++ b/djgpp/dosdma.h @@ -0,0 +1,190 @@ +/* Interface for DMA routines on DOS -- from libMikMod. + Copyright (C) 1999 by Andrew Zabolotny, + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __DOSDMA_H__ +#define __DOSDMA_H__ + +#include + +#define DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_ADDR_0 0x00 /* DMA address registers */ +#define DMA_ADDR_1 0x02 +#define DMA_ADDR_2 0x04 +#define DMA_ADDR_3 0x06 +#define DMA_ADDR_4 0xC0 +#define DMA_ADDR_5 0xC4 +#define DMA_ADDR_6 0xC8 +#define DMA_ADDR_7 0xCC + +#define DMA_SIZE_0 0x01 /* DMA transfer size registers */ +#define DMA_SIZE_1 0x03 +#define DMA_SIZE_2 0x05 +#define DMA_SIZE_3 0x07 +#define DMA_SIZE_4 0xC2 +#define DMA_SIZE_5 0xC6 +#define DMA_SIZE_6 0xCA +#define DMA_SIZE_7 0xCE + +#define DMA_PAGE_0 0x87 /* DMA page registers */ +#define DMA_PAGE_1 0x83 +#define DMA_PAGE_2 0x81 +#define DMA_PAGE_3 0x82 +#define DMA_PAGE_5 0x8B +#define DMA_PAGE_6 0x89 +#define DMA_PAGE_7 0x8A + +#define DMA_MODE_AUTOINIT 0x10 /* Auto-init mode bit */ +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +/* Indexable specific DMA registers */ +typedef struct __dma_regs_s { + unsigned char addr; /* DMA transfer address register */ + unsigned char page; /* DMA page register */ + unsigned char size; /* DMA transfer size register */ + unsigned char mask; /* DMA mask/unmask register */ + unsigned char flip; /* DMA flip-flop reset register */ + unsigned char mode; /* DMA mode register */ +} __dma_regs; + +extern __dma_regs dma[8]; + +/* Enable a specific DMA channel */ +static inline void dma_enable(unsigned int channel) +{ + outportb(dma[channel].mask, channel & 3); +} + +/* Disable a specific DMA channel */ +static inline void dma_disable(unsigned int channel) +{ + outportb(dma[channel].mask, (channel & 3) | 0x04); +} + +/* Clear the 'DMA Flip Flop' flag */ +static inline void dma_clear_ff(unsigned int channel) +{ + outportb(dma[channel].flip, 0); +} + +/* Set mode for a specific DMA channel */ +static inline void dma_set_mode(unsigned int channel, char mode) +{ + outportb(dma[channel].mode, mode | (channel & 3)); +} + +/* Set DMA page register */ +static inline void dma_set_page(unsigned int channel, char page) +{ + if (channel > 3) + page &= 0xfe; + outportb(dma[channel].page, page); +} + +/* + Set transfer address & page bits for specific DMA channel. + Assumes dma flipflop is clear. +*/ +static inline void dma_set_addr(unsigned int channel, unsigned int address) +{ + unsigned char dma_reg = dma[channel].addr; + dma_set_page(channel, address >> 16); + if (channel <= 3) { + outportb(dma_reg, (address) & 0xff); + outportb(dma_reg, (address >> 8) & 0xff); + } else { + outportb(dma_reg, (address >> 1) & 0xff); + outportb(dma_reg, (address >> 9) & 0xff); + } +} + +/* + Set transfer size for a specific DMA channel. + Assumes dma flip-flop is clear. +*/ +static inline void dma_set_count(unsigned int channel, unsigned int count) +{ + unsigned char dma_reg = dma[channel].size; + count--; /* number of DMA transfers is bigger by one */ + if (channel > 3) + count >>= 1; + outportb(dma_reg, (count) & 0xff); + outportb(dma_reg, (count >> 8) & 0xff); +} + +/* + Query the number of bytes left to transfer. + Assumes DMA flip-flop is clear. +*/ +static inline int dma_get_count(unsigned int channel) +{ + unsigned char dma_reg = dma[channel].size; + + /* using short to get 16-bit wrap around */ + unsigned short count; + count = inportb(dma_reg); + count |= inportb(dma_reg) << 8; + count++; + return (channel <= 3) ? count : (count << 1); +} + +typedef struct dma_buffer_s { + unsigned char *linear; /* Linear address */ + unsigned long physical; /* Physical address */ + unsigned long size; /* Buffer size */ + unsigned short selector; /* The selector assigned to this memory */ + unsigned char channel; /* The DMA channel */ +} dma_buffer; + +/* Allocate a block of memory suitable for using as a DMA buffer */ +extern dma_buffer *dma_allocate(unsigned int channel, unsigned int size); +/* Deallocate a DMA buffer */ +extern void dma_free(dma_buffer * buffer); +/* Start DMA transfer to or from given buffer */ +extern void dma_start(dma_buffer * buffer, unsigned long count, + unsigned char mode); + +#endif /* __DOSDMA_H__ */ diff --git a/djgpp/dosirq.c b/djgpp/dosirq.c new file mode 100644 index 00000000..5262dda6 --- /dev/null +++ b/djgpp/dosirq.c @@ -0,0 +1,322 @@ +/* Implementation of IRQ routines on DOS -- from libMikMod. + Copyright (C) 1999 by Andrew Zabolotny, + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#include "dosirq.h" + +#include +#include +#include +#include +#include +#include + +unsigned int __irq_stack_size = 0x4000; +unsigned int __irq_stack_count = 1; + +static void __int_stub_template (void) +{ +/* *INDENT-OFF* */ + asm(" pushal\n" + " pushl %ds\n" + " pushl %es\n" + " pushl %fs\n" + " pushl %gs\n" + " movw $0x1234,%ax\n" /* Get DPMI data selector */ + " movw %ax,%ds\n" /* Set DS and ES to data selector */ + " movw %ax,%es\n" + " movl $0x12345678,%ebx\n" /* Interrupt stack top */ + " movl (%ebx),%ecx\n" + " movl %ecx,%edx\n" + " subl $0x12345678,%ecx\n" /* Subtract irq_stack_count */ + " movl %ecx,(%ebx)\n" + " movw %ss,%si\n" /* Save old SS:ESP */ + " movl %esp,%edi\n" + " movl %edx,%esp\n" /* Set SS:ESP to interrupt stack */ + " movw %ax,%ss\n" + " pushl %esi\n" + " pushl %edi\n" + " pushl %ebx\n" + " pushl %edx\n" + " call 1f\n" /* Call user interrupt handler */ + "1: popl %edx\n" + " popl %ebx\n" + " movl %edx,(%ebx)\n" + " popl %edi\n" + " popl %esi\n" + " movl %edi,%esp\n" /* Restore old SS:ESP */ + " movw %si,%ss\n" + " popl %gs\n" + " popl %fs\n" + " popl %es\n" + " popl %ds\n" + " popal\n" + " iret\n"); +/* *INDENT-ON* */ +} + +#include + +static int _allocate_iret_wrapper(_go32_dpmi_seginfo * info) +{ + unsigned char *irqtpl = (unsigned char *)__int_stub_template; + unsigned char *irqend, *irqwrapper, *tmp; + __dpmi_meminfo handler_info; + unsigned int wrappersize; + + /* First, skip until pushal */ + while (*irqtpl != 0x60) + irqtpl++; + /* Now find the iret */ + irqend = irqtpl; + while (*irqend++ != 0xcf); + + wrappersize = 4 + __irq_stack_size * __irq_stack_count + 4 + + ((long)irqend - (long)irqtpl); + irqwrapper = (unsigned char *) malloc(wrappersize); + /* Lock the wrapper */ + handler_info.address = __djgpp_base_address + (unsigned long)irqwrapper; + handler_info.size = wrappersize; + if (__dpmi_lock_linear_region(&handler_info)) { + free(irqwrapper); + return -1; + } + + /* First comes the interrupt wrapper size */ + *(unsigned long *)irqwrapper = wrappersize; + + /* Next comes the interrupt stack */ + tmp = irqwrapper + 4 + __irq_stack_size * __irq_stack_count; + + /* The following dword is interrupt stack pointer */ + *((void **)tmp) = tmp; + tmp += 4; + + /* Now comes the interrupt wrapper itself */ + memcpy(tmp, irqtpl, irqend - irqtpl); + *(unsigned short *)(tmp + 9) = _my_ds(); + *(unsigned long *)(tmp + 16) = (unsigned long)tmp - 4; + *(unsigned long *)(tmp + 26) = __irq_stack_size; + *(unsigned long *)(tmp + 46) = + info->pm_offset - (unsigned long)(tmp + 50); + + info->pm_offset = (unsigned long)tmp; + info->pm_selector = _my_cs(); + + return 0; +} + +static void _free_iret_wrapper(_go32_dpmi_seginfo * info) +{ + __dpmi_meminfo handler_info; + + info->pm_offset -= 4 + __irq_stack_size * __irq_stack_count + 4; + + handler_info.address = __djgpp_base_address + info->pm_offset; + handler_info.size = *(unsigned long *)info->pm_offset; + __dpmi_unlock_linear_region(&handler_info); + + free((void *)info->pm_offset); +} + +struct irq_handle *irq_hook(int irqno, void (*handler)(), unsigned long size) +{ + int interrupt; + struct irq_handle *irq; + __dpmi_version_ret version; + __dpmi_meminfo handler_info, struct_info; + _go32_dpmi_seginfo info; + unsigned long old_sel, old_ofs; + + __dpmi_get_version(&version); + if (irqno < 8) + interrupt = version.master_pic + irqno; + else + interrupt = version.slave_pic + (irqno - 8); + + if (_go32_dpmi_get_protected_mode_interrupt_vector(interrupt, &info)) + return NULL; + + old_sel = info.pm_selector; + old_ofs = info.pm_offset; + + info.pm_offset = (unsigned long)handler; + if (_allocate_iret_wrapper(&info)) + return NULL; + + /* Lock the interrupt handler in memory */ + handler_info.address = __djgpp_base_address + (unsigned long)handler; + handler_info.size = size; + if (__dpmi_lock_linear_region(&handler_info)) { + _free_iret_wrapper(&info); + return NULL; + } + + irq = (struct irq_handle *) malloc(sizeof(struct irq_handle)); + irq->c_handler = handler; + irq->handler_size = size; + irq->handler = info.pm_offset; + irq->prev_selector = old_sel; + irq->prev_offset = old_ofs; + irq->int_num = interrupt; + irq->irq_num = irqno; + irq->pic_base = irqno < 8 ? PIC1_BASE : PIC2_BASE; + + struct_info.address = __djgpp_base_address + (unsigned long)irq; + struct_info.size = sizeof(struct irq_handle); + if (__dpmi_lock_linear_region(&struct_info)) { + free(irq); + __dpmi_unlock_linear_region(&handler_info); + _free_iret_wrapper(&info); + return NULL; + } + + _go32_dpmi_set_protected_mode_interrupt_vector(interrupt, &info); + + irq->pic_mask = irq_state(irq); + return irq; +} + +void irq_unhook(struct irq_handle *irq) +{ + _go32_dpmi_seginfo info; + __dpmi_meminfo mem_info; + + if (!irq) + return; + + /* Restore the interrupt vector */ + irq_disable(irq); + info.pm_offset = irq->prev_offset; + info.pm_selector = irq->prev_selector; + _go32_dpmi_set_protected_mode_interrupt_vector(irq->int_num, &info); + + /* Unlock the interrupt handler */ + mem_info.address = __djgpp_base_address + (unsigned long)irq->c_handler; + mem_info.size = irq->handler_size; + __dpmi_unlock_linear_region(&mem_info); + + /* Unlock the irq_handle structure */ + mem_info.address = __djgpp_base_address + (unsigned long)irq; + mem_info.size = sizeof(struct irq_handle); + __dpmi_unlock_linear_region(&mem_info); + + info.pm_offset = irq->handler; + _free_iret_wrapper(&info); + + /* If IRQ was enabled before we hooked, restore enabled state */ + if (irq->pic_mask) + irq_enable(irq); + else + irq_disable(irq); + + free(irq); +} + +/*---------------------------------------------- IRQ detection mechanism -----*/ +static struct irq_handle *__irqs[16]; +static int (*__irq_confirm) (int irqno); +static volatile unsigned int __irq_mask; +static volatile unsigned int __irq_count[16]; + +#define DECLARE_IRQ_HANDLER(irqno) \ +static void __irq##irqno##_handler () \ +{ \ + if (irq_check (__irqs [irqno]) && __irq_confirm (irqno)) \ + { \ + __irq_count [irqno]++; \ + __irq_mask |= (1 << irqno); \ + } \ + irq_ack (__irqs [irqno]); \ +} + +/* *INDENT-OFF* */ +DECLARE_IRQ_HANDLER(0) +DECLARE_IRQ_HANDLER(1) +DECLARE_IRQ_HANDLER(2) +DECLARE_IRQ_HANDLER(3) +DECLARE_IRQ_HANDLER(4) +DECLARE_IRQ_HANDLER(5) +DECLARE_IRQ_HANDLER(6) +DECLARE_IRQ_HANDLER(7) +DECLARE_IRQ_HANDLER(8) +DECLARE_IRQ_HANDLER(9) +DECLARE_IRQ_HANDLER(10) +DECLARE_IRQ_HANDLER(11) +DECLARE_IRQ_HANDLER(12) +DECLARE_IRQ_HANDLER(13) +DECLARE_IRQ_HANDLER(14) +DECLARE_IRQ_HANDLER(15) +/* *INDENT-ON* */ + +static void (*__irq_handlers[16]) () = { + __irq0_handler, __irq1_handler, __irq2_handler, __irq3_handler, + __irq4_handler, __irq5_handler, __irq6_handler, __irq7_handler, + __irq8_handler, __irq9_handler, __irq10_handler, __irq11_handler, + __irq12_handler, __irq13_handler, __irq14_handler, __irq15_handler}; + +void irq_detect_start(unsigned int irqs, int (*irq_confirm) (int irqno)) +{ + int i; + + __irq_mask = 0; + __irq_confirm = irq_confirm; + memset(&__irqs, 0, sizeof(__irqs)); + memset((void *) &__irq_count, 0, sizeof(__irq_count)); + + /* Hook all specified IRQs */ + for (i = 1; i <= 15; i++) + if (irqs & (1 << i)) { + __irqs[i] = irq_hook(i, __irq_handlers[i], 200); + /* Enable the interrupt */ + irq_enable(__irqs[i]); + } + /* Enable IRQ2 if we need at least one IRQ above 7 */ + if (irqs & 0xff00) + _irq_enable(2); +} + +void irq_detect_end() +{ + int i; + for (i = 15; i >= 1; i--) + if (__irqs[i]) + irq_unhook(__irqs[i]); +} + +int irq_detect_get(int irqno, unsigned int *irqmask) +{ + int oldirq = disable(); + int count = __irq_count[irqno]; + *irqmask = __irq_mask; + __irq_mask = 0; + if (oldirq) + enable(); + return count; +} + +void irq_detect_clear() +{ + int oldirq = disable(); + memset((void *) &__irq_count, 0, sizeof(__irq_count)); + __irq_mask = 0; + if (oldirq) + enable(); +} diff --git a/djgpp/dosirq.h b/djgpp/dosirq.h new file mode 100644 index 00000000..f7af5282 --- /dev/null +++ b/djgpp/dosirq.h @@ -0,0 +1,122 @@ +/* Interface for IRQ routines on DOS -- from libMikMod. + Copyright (C) 1999 by Andrew Zabolotny, + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __DOSIRQ_H__ +#define __DOSIRQ_H__ + +#include + +#define PIC1_BASE 0x20 /* PIC1 base */ +#define PIC2_BASE 0xA0 /* PIC2 base */ + +struct irq_handle { + void (*c_handler) (); /* The real interrupt handler */ + unsigned long handler_size; /* The size of interrupt handler */ + unsigned long handler; /* Interrupt wrapper address */ + unsigned long prev_selector; /* Selector of previous handler */ + unsigned long prev_offset; /* Offset of previous handler */ + unsigned char irq_num; /* IRQ number */ + unsigned char int_num; /* Interrupt number */ + unsigned char pic_base; /* PIC base (0x20 or 0xA0) */ + unsigned char pic_mask; /* Old PIC mask state */ +}; + +/* Return the enabled state for specific IRQ */ +static inline unsigned char irq_state(struct irq_handle * irq) +{ + return ((~inportb(irq->pic_base + 1)) & (0x01 << (irq->irq_num & 7))); +} + +/* Acknowledge the end of interrupt */ +static inline void _irq_ack(int irqno) +{ + outportb(irqno > 7 ? PIC2_BASE : PIC1_BASE, 0x60 | (irqno & 7)); + /* For second controller we also should acknowledge first controller */ + if (irqno > 7) + outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */ +} + +/* Acknowledge the end of interrupt */ +static inline void irq_ack(struct irq_handle * irq) +{ + outportb(irq->pic_base, 0x60 | (irq->irq_num & 7)); + /* For second controller we also should acknowledge first controller */ + if (irq->pic_base != PIC1_BASE) + outportb(PIC1_BASE, 0x20); /* 0x20, 0x62? */ +} + +/* Mask (disable) the particular IRQ given his ordinal */ +static inline void _irq_disable(int irqno) +{ + unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1; + outportb(port_no, inportb(port_no) | (1 << (irqno & 7))); +} + +/* Unmask (enable) the particular IRQ given its ordinal */ +static inline void _irq_enable(int irqno) +{ + unsigned int port_no = (irqno < 8 ? PIC1_BASE : PIC2_BASE) + 1; + outportb(port_no, inportb(port_no) & ~(1 << (irqno & 7))); +} + +/* Mask (disable) the particular IRQ given its irq_handle structure */ +static inline void irq_disable(struct irq_handle * irq) +{ + outportb(irq->pic_base + 1, + inportb(irq->pic_base + 1) | (1 << (irq->irq_num & 7))); +} + +/* Unmask (enable) the particular IRQ given its irq_handle structure */ +static inline void irq_enable(struct irq_handle * irq) +{ + outportb(irq->pic_base + 1, + inportb(irq->pic_base + 1) & ~(1 << (irq->irq_num & 7))); +} + +/* Check if a specific IRQ is pending: return 0 is no */ +static inline int irq_check(struct irq_handle * irq) +{ + outportb(irq->pic_base, 0x0B); /* Read IRR vector */ + return (inportb(irq->pic_base) & (1 << (irq->irq_num & 7))); +} + +/* Hook a specific IRQ; NOTE: IRQ is disabled upon return, irq_enable() it */ +extern struct irq_handle *irq_hook(int irqno, void (*handler)(), + unsigned long size); +/* Unhook a previously hooked IRQ */ +extern void irq_unhook(struct irq_handle * irq); +/* Start IRQ detection process (IRQ list is given with irq mask) */ +/* irq_confirm should return "1" if the IRQ really comes from the device */ +extern void irq_detect_start(unsigned int irqs, + int (*irq_confirm) (int irqno)); +/* Finish IRQ detection process */ +extern void irq_detect_end(); +/* Get the count of specific irqno that happened */ +extern int irq_detect_get(int irqno, unsigned int *irqmask); +/* Clear IRQ counters */ +extern void irq_detect_clear(); + +/* The size of interrupt stack */ +extern unsigned int __irq_stack_size; +/* The number of nested interrupts that can be handled */ +extern unsigned int __irq_stack_count; + +#endif /* __DOSIRQ_H__ */ diff --git a/djgpp/dossb.c b/djgpp/dossb.c new file mode 100644 index 00000000..b979845f --- /dev/null +++ b/djgpp/dossb.c @@ -0,0 +1,561 @@ +/* Sound Blaster I/O routines, common for SB8, SBPro and SB16 -- + from libMikMod. Written by Andrew Zabolotny + Further bug fixes by O. Sezer + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "dossb.h" + +/********************************************* Private variables/routines *****/ + +__sb_state sb; + +/* Wait for SoundBlaster for some time */ +#if (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ == 0) +# define _func_noinline volatile /* match original code */ +# define _func_noclone +#else +/* avoid warnings from newer gcc: + * "function definition has qualified void return type" and + * function return types not compatible due to 'volatile' */ +# define _func_noinline __attribute__((__noinline__)) +# if (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) +# define _func_noclone +# else +# define _func_noclone __attribute__((__noclone__)) +# endif +#endif +_func_noinline +_func_noclone + void __sb_wait() +{ + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); + inportb(SB_DSP_RESET); +} + +static void sb_irq() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(sb.irq_handle)) + return; + + sb.irqcount++; + + /* Acknowledge DMA transfer is complete */ + if (sb.mode & SBMODE_16BITS) + __sb_dsp_ack_dma16(); + else + __sb_dsp_ack_dma8(); + + /* SoundBlaster 1.x cannot do autoinit ... */ + if (sb.dspver < SBVER_20) + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, (sb.dma_buff->size >> 1) - 1); + + /* Send EOI */ + irq_ack(sb.irq_handle); + + enable(); + if (sb.timer_callback) + sb.timer_callback(); +} + +static void sb_irq_end() +{ +} + +static boolean __sb_reset() +{ + /* Disable the output */ + sb_output(FALSE); + + /* Clear pending ints if any */ + __sb_dsp_ack_dma8(); + __sb_dsp_ack_dma16(); + + /* Reset the DSP */ + outportb(SB_DSP_RESET, SBM_DSP_RESET); + __sb_wait(); + __sb_wait(); + outportb(SB_DSP_RESET, 0); + + /* Now wait for AA coming from datain port */ + if (__sb_dsp_in() != 0xaa) + return FALSE; + + /* Finally, get the DSP version */ + if ((sb.dspver = __sb_dsp_version()) == 0xffff) + return FALSE; + /* Check again */ + if (sb.dspver != __sb_dsp_version()) + return FALSE; + + return TRUE; +} + +/***************************************************** SB detection stuff *****/ + +static int __sb_irq_irqdetect(int irqno) +{ + (void)irqno; + __sb_dsp_ack_dma8(); + return 1; +} + +static void __sb_irq_dmadetect() +{ + /* Make sure its not a spurious IRQ */ + if (!irq_check(sb.irq_handle)) + return; + + sb.irqcount++; + + /* Acknowledge DMA transfer is complete */ + if (sb.mode & SBMODE_16BITS) + __sb_dsp_ack_dma16(); + else + __sb_dsp_ack_dma8(); + + /* Send EOI */ + irq_ack(sb.irq_handle); +} + +static boolean __sb_detect() +{ + /* First find the port number */ + if (!sb.port) { + int i; + for (i = 5; i >= 0; i--) { + sb.port = 0x210 + i * 0x10; + if (__sb_reset()) + break; + } + if (i < 0) { + sb.port = 0; + return FALSE; + } + } + + /* Now detect the IRQ and DMA numbers */ + if (!sb.irq) { + unsigned int irqmask, sbirqmask, sbirqcount; + unsigned long timer; + + /* IRQ can be one of 2,3,5,7,10 */ + irq_detect_start(0x04ac, __sb_irq_irqdetect); + + /* Prepare timeout counter */ + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + sbirqmask = 0; + sbirqcount = 10; /* Emit 10 SB irqs */ + + /* Tell SoundBlaster to emit IRQ for 8-bit transfers */ + __sb_dsp_out(SBDSP_GEN_IRQ8); + __sb_wait(); + for (;;) { + irq_detect_get(0, &irqmask); + if (irqmask) { + sbirqmask |= irqmask; + if (!--sbirqcount) + break; + __sb_dsp_out(SBDSP_GEN_IRQ8); + } + if (_farnspeekl(0x46c) - timer >= 9) /* Wait ~1/2 secs */ + break; + } + if (sbirqmask) + for (sb.irq = 15; sb.irq > 0; sb.irq--) + if (irq_detect_get(sb.irq, &irqmask) == 10) + break; + + irq_detect_end(); + if (!sb.irq) + return FALSE; + } + + /* Detect the 8-bit and 16-bit DMAs */ + if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) { + static int __dma8[] = { 0, 1, 3 }; + static int __dma16[] = { 5, 6, 7 }; + int *dma; + + sb_output(FALSE); + /* Temporary hook SB IRQ */ + sb.irq_handle = irq_hook(sb.irq, __sb_irq_dmadetect, 200); + irq_enable(sb.irq_handle); + if (sb.irq > 7) + _irq_enable(2); + + /* Start a short DMA transfer and check if IRQ happened */ + for (;;) { + int i, oldcount; + unsigned int timer; + + if (!sb.dma8) + dma = &sb.dma8; + else if ((sb.dspver >= SBVER_16) && !sb.dma16) + dma = &sb.dma16; + else + break; + + for (i = 0; i < 3; i++) { + boolean success = 1; + + *dma = (dma == &sb.dma8) ? __dma8[i] : __dma16[i]; + oldcount = sb.irqcount; + + dma_disable(*dma); + dma_set_mode(*dma, DMA_MODE_WRITE); + dma_clear_ff(*dma); + dma_set_count(*dma, 2); + dma_enable(*dma); + + __sb_dspreg_out(SBDSP_SET_TIMING, 206); /* 20KHz */ + if (dma == &sb.dma8) { + sb.mode = 0; + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, 1); + } else { + sb.mode = SBMODE_16BITS; + __sb_dspreg_out(SBDSP_DMA_GENERIC16, 0); + __sb_dsp_out(0); + __sb_dsp_out(1); + } + + _farsetsel(_dos_ds); + timer = _farnspeekl(0x46c); + + while (oldcount == sb.irqcount) + if (_farnspeekl(0x46c) - timer >= 2) { + success = 0; + break; + } + dma_disable(*dma); + if (success) + break; + *dma = 0; + } + if (!*dma) + break; + } + + irq_unhook(sb.irq_handle); + sb.irq_handle = NULL; + if (!sb.dma8 || ((sb.dspver >= SBVER_16) && !sb.dma16)) + return FALSE; + } + return TRUE; +} + +/*************************************************** High-level interface *****/ + +/* Detect whenever SoundBlaster is present and fill "sb" structure */ +boolean sb_detect() +{ + char *env; + + /* Try to find the port and DMA from environment */ + env = getenv("BLASTER"); + + while (env && *env) { + /* Skip whitespace */ + while ((*env == ' ') || (*env == '\t')) + env++; + if (!*env) + break; + + switch (*env++) { + case 'A': + case 'a': + if (!sb.port) + sb.port = strtol(env, &env, 16); + break; + case 'E': + case 'e': + if (!sb.aweport) + sb.aweport = strtol(env, &env, 16); + break; + case 'I': + case 'i': + if (!sb.irq) + sb.irq = strtol(env, &env, 10); + break; + case 'D': + case 'd': + if (!sb.dma8) + sb.dma8 = strtol(env, &env, 10); + break; + case 'H': + case 'h': + if (!sb.dma16) + sb.dma16 = strtol(env, &env, 10); + break; + default: + /* Skip other values (H == MIDI, T == model, any other?) */ + while (*env && (*env != ' ') && (*env != '\t')) + env++; + break; + } + } + + /* Try to detect missing sound card parameters */ + __sb_detect(); + + if (!sb.port || !sb.irq || !sb.dma8) + return FALSE; + + if (!__sb_reset()) + return FALSE; + + if ((sb.dspver >= SBVER_16) && !sb.dma16) + return FALSE; + + if (sb.dspver >= SBVER_PRO) + sb.caps |= SBMODE_STEREO; + if (sb.dspver >= SBVER_16 && sb.dma16) + sb.caps |= SBMODE_16BITS; + if (sb.dspver < SBVER_20) + sb.maxfreq_mono = 22222; + else + sb.maxfreq_mono = 45454; + if (sb.dspver <= SBVER_16) + sb.maxfreq_stereo = 22727; + else + sb.maxfreq_stereo = 45454; + + sb.ok = 1; + return TRUE; +} + +/* Reset SoundBlaster */ +void sb_reset() +{ + sb_stop_dma(); + __sb_reset(); +} + +/* Start working with SoundBlaster */ +boolean sb_open() +{ + __dpmi_meminfo struct_info; + + if (!sb.ok) + if (!sb_detect()) + return FALSE; + + if (sb.open) + return FALSE; + + /* Now lock the sb structure in memory */ + struct_info.address = __djgpp_base_address + (unsigned long)&sb; + struct_info.size = sizeof(sb); + if (__dpmi_lock_linear_region(&struct_info)) + return FALSE; + + /* Hook the SB IRQ */ + sb.irq_handle = irq_hook(sb.irq, sb_irq, (long)sb_irq_end - (long)sb_irq); + if (!sb.irq_handle) { + __dpmi_unlock_linear_region(&struct_info); + return FALSE; + } + + /* Enable the interrupt */ + irq_enable(sb.irq_handle); + if (sb.irq > 7) + _irq_enable(2); + + sb.open++; + + return TRUE; +} + +/* Finish working with SoundBlaster */ +boolean sb_close() +{ + __dpmi_meminfo struct_info; + if (!sb.open) + return FALSE; + + sb.open--; + + /* Stop/free DMA buffer */ + sb_stop_dma(); + + /* Unhook IRQ */ + irq_unhook(sb.irq_handle); + sb.irq_handle = NULL; + + /* Unlock the sb structure */ + struct_info.address = __djgpp_base_address + (unsigned long)&sb; + struct_info.size = sizeof(sb); + __dpmi_unlock_linear_region(&struct_info); + + return TRUE; +} + +/* Enable/disable stereo DSP mode */ +/* Enable/disable speaker output */ +void sb_output(boolean enable) +{ + __sb_dsp_out(enable ? SBDSP_SPEAKER_ENA : SBDSP_SPEAKER_DIS); +} + +/* Start playing from DMA buffer */ +boolean sb_start_dma(unsigned char mode, unsigned int freq) +{ + int dmachannel = (mode & SBMODE_16BITS) ? sb.dma16 : sb.dma8; + int dmabuffsize; + unsigned int tc = 0; /* timing constant (<=sbpro only) */ + + /* Stop DMA transfer if it is enabled */ + sb_stop_dma(); + + /* Sanity check */ + if ((mode & SBMODE_MASK & sb.caps) != (mode & SBMODE_MASK)) + return FALSE; + + /* Check this SB can perform at requested frequency */ + if (((mode & SBMODE_STEREO) && (freq > sb.maxfreq_stereo)) + || (!(mode & SBMODE_STEREO) && (freq > sb.maxfreq_mono))) + return FALSE; + + /* Check the timing constant here to avoid failing later */ + if (sb.dspver < SBVER_16) { + /* SBpro cannot do signed transfer */ + if (mode & SBMODE_SIGNED) + return FALSE; + + /* Old SBs have a different way on setting DMA timing constant */ + tc = freq; + if (mode & SBMODE_STEREO) + tc *= 2; + tc = 1000000 / tc; + if (tc > 255) + return FALSE; + } + + sb.mode = mode; + + /* Get a DMA buffer enough for a 1/4sec interval... 4K <= dmasize <= 32K */ + dmabuffsize = freq; + if (mode & SBMODE_STEREO) + dmabuffsize *= 2; + if (mode & SBMODE_16BITS) + dmabuffsize *= 2; + dmabuffsize >>= 2; + if (dmabuffsize < 4096) + dmabuffsize = 4096; + if (dmabuffsize > 32768) + dmabuffsize = 32768; + dmabuffsize = (dmabuffsize + 255) & 0xffffff00; + + sb.dma_buff = dma_allocate(dmachannel, dmabuffsize); + if (!sb.dma_buff) + return FALSE; + + /* Fill DMA buffer with silence */ + dmabuffsize = sb.dma_buff->size; + if (mode & SBMODE_SIGNED) + memset(sb.dma_buff->linear, 0, dmabuffsize); + else + memset(sb.dma_buff->linear, 0x80, dmabuffsize); + + /* Prime DMA for transfer */ + dma_start(sb.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); + + /* Tell SoundBlaster to start transfer */ + if (sb.dspver >= SBVER_16) { /* SB16 */ + __sb_dspreg_outwhl(SBDSP_SET_RATE, freq); + + /* Start DMA->DAC transfer */ + __sb_dspreg_out(SBM_GENDAC_AUTOINIT | SBM_GENDAC_FIFO | + ((mode & SBMODE_16BITS) ? SBDSP_DMA_GENERIC16 : + SBDSP_DMA_GENERIC8), + ((mode & SBMODE_SIGNED) ? SBM_GENDAC_SIGNED : 0) | + ((mode & SBMODE_STEREO) ? SBM_GENDAC_STEREO : 0)); + + /* Write the length of transfer */ + dmabuffsize = (dmabuffsize >> 2) - 1; + __sb_dsp_out(dmabuffsize); + __sb_dsp_out(dmabuffsize >> 8); + } else { + __sb_dspreg_out(SBDSP_SET_TIMING, 256 - tc); + dmabuffsize = (dmabuffsize >> 1) - 1; + if (sb.dspver >= SBVER_20) { /* SB 2.0/Pro */ + /* Set stereo mode */ + __sb_stereo((mode & SBMODE_STEREO) ? TRUE : FALSE); + __sb_dspreg_outwlh(SBDSP_SET_DMA_BLOCK, dmabuffsize); + if (sb.dspver >= SBVER_PRO) + __sb_dsp_out(SBDSP_HS_DMA_DAC8_AUTO); + else + __sb_dsp_out(SBDSP_DMA_PCM8_AUTO); + } else { /* Original SB */ + /* Start DMA->DAC transfer */ + __sb_dspreg_outwlh(SBDSP_DMA_PCM8, dmabuffsize); + } + } + + return TRUE; +} + +/* Stop playing from DMA buffer */ +void sb_stop_dma() +{ + if (!sb.dma_buff) + return; + + if (sb.mode & SBMODE_16BITS) + __sb_dsp_out(SBDSP_DMA_HALT16); + else + __sb_dsp_out(SBDSP_DMA_HALT8); + + dma_disable(sb.dma_buff->channel); + dma_free(sb.dma_buff); + sb.dma_buff = NULL; +} + +/* Query current position/total size of the DMA buffer */ +void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos) +{ + unsigned int dma_left; + *dma_size = sb.dma_buff->size; + /* It can happen we try to read DMA count when HI/LO bytes will be + inconsistent */ + for (;;) { + unsigned int dma_left_test; + dma_clear_ff(sb.dma_buff->channel); + dma_left_test = dma_get_count(sb.dma_buff->channel); + dma_left = dma_get_count(sb.dma_buff->channel); + if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10)) + break; + } + *dma_pos = *dma_size - dma_left; +} diff --git a/djgpp/dossb.h b/djgpp/dossb.h new file mode 100644 index 00000000..ebd22be2 --- /dev/null +++ b/djgpp/dossb.h @@ -0,0 +1,334 @@ +/* SoundBlaster and compatible soundcards definitions -- + from libMikMod. Written by Andrew Zabolotny + Further bug fixes by O.Sezer + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef __DOSSB_H__ +#define __DOSSB_H__ + +#include "dosdma.h" +#include "dosirq.h" + +#define SB_FM_LEFT_STATUS (sb.port + 0x00) /* (r) Left FM status */ +#define SB_FM_LEFT_REGSEL (sb.port + 0x00) /* (w) Left FM register select */ +#define SB_FM_LEFT_DATA (sb.port + 0x01) /* (w) Left FM data */ +#define SB_FM_RIGHT_STATUS (sb.port + 0x02) /* (r) Right FM status */ +#define SB_FM_RIGHT_REGSEL (sb.port + 0x02) /* (w) Right FM register select */ +#define SB_FM_RIGHT_DATA (sb.port + 0x03) /* (w) Right FM data */ +#define SB_MIXER_REGSEL (sb.port + 0x04) /* (w) Mixer register select */ +#define SB_MIXER_DATA (sb.port + 0x05) /* (rw)Mixer data */ +#define SB_DSP_RESET (sb.port + 0x06) /* (w) DSP reset */ +#define SB_FM_STATUS (sb.port + 0x08) /* (r) FM status */ +#define SB_FM_REGSEL (sb.port + 0x08) /* (w) FM register select */ +#define SB_FM_DATA (sb.port + 0x09) /* (w) FM data */ +#define SB_DSP_DATA_IN (sb.port + 0x0a) /* (r) DSP data input */ +#define SB_DSP_DATA_OUT (sb.port + 0x0c) /* (w) DSP data output */ +#define SB_DSP_DATA_OUT_STATUS (sb.port + 0x0c) /* (r) DSP data output status */ +#define SB_DSP_TIMER_IRQ (sb.port + 0x0d) /* (r) clear timer IRQ? */ +#define SB_DSP_DATA_IN_STATUS (sb.port + 0x0e) /* (r) DSP data input status */ +#define SB_DSP_DMA8_IRQ (sb.port + 0x0e) /* (r) Acknowledge 8-bit DMA transfer */ +#define SB_DSP_DMA16_IRQ (sb.port + 0x0f) /* (r) Acknowledge 16-bit DMA transfer */ + +/* DSP commands */ +#define SBDSP_ASP_STATUS 0x03 /* ASP Status (SB16ASP) */ +#define SBDSP_STATUS_OLD 0x04 /* DSP Status (Obsolete) (SB2.0-Pro2) */ +#define SBDSP_DIRECT_DAC 0x10 /* Direct DAC, 8-bit (SB) */ +#define SBDSP_DMA_PCM8 0x14 /* DMA DAC, 8-bit (SB) */ +#define SBDSP_DMA_ADPCM2 0x16 /* DMA DAC, 2-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM2R 0x17 /* DMA DAC, 2-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_PCM8_AUTO 0x1C /* Auto-Initialize DMA DAC, 8-bit (SB2.0) */ +#define SBDSP_DMA_ADPCM2R_AUTO 0x1F /* Auto-Initialize DMA DAC, 2-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DIRECT_ADC 0x20 /* Direct ADC, 8-bit (SB) */ +#define SBDSP_DMA_ADC8 0x24 /* DMA ADC, 8-bit (SB) */ +#define SBDSP_DIRECT_ADC8_BURST 0x28 /* Direct ADC, 8-bit (Burst) (SB-Pro2) */ +#define SBDSP_DMA_ADC8_AUTO 0x2C /* Auto-Initialize DMA ADC, 8-bit (SB2.0) */ +#define SBDSP_MIDI_READ_POLL 0x30 /* MIDI Read Poll (SB) */ +#define SBDSP_MIDI_READ_IRQ 0x31 /* MIDI Read Interrupt (SB) */ +#define SBDSP_MIDI_READ_TIME 0x32 /* MIDI Read Timestamp Poll (SB???) */ +#define SBDSP_MIDI_READ_TIME_IRQ 0x33 /* MIDI Read Timestamp Interrupt (SB???) */ +#define SBDSP_MIDI_RW_POLL 0x34 /* MIDI Read Poll + Write Poll (UART) (SB2.0) */ +#define SBDSP_MIDI_RW_IRQ 0x35 /* MIDI Read Interrupt + Write Poll (UART) (SB2.0???) */ +#define SBDSP_MIDI_RW_TIME_IRQ 0x37 /* MIDI Read Timestamp Interrupt + Write Poll (UART) (SB2.0???) */ +#define SBDSP_MIDI_WRITE_POLL 0x38 /* MIDI Write Poll (SB) */ +#define SBDSP_SET_TIMING 0x40 /* Set Time Constant (SB) */ +#define SBDSP_SET_RATE 0x41 /* Set Sample Rate, Hz (SB16) */ +#define SBDSP_DMA_CONT8_AUTO 0x45 /* Continue Auto-Initialize DMA, 8-bit (SB16) */ +#define SBDSP_DMA_CONT16_AUTO 0x47 /* Continue Auto-Initialize DMA, 16-bit (SB16) */ +#define SBDSP_SET_DMA_BLOCK 0x48 /* Set DMA Block Size (SB2.0) */ +#define SBDSP_DMA_ADPCM4 0x74 /* DMA DAC, 4-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM4_REF 0x75 /* DMA DAC, 4-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_ADPCM26 0x76 /* DMA DAC, 2.6-bit ADPCM (SB) */ +#define SBDSP_DMA_ADPCM26_REF 0x77 /* DMA DAC, 2.6-bit ADPCM Reference (SB) */ +#define SBDSP_DMA_ADPCM4R_AUTO 0x7D /* Auto-Initialize DMA DAC, 4-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DMA_ADPCM26R_AUTO 0x7F /* Auto-Initialize DMA DAC, 2.6-bit ADPCM Reference (SB2.0) */ +#define SBDSP_DISABLE_DAC 0x80 /* Silence DAC (SB) */ +#define SBDSP_HS_DMA_DAC8_AUTO 0x90 /* Auto-Initialize DMA DAC, 8-bit (High Speed) (SB2.0-Pro2) */ +#define SBDSP_HS_DMA_ADC8_AUTO 0x98 /* Auto-Initialize DMA ADC, 8-bit (High Speed) (SB2.0-Pro2) */ +#define SBDSP_STEREO_ADC_DIS 0xA0 /* Disable Stereo Input Mode (SBPro Only) */ +#define SBDSP_STEREO_ADC_ENA 0xA8 /* Enable Stereo Input Mode (SBPro Only) */ +#define SBDSP_DMA_GENERIC16 0xB0 /* Generic DAC/ADC DMA (16-bit) (SB16) */ +#define SBDSP_DMA_GENERIC8 0xC0 /* Generic DAC/ADC DMA (8-bit) (SB16) */ +#define SBDSP_DMA_HALT8 0xD0 /* Halt DMA Operation, 8-bit (SB) */ +#define SBDSP_SPEAKER_ENA 0xD1 /* Enable Speaker (SB) */ +#define SBDSP_SPEAKER_DIS 0xD3 /* Disable Speaker (SB) */ +#define SBDSP_DMA_CONT8 0xD4 /* Continue DMA Operation, 8-bit (SB) */ +#define SBDSP_DMA_HALT16 0xD5 /* Halt DMA Operation, 16-bit (SB16) */ +#define SBDSP_DMA_CONT16 0xD6 /* Continue DMA Operation, 16-bit (SB16) */ +#define SBDSP_SPEAKER_STATUS 0xD8 /* Speaker Status (SB) */ +#define SBDSP_DMA_EXIT16_AUTO 0xD9 /* Exit Auto-Initialize DMA Operation, 16-bit (SB16) */ +#define SBDSP_DMA_EXIT8_AUTO 0xDA /* Exit Auto-Initialize DMA Operation, 8-bit (SB2.0) */ +#define SBDSP_IDENTIFY 0xE0 /* DSP Identification (SB2.0) */ +#define SBDSP_VERSION 0xE1 /* DSP Version (SB) */ +#define SBDSP_COPYRIGHT 0xE3 /* DSP Copyright (SBPro2???) */ +#define SBDSP_WRITE_TEST 0xE4 /* Write Test Register (SB2.0) */ +#define SBDSP_READ_TEST 0xE8 /* Read Test Register (SB2.0) */ +#define SBDSP_SINE_GEN 0xF0 /* Sine Generator (SB) */ +#define SBDSP_AUX_STATUS_PRO 0xF1 /* DSP Auxiliary Status (Obsolete) (SB-Pro2) */ +#define SBDSP_GEN_IRQ8 0xF2 /* IRQ Request, 8-bit (SB) */ +#define SBDSP_GEN_IRQ16 0xF3 /* IRQ Request, 16-bit (SB16) */ +#define SBDSP_STATUS 0xFB /* DSP Status (SB16) */ +#define SBDSP_AUX_STATUS_16 0xFC /* DSP Auxiliary Status (SB16) */ +#define SBDSP_CMD_STATUS 0xFD /* DSP Command Status (SB16) */ + +/* Mixer commands */ +#define SBMIX_RESET 0x00 /* Reset Write SBPro */ +#define SBMIX_STATUS 0x01 /* Status Read SBPro */ +#define SBMIX_MASTER_LEVEL1 0x02 /* Master Volume Read/Write SBPro Only */ +#define SBMIX_DAC_LEVEL 0x04 /* DAC Level Read/Write SBPro */ +#define SBMIX_FM_OUTPUT 0x06 /* FM Output Control Read/Write SBPro Only */ +#define SBMIX_MIC_LEVEL 0x0A /* Microphone Level Read/Write SBPro */ +#define SBMIX_INPUT_SELECT 0x0C /* Input/Filter Select Read/Write SBPro Only */ +#define SBMIX_OUTPUT_SELECT 0x0E /* Output/Stereo Select Read/Write SBPro Only */ +#define SBMIX_FM_LEVEL 0x22 /* Master Volume Read/Write SBPro */ +#define SBMIX_MASTER_LEVEL 0x26 /* FM Level Read/Write SBPro */ +#define SBMIX_CD_LEVEL 0x28 /* CD Audio Level Read/Write SBPro */ +#define SBMIX_LINEIN_LEVEL 0x2E /* Line In Level Read/Write SBPro */ +#define SBMIX_MASTER_LEVEL_L 0x30 /* Master Volume Left Read/Write SB16 */ +#define SBMIX_MASTER_LEVEL_R 0x31 /* Master Volume Right Read/Write SB16 */ +#define SBMIX_DAC_LEVEL_L 0x32 /* DAC Level Left Read/Write SB16 */ +#define SBMIX_DAC_LEVEL_R 0x33 /* DAC Level Right Read/Write SB16 */ +#define SBMIX_FM_LEVEL_L 0x34 /* FM Level Left Read/Write SB16 */ +#define SBMIX_FM_LEVEL_R 0x35 /* FM Level Right Read/Write SB16 */ +#define SBMIX_CD_LEVEL_L 0x36 /* CD Audio Level Left Read/Write SB16 */ +#define SBMIX_CD_LEVEL_R 0x37 /* CD Audio Level Right Read/Write SB16 */ +#define SBMIX_LINEIN_LEVEL_L 0x38 /* Line In Level Left Read/Write SB16 */ +#define SBMIX_LINEIN_LEVEL_R 0x39 /* Line In Level Right Read/Write SB16 */ +#define SBMIX_MIC_LEVEL_16 0x3A /* Microphone Level Read/Write SB16 */ +#define SBMIX_PCSPK_LEVEL 0x3B /* PC Speaker Level Read/Write SB16 */ +#define SBMIX_OUTPUT_CONTROL 0x3C /* Output Control Read/Write SB16 */ +#define SBMIX_INPUT_CONTROL_L 0x3D /* Input Control Left Read/Write SB16 */ +#define SBMIX_INPUT_CONTROL_R 0x3E /* Input Control Right Read/Write SB16 */ +#define SBMIX_INPUT_GAIN_L 0x3F /* Input Gain Control Left Read/Write SB16 */ +#define SBMIX_INPUT_GAIN_R 0x40 /* Input Gain Control Right Read/Write SB16 */ +#define SBMIX_OUTPUT_GAIN_L 0x41 /* Output Gain Control Left Read/Write SB16 */ +#define SBMIX_OUTPUT_GAIN_R 0x42 /* Output Gain Control Right Read/Write SB16 */ +#define SBMIX_AGC_CONTROL 0x43 /* Automatic Gain Control (AGC) Read/Write SB16 */ +#define SBMIX_TREBLE_L 0x44 /* Treble Left Read/Write SB16 */ +#define SBMIX_TREBLE_R 0x45 /* Treble Right Read/Write SB16 */ +#define SBMIX_BASS_L 0x46 /* Bass Left Read/Write SB16 */ +#define SBMIX_BASS_R 0x47 /* Bass Right Read/Write SB16 */ +#define SBMIX_IRQ_SELECT 0x80 /* IRQ Select Read/Write SB16 */ +#define SBMIX_DMA_SELECT 0x81 /* DMA Select Read/Write SB16 */ +#define SBMIX_IRQ_STATUS 0x82 /* IRQ Status Read SB16 */ + +/* SB_DSP_DATA_OUT_STATUS and SB_DSP_DATA_IN_STATUS bits */ +#define SBM_DSP_READY 0x80 + +/* SB_DSP_RESET / SBMIX_RESET */ +#define SBM_DSP_RESET 0x01 + +/* SBMIX_OUTPUT_SELECT */ +#define SBM_MIX_STEREO 0x02 +#define SBM_MIX_FILTER 0x20 + +/* SBDSP_DMA_GENERIC16/SBDSP_DMA_GENERIC8 */ +#define SBM_GENDAC_FIFO 0x02 +#define SBM_GENDAC_AUTOINIT 0x04 +#define SBM_GENDAC_ADC 0x08 +/* Second (mode) byte */ +#define SBM_GENDAC_SIGNED 0x10 +#define SBM_GENDAC_STEREO 0x20 + +/* DSP version masks */ +#define SBVER_10 0x0100 /* Original SoundBlaster */ +#define SBVER_15 0x0105 /* SoundBlaster 1.5 */ +#define SBVER_20 0x0200 /* SoundBlaster 2.0 */ +#define SBVER_PRO 0x0300 /* SoundBlaster Pro */ +#define SBVER_PRO2 0x0301 /* SoundBlaster Pro 2 */ +#define SBVER_16 0x0400 /* SoundBlaster 16 */ +#define SBVER_AWE32 0x040c /* SoundBlaster AWE32 */ + +typedef unsigned char boolean; + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* Play mode bits */ +#define SBMODE_16BITS 0x0001 +#define SBMODE_STEREO 0x0002 +#define SBMODE_SIGNED 0x0004 + +/* Mask for capabilities that never change */ +#define SBMODE_MASK (SBMODE_16BITS | SBMODE_STEREO) + +/* You can fill some members of this struct (i.e. port,irq,dma) before + * calling sb_detect() or sb_open()... this will ignore environment settings. + */ +typedef struct __sb_state_s { + boolean ok; /* Are structure contents valid? */ + int port, aweport; /* sb/awe32 base port */ + int irq; /* SoundBlaster IRQ */ + int dma8, dma16; /* 8-bit and 16-bit DMAs */ + unsigned maxfreq_mono; /* Maximum discretization frequency / mono mode */ + unsigned maxfreq_stereo; /* Maximum discretization frequency / stereo mode */ + unsigned short dspver; /* DSP version number */ + struct irq_handle *irq_handle; /* The interrupt handler */ + dma_buffer *dma_buff; /* Pre-allocated DMA buffer */ + unsigned char caps; /* SoundBlaster capabilities (SBMODE_XXX) */ + unsigned char mode; /* Current SB mode (SBMODE_XXX) */ + boolean open; /* Whenever the card has been opened */ + volatile int irqcount; /* Incremented on each IRQ... for diagnostics */ + void (*timer_callback) (); /* Called TWICE per buffer play */ +} __sb_state; + +extern __sb_state sb; + +extern void __sb_wait(); + +static inline boolean __sb_dsp_ready_in() +{ + int count; + for (count = 10000; count >= 0; count--) + if (inportb(SB_DSP_DATA_IN_STATUS) & SBM_DSP_READY) + return TRUE; + return FALSE; +} + +static inline boolean __sb_dsp_ready_out() +{ + int count; + for (count = 10000; count >= 0; count--) + if ((inportb(SB_DSP_DATA_OUT_STATUS) & SBM_DSP_READY) == 0) + return TRUE; + return FALSE; +} + +static inline void __sb_dsp_out(unsigned char reg) +{ + __sb_dsp_ready_out(); + outportb(SB_DSP_DATA_OUT, reg); +} + +static inline unsigned char __sb_dsp_in() +{ + __sb_dsp_ready_in(); + return inportb(SB_DSP_DATA_IN); +} + +static inline void __sb_dspreg_out(unsigned char reg, unsigned char val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val); +} + +static inline void __sb_dspreg_outwlh(unsigned char reg, unsigned short val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val); + __sb_dsp_out(val >> 8); +} + +static inline void __sb_dspreg_outwhl(unsigned char reg, unsigned short val) +{ + __sb_dsp_out(reg); + __sb_dsp_out(val >> 8); + __sb_dsp_out(val); +} + +static inline unsigned char __sb_dspreg_in(unsigned char reg) +{ + __sb_dsp_out(reg); + return __sb_dsp_in(); +} + +static inline void __sb_dsp_ack_dma8() +{ + inportb(SB_DSP_DMA8_IRQ); +} + +static inline void __sb_dsp_ack_dma16() +{ + inportb(SB_DSP_DMA16_IRQ); +} + +static inline unsigned short __sb_dsp_version() +{ + unsigned short ver; + __sb_dsp_out(SBDSP_VERSION); + __sb_dsp_ready_in(); + ver = ((unsigned short)__sb_dsp_in()) << 8; + ver |= __sb_dsp_in(); + return ver; +} + +static inline void __sb_mixer_out(unsigned char reg, unsigned char val) +{ + outportb(SB_MIXER_REGSEL, reg); + outportb(SB_MIXER_DATA, val); +} + +static inline unsigned char __sb_mixer_in(unsigned char reg) +{ + outportb(SB_MIXER_REGSEL, reg); + return inportb(SB_MIXER_DATA); +} + +/* Enable stereo transfers: sbpro mode only */ +static inline void __sb_stereo(boolean stereo) +{ + unsigned char val = __sb_mixer_in(SBMIX_OUTPUT_SELECT); + if (stereo) + val |= SBM_MIX_STEREO; + else + val &= ~SBM_MIX_STEREO; + __sb_mixer_out(SBMIX_OUTPUT_SELECT, val); +} + +/* Detect whenever SoundBlaster is present and fill "sb" structure */ +extern boolean sb_detect(); +/* Reset SoundBlaster */ +extern void sb_reset(); +/* Start working with SoundBlaster */ +extern boolean sb_open(); +/* Finish working with SoundBlaster */ +extern boolean sb_close(); +/* Enable/disable speaker output */ +extern void sb_output(boolean enable); +/* Start playing from DMA buffer in either 8/16 bit mono/stereo */ +extern boolean sb_start_dma(unsigned char mode, unsigned int freq); +/* Stop playing from DMA buffer */ +extern void sb_stop_dma(); +/* Query current position/total size of the DMA buffer */ +extern void sb_query_dma(unsigned int *dma_size, unsigned int *dma_pos); + +#endif /* __DOSSB_H__ */ + diff --git a/docs/ProgRef.odt b/docs/ProgRef.odt index 9ac10919..3c8b3361 100644 Binary files a/docs/ProgRef.odt and b/docs/ProgRef.odt differ diff --git a/docs/TechDoc.odt b/docs/TechDoc.odt deleted file mode 100644 index f07e0e07..00000000 Binary files a/docs/TechDoc.odt and /dev/null differ diff --git a/docs/formats/MUSFileFormat.txt b/docs/formats/MUSFileFormat.txt new file mode 100644 index 00000000..8ae03bff --- /dev/null +++ b/docs/formats/MUSFileFormat.txt @@ -0,0 +1,331 @@ + MUS File Format + ═══════════════ + +Written by: Vladimir Arnost, QA-Software +Updated: March 9, 1996 +Version: 1.31 +Internet: xarnos00@dcse.fee.vutbr.cz +FIDO: 2:423/36.2 + + +1. General Description +────────────────────── + + A .MUS file is a simple clone of .MID file. It uses the same instruments, + similar commands and the same principle: a list of sound events. + It consists of two parts: header and body. + + NOTE: All numerical values mentioned in this document are zero-based. + If not specified otherwise, all numbers are given in decimal. + Hexadecimal numbers are suffixed by 'h' (e.g. 5Ch). Bits are + numbered in this fashion: LSB (right-most) = 0, MSB (left-most) = 7. + + +2. MUS File Header +────────────────── + + The MUS header has the following structure: + + struct MUSheader { + char ID[4]; // identifier "MUS" 0x1A + WORD scoreLen; + WORD scoreStart; + WORD channels; // count of primary channels + WORD sec_channels; // count of secondary channels + WORD instrCnt; + WORD dummy; + // variable-length part starts here + WORD instruments[]; + }; + + NOTE: WORD is a 16-bit unsigned integer (little-endian) + + The header has two parts: the fixed-length and the variable-length part. + The former contains file identifier, score start and length, number of + channels and number of used instruments. The latter part is actually + a list of used instruments. The instruments are stored as numbers which + are arranged in this fashion: + + Instrument Number Meaning + 0 - 127 standard MIDI instruments + 135 - 181 standard MIDI percussions (notes 35 - 81) + + `scoreStart' is the absolute file position of the score and `scoreLen' is + its length in bytes. Usage of a 16-bit number as length limits .MUS file + size to 64KB. + + `channels' tells you how many channels are utilized in the song. The + channel number 15 (percussions) is not included in the sum. + + +3. MUS File Body +──────────────── + + Unlike MID files, MUS body contains only one track. File body is + a sequence of sound events and time records. A sound event consists of + one or more bytes encoded as follows: + + 1st byte -- event descriptor: + ╓──7─┬──6─┬──5─┬──4─┬──3─┬──2─┬──1─┬──0─╖ + ║Last│ Event type │ Channel number ║ + ╙────┴────┴────┴────┴────┴────┴────┴────╜ + + `Event type' is one of these: + 0 - release note + 1 - play note + 2 - pitch wheel (bender) + 3 - system event (valueless controller) + 4 - change controller + 5 - ??? + 6 - score end + 7 - ??? + + `Channel number' determines which channel this event refers to. + Channels provide only logical score division. Every channel + carries its own settings (instrument #, panning, volume) and the + channel number specifies only which settings to use. In general, + the channel number itself is almost irrelevant and may be chosen + arbitrarily within the interval 0 to 14. The only exception is + the channel number 15, which is dedicated ONLY to percussions. + + `Last' - if set, the event is followed by time information. This + means that this is the last event in a group of events which + occur at the same time. The time information is a number of + ticks to wait before processing next event. One tick is usually + 1/140 sec (in Doom I, II and Heretic; Raptor uses 1/70 sec). + + Time information can be read in this way: + 1. time = 0 + 2. READ a byte + 3. time = time * 128 + byte AND 127 + 4. IF (byte AND 128) GO TO 2 + 5. RETURN time + The time info is a series of 7-bit chunks. The bit #7 is set + until the last byte whose bit 7 is zero. This scheme allows + small numbers occupy less space than large ones. + + Event Type + ──────────────────── + 0 Release note + ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ + ║ 0 │ Note number 0 - 127 ║ + ╙───┴───┴───┴───┴───┴───┴───┴───╜ + + 1 Play note + ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ + ║Vol│ Note number 0 - 127 ║ ║ 0 │ Note volume 0 - 127 ║ + ╙───┴───┴───┴───┴───┴───┴───┴───╜ ╙───┴───┴───┴───┴───┴───┴───┴───╜ + + `Note volume' is present only if `Vol' bit is set. Otherwise the + previous value is used and the second byte is not present. + NOTE: Each channel keeps track of its own last volume value. + More than one note can be played at once in one channel. + Channel 15 is dedicated to drums and percussions. `Note number' + acts as an instrument selector there. See Appendix C + + 2 Pitch wheel + ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ + ║ Pitch wheel value ║ + ╙───┴───┴───┴───┴───┴───┴───┴───╜ + + Sets pitch wheel (bender) value of a channel. Some handy values are + shown in the table (all values in the range 0-255 can be used): + + ┌───────┬───────────────────────┐ + │ Value │ Pitch change │ + ├───────┼───────────────────────┤ + │ 0 │ two half-tones down │ + │ 64 │ one half-tone down │ + │ 128 │ normal (default) │ + │ 192 │ one half-tone up │ + │ 255 │ two half-tones up │ + └───────┴───────────────────────┘ + + 3 System event (Valueless controller) + ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ + ║ 0 │ Number ║ + ╙───┴───┴───┴───┴───┴───┴───┴───╜ + + Number MIDI ctrl Description + 10 120 (78h) All sounds off + 11 123 (7Bh) All notes off + 12 126 (7Eh) Mono + 13 127 (7Fh) Poly + 14 121 (79h) Reset all controllers + + NOTE: The second column (MIDI ctrl) lists the corresponding MIDI + controller number. It is not needed unless you want to + convert MUS file data to MIDI. + + 4 Change controller + ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ ╓─7─┬─6─┬─5─┬─4─┬─3─┬─2─┬─1─┬─0─╖ + ║ 0 │ Controller number ║ ║ 0 │ Controller value ║ + ╙───┴───┴───┴───┴───┴───┴───┴───╜ ╙───┴───┴───┴───┴───┴───┴───┴───╜ + + Number MIDI ctrl Description + 0 N/A Instrument (patch, program) number + 1 0 or 32 Bank select: 0 by default + 2 1 (01h) Modulation pot (frequency vibrato depth) + 3 7 (07h) Volume: 0-silent, ~100-normal, 127-loud + 4 10 (0Ah) Pan (balance) pot: 0-left, 64-center (default), + 127-right + 5 11 (0Bh) Expression pot + 6 91 (5Bh) Reverb depth + 7 93 (5Dh) Chorus depth + 8 64 (40h) Sustain pedal (hold) + 9 67 (43h) Soft pedal + + NOTE: MUS controller 0 has no equivalent MIDI controller, but + is encoded as MIDI event 0Cxh--patch change (`x' is the + channel number) + + 5 Unknown + Not known what data (if any) this command takes. + + 6 Score end + No data. + + Marks the end of score. Must be present at the end, otherwise the + player may go off the rails. In DOOM this command restarts playing. + + 7 Unknown + Not known what data (if any) this command takes. + + +APPENDIX A - Note numbers +───────────────────────── + + ╔════════╦═════╤════╤════╤════╤════╤════╤════╤════╤════╤════╤════╤════╗ + ║ Octave ║ C │ C# │ D │ D# │ E │ F │ F# │ G │ G# │ A │ A# │ B ║ + ╠════════╬═════╪════╪════╪════╪════╪════╪════╪════╪════╪════╪════╪════╣ + ║ 0 ║ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 ║ + ║ 1 ║ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 ║ + ║ 2 ║ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ 32 │ 33 │ 34 │ 35 ║ + ║ 3 ║ 36 │ 37 │ 38 │ 39 │ 40 │ 41 │ 42 │ 43 │ 44 │ 45 │ 46 │ 47 ║ + ║ 4 ║ 48 │ 49 │ 50 │ 51 │ 52 │ 53 │ 54 │ 55 │ 56 │ 57 │ 58 │ 59 ║ + ║ 5 ║ 60 │ 61 │ 62 │ 63 │ 64 │ 65 │ 66 │ 67 │ 68 │ 69 │ 70 │ 71 ║ + ║ 6 ║ 72 │ 73 │ 74 │ 75 │ 76 │ 77 │ 78 │ 79 │ 80 │ 81 │ 82 │ 83 ║ + ║ 7 ║ 84 │ 85 │ 86 │ 87 │ 88 │ 89 │ 90 │ 91 │ 92 │ 93 │ 94 │ 95 ║ + ║ 8 ║ 96 │ 97 │ 98 │ 99 │100 │101 │102 │103 │104 │105 │106 │107 ║ + ║ 9 ║ 108 │109 │110 │111 │112 │113 │114 │115 │116 │117 │118 │119 ║ + ║ 10 ║ 120 │121 │122 │123 │124 │125 │126 │127 │ │ │ │ ║ + ╚════════╩═════╧════╧════╧════╧════╧════╧════╧════╧════╧════╧════╧════╝ + + +APPENDIX B - Instrument Patch Map +───────────────────────────────── + + Block 0-7 PIANO Block 8-15 CHROM PERCUSSION + 0 Acoustic Grand Piano 8 Celesta + 1 Bright Acoustic Piano 9 Glockenspiel + 2 Electric Grand Piano 10 Music Box + 3 Honky-tonk Piano 11 Vibraphone + 4 Rhodes Paino 12 Marimba + 5 Chorused Piano 13 Xylophone + 6 Harpsichord 14 Tubular-bell + 7 Clavinet 15 Dulcimer + + Block 16-23 ORGAN Block 24-31 GUITAR + 16 Hammond Organ 24 Acoustic Guitar (nylon) + 17 Percussive Organ 25 Acoustic Guitar (steel) + 18 Rock Organ 26 Electric Guitar (jazz) + 19 Church Organ 27 Electric Guitar (clean) + 20 Reed Organ 28 Electric Guitar (muted) + 21 Accordion 29 Overdriven Guitar + 22 Harmonica 30 Distortion Guitar + 23 Tango Accordion 31 Guitar Harmonics + + Block 32-39 BASS Block 40-47 STRINGS + 32 Acoustic Bass 40 Violin + 33 Electric Bass (finger) 41 Viola + 34 Electric Bass (pick) 42 Cello + 35 Fretless Bass 43 Contrabass + 36 Slap Bass 1 44 Tremolo Strings + 37 Slap Bass 2 45 Pizzicato Strings + 38 Synth Bass 1 46 Orchestral Harp + 39 Synth Bass 2 47 Timpani + + Block 48-55 ENSEMBLE Block 56-63 BRASS + 48 String Ensemble 1 56 Trumpet + 49 String Ensemble 2 57 Trombone + 50 Synth Strings 1 58 Tuba + 51 Synth Strings 2 59 Muted Trumpet + 52 Choir Aahs 60 French Horn + 53 Voice Oohs 61 Brass Section + 54 Synth Voice 62 Synth Brass 1 + 55 Orchestra Hit 63 Synth Bass 2 + + Block 64-71 REED Block 72-79 PIPE + 64 Soprano Sax 72 Piccolo + 65 Alto Sax 73 Flute + 66 Tenor Sax 74 Recorder + 67 Baritone Sax 75 Pan Flute + 68 Oboe 76 Bottle Blow + 69 English Horn 77 Shakuhachi + 70 Bassoon 78 Whistle + 71 Clarinet 79 Ocarina + + Block 80-87 SYNTH LEAD Block 88-95 SYNTH PAD + 80 Lead 1 (square) 88 Pad 1 (new age) + 81 Lead 2 (sawtooth) 89 Pad 2 (warm) + 82 Lead 3 (calliope) 90 Pad 3 (polysynth) + 83 Lead 4 (chiffer) 91 Pad 4 (choir) + 84 Lead 5 (charang) 92 Pad 5 (bowed glass) + 85 Lead 6 (voice) 93 Pad 6 (metal) + 86 Lead 7 (5th sawtooth) 94 Pad 7 (halo) + 87 Lead 8 (bass & lead) 95 Pad 8 (sweep) + + Block 96-103 SYNTH EFFECTS Block 104-111 ETHNIC + 96 FX 1 (rain) 104 Sitar + 97 FX 2 (soundtrack) 105 Banjo + 98 FX 3 (crystal) 106 Shamisen + 99 FX 4 (atmosphere) 107 Koto + 100 FX 5 (brightness) 108 Kalimba + 101 FX 6 (goblin) 109 Bag Pipe + 102 FX 7 (echo drops) 110 Fiddle + 103 FX 8 (star-theme) 111 Shanai + + Block 112-119 PERCUSSIVE Block 120-127 SOUND EFFECTS + 112 Tinkle Bell 120 Guitar Fret Noise + 113 Agogo 121 Breath Noise + 114 Steel Drums 122 Seashore + 115 Woodblock 123 Bird Tweet + 116 Taiko Drum 124 Telephone Ring + 117 Melodic Tom 125 Helicopter + 118 Synth Drum 126 Applause + 119 Reverse Cymbal 127 Gun Shot + + +APPENDIX C - Percussion Key Map +─────────────────────────────── + + In channel #15, the note number does not affect the pitch but + the instrument type. The default pitch for percussions is 60 (C-5). + + Note Instrument Note Instrument + + 35 Acoustic Bass Drum 59 Ride Cymbal 2 + 36 Bass Drum 60 High Bongo + 37 Slide Stick 61 Low Bango + 38 Acoustic Snare 62 Mute High Conga + 39 Hand Clap 63 Open High Conga + 40 Electric Snare 64 Low Conga + 41 Low Floor Tom 65 High Timbale + 42 Closed High-Hat 66 Low Timbale + 43 High Floor Tom 67 High Agogo + 44 Pedal High Hat 68 Low Agogo + 45 Low Tom 69 Cabasa + 46 Open High Hat 70 Maracas + 47 Low-Mid Tom 71 Short Whistle + 48 High-Mid Tom 72 Long Whistle + 49 Crash Cymbal 1 73 Short Guiro + 50 High Tom 74 Long Guiro + 51 Ride Cymbal 1 75 Claves + 52 Chinses Cymbal 76 High Wood Block + 53 Ride Bell 77 Low Wood Block + 54 Tambourine 78 Mute Cuica + 55 Splash Cymbal 79 Open Cuica + 56 Cowbell 80 Mute Triangle + 57 Crash Cymbal 2 81 Open Triangle + 58 Vibraslap + diff --git a/docs/man/man1/wildmidi.1 b/docs/man/man1/wildmidi.1 index 6bf082c5..30459dff 100644 --- a/docs/man/man1/wildmidi.1 +++ b/docs/man/man1/wildmidi.1 @@ -6,29 +6,29 @@ wildmidi \- example player for libWildMidi .B libWildMidi .PP .SH FILES -.B /etc/wildmidi.cfg +.B /etc/wildmidi/wildmidi.cfg .PP .SH SYNOPSIS -.B wildmidi [-behlv] [-c \fIconfig-file\fB] [-d \fIdevice\fB] [-m \fIvolume-level\fB] [-o \fIwav-file\fB] \fImidifile ... +.B wildmidi [\-behlvwn] [\-c \fIconfig\-file\fB] [\-d \fIaudiodev\fB] [\-m \fIvolume\-level\fB] [\-o \fIwav\-file\fB] \fImidifile ... .PP .SH DESCRIPTION This is a demonstration program to show the capabilities of libWildMidi. .PP \fImidifile\fP is processed by libWildMidi and the resulting audio is output by the player. .PP -You can have more than one \fImidifile\fB on the command line and \fBwildmidi\fP will pass them to libWildMidi for processing, one after the other. You can also use wildcards, for example: \fBwildmidi *.mid\fP +You can have more than one \fImidifile\fP on the command line and \fBwildmidi\fP will pass them to libWildMidi for processing, one after the other. You can also use wildcards, for example: \fBwildmidi *.mid\fP .PP .SH OPTIONS .IP "\fB\-b\fP | \fB\-\-reverb\fP" Turns on an 8 point reverb engine that adds depth to the final mix. .P -.IP "\fB\-c\fP \fIconfig-file\fP | \fB\-\-config_file\fP \fIconfig\-file\fP" -Uses the configuration file stated by \fIconfig\-file\fP instead of /etc/wildmidi.cfg +.IP "\fB\-c\fP \fIconfig\-file\fP | \fB\-\-config\fP \fIconfig\-file\fP" +Uses the configuration file stated by \fIconfig\-file\fP instead of /etc/wildmidi/wildmidi.cfg .PP -.IP "\fB\-d\fP \fIdevice\fP | \fB\-\-auddev=\fIdevice\fP" -Send the audio to \fIdevice\fP instead of the default. ALSA defaults to the system "default" while OSS defaults to "/dev/dsp". Win32 environments ignore this option. +.IP "\fB\-d\fP \fIaudiodev\fP | \fB\-\-device=\fIaudiodev\fP" +Send the audio to \fIaudiodev\fP instead of the default. ALSA defaults to the system "default" while OSS defaults to "/dev/dsp". Other environments do not support this option. .PP -.IP "\fB\-e\fP | \fB\-\-enhanced_resample\fP" +.IP "\fB\-e\fP | \fB\-\-enhanced\fP" Switches to a gauss based resampling algorithm which can improve the shape of the sound. .PP .IP "\fB\-h\fP | \fB\-\-help\fP" @@ -37,7 +37,7 @@ Displays command line options .IP "\fB\-l\fP | \fB\-\-log_vol\fP" Some MIDI files have been recorded on hardware that uses a volume curve, making them sound really badly mixed on other MIDI devices. Use this option to use volume curves. .PP -.IP "\fB\-m\fP \fIvolume\-level\fP | \fB\-\-master_volume=\fIvolume\-level\fP" +.IP "\fB\-m\fP \fIvolume\-level\fP | \fB\-\-mastervol=\fIvolume\-level\fP" Set the overall volume level to \fIvolume\-level\fP. The minimum is 0 and the maximum is 127, with the default being 100. .PP .IP "\fB\-o\fP \fIwav\-file\fP | \fB\-\-wavout=\fIwav\-file\fP" @@ -46,6 +46,12 @@ Records the audio in wav format to \fIwav-file\fP. .IP "\fB\-r\fP \fIsndrate\fP | \fB\-\-rate=\fIsndrate\fP" Set the audio output rate to \fIsndrate\fP. The default rate is 32072. .PP +.IP "\fB\-w\fP | \fB\-\-wholetempo\fP" +Round down tempo to whole number +.PP +.IP "\fB\-n\fP | \fB\-\-roundtempo\fP" +Round tempo to nearest whole number +.PP .IP "\fB\-v\fP | \fB\-\-version\fP" Display version and copyright information .PP diff --git a/docs/man/man3/WildMidi_SetOption.3 b/docs/man/man3/WildMidi_SetOption.3 index 4f544fc2..671c4027 100644 --- a/docs/man/man3/WildMidi_SetOption.3 +++ b/docs/man/man3/WildMidi_SetOption.3 @@ -36,7 +36,7 @@ To turn on an option, repeat that option here. To turn off an option, do not put .IP "Example: To turn on Reverb" WildMidi_SetOption(handle, WM_MO_REVERB, WM_MO_REVERB); .IP "Example: To turn off Reverb" -WildMidi_SetOption(handle, WM_MO_REVERB, NULL); +WildMidi_SetOption(handle, WM_MO_REVERB, 0); .IP "Example: To turn on Reverb and Enhanced Resampling" WildMidi_SetOption(handle, (WM_MO_REVERB | WM_MO_ENHANCED_RESAMPLING), (WM_MO_REVERB | WM_MO_ENHANCED_RESAMPLING)); .PP diff --git a/docs/man/man5/wildmidi.cfg.5 b/docs/man/man5/wildmidi.cfg.5 index 1a155305..73981c8f 100644 --- a/docs/man/man5/wildmidi.cfg.5 +++ b/docs/man/man5/wildmidi.cfg.5 @@ -46,7 +46,7 @@ The patches following this setting belong to MIDI instrument bank \fIN\fP. .IP "\fBdrumset\fP \fIN\fP" The patches following this setting belong to MIDI drum bank \fIN\fP. .PP -.IP "\fIpatchno\fP \fIpatchfile\fP [\fBamp=\fP\fIvolume\fP] [\fBnote=\fP\fImiodinte\fP] [\fBkeep=loop\fP] [\fBkeep=env\fP] [\fBremove=sustain\fP] [\fBremove=clamped\fP] [\fBenv_level\fP[\fI0-5\fP]\fB=\fP\fIlevel\fP] [\fBenv_time\fP[\fI0-5\fP]\fB=\fP\fItime\fP]" +.IP "\fIpatchno\fP \fIpatchfile\fP [\fBamp=\fP\fIvolume\fP] [\fBnote=\fP\fImiodinte\fP] [\fBkeep=loop\fP] [\fBkeep=env\fP] [\fBremove=sustain\fP] [\fBremove=clamped\fP] [\fBenv_level\fP[\fI0\-5\fP]\fB=\fP\fIlevel\fP] [\fBenv_time\fP[\fI0\-5\fP]\fB=\fP\fItime\fP]" .PP Example: 0 acpiano.pat amp=110 .PP @@ -75,12 +75,12 @@ Do note hold the note after the 3rd envelope until note off, which is what happe .IP "\fBremove=clamped\fP" Do not jump to 6th envelope on note off, which is what happens if the clamped bit is set in the patch file. .PP -.IP "\fBenv_level\fP[\fI0-5\fP]\fB=\fP\fIlevel\fP" +.IP "\fBenv_level\fP[\fI0\-5\fP]\fB=\fP\fIlevel\fP" Set the envelope level to \fIlevel\fP with 1.0 being maximum, and 0.0 being minimum. .IP -Example: set 5th envelope level to 10% \- \fBenv_level[0-5]=\fP0.1 +Example: set 5th envelope level to 10% \- \fBenv_level[0\-5]=\fP0.1 .PP -.IP "\fBenv_time\fP[\fI0-5\fP]\fB=\fP\fItime\fP" +.IP "\fBenv_time\fP[\fI0\-5\fP]\fB=\fP\fItime\fP" Set the envelope time to \fItime\fP with a resolution of 1/1000th of a second. This setting is the time it should take for the envelope to reach maximum level. .IP Example: set 1st envelope time to 1sec \- \fBenv_time0=\fP1000 @@ -89,12 +89,12 @@ Example: set 3rd envelope time to 0.5secs \- \fBenv_time2=\fP500 .RE .PP .IP "\fBreverb_room_width\fP \fIfval\fP" -Set the room width for the reverb engine in meters. \fIfval\fP is a float value in meters. Minimum setting is 1.0 meters, maximum setting is 100.0 meters, and default is 15.0 meters. +Set the room width for the reverb engine in meters. \fIfval\fP is a float value in meters. Minimum setting is 1.0 meter, maximum setting is 100.0 meters, and default is 15.0 meters. .IP Example: set room width to 30 meters \- \fBreverb_room_width 30\fP .PP .IP "\fBreverb_room_length\fP \fIfval\fP" -Set the room length for the reverb engine in meters. \fIfval\fP is a float value in meters. . Minimum setting is 1.0 meters, maximum setting is 100.0 meters, and default is 20.0 meters. +Set the room length for the reverb engine in meters. \fIfval\fP is a float value in meters. Minimum setting is 1.0 meter, maximum setting is 100.0 meters, and default is 20.0 meters. .IP Example: set room length to 40 meters \- \fBreverb_room_length 40\fP .PP diff --git a/include/common.h b/include/common.h index 177da9e4..5d42da12 100644 --- a/include/common.h +++ b/include/common.h @@ -55,8 +55,8 @@ struct _sample { uint32_t freq_high; uint32_t freq_root; uint8_t modes; - uint32_t env_rate[7]; - uint32_t env_target[7]; + int32_t env_rate[7]; + int32_t env_target[7]; uint32_t inc_div; int16_t *data; struct _sample *next; @@ -91,9 +91,4 @@ struct _patch { #define M_LN2 0.69314718055994530942 #endif -/* Define this if the GCC __builtin_expect keyword is available */ -#ifndef HAVE___BUILTIN_EXPECT -#define __builtin_expect(x,c) x -#endif - #endif /* __COMMON_H */ diff --git a/include/config.h.cmake b/include/config.h.cmake index a0c4ac5f..d6ce76bd 100644 --- a/include/config.h.cmake +++ b/include/config.h.cmake @@ -45,6 +45,9 @@ /* Define if the compiler has the `__builtin_expect' built-in function */ #cmakedefine HAVE___BUILTIN_EXPECT +#ifndef HAVE___BUILTIN_EXPECT +#define __builtin_expect(x,c) x +#endif /* define this if you are running a bigendian system (motorola, sparc, etc) */ #cmakedefine WORDS_BIGENDIAN 1 @@ -59,10 +62,8 @@ #cmakedefine HAVE_LINUX_SOUNDCARD_H #cmakedefine HAVE_SYS_SOUNDCARD_H #cmakedefine HAVE_MACHINE_SOUNDCARD_H +#cmakedefine HAVE_SOUNDCARD_H + #cmakedefine AUDIODRV_ALSA #cmakedefine AUDIODRV_OSS #cmakedefine AUDIODRV_OPENAL - -/* set some windows magic */ -#define WIN32_LEAN_AND_MEAN - diff --git a/include/getopt_long.h b/include/getopt_long.h index 0e2b0adf..6d57a745 100644 --- a/include/getopt_long.h +++ b/include/getopt_long.h @@ -1,33 +1,78 @@ -#ifndef __GETOPT_LONG_H__ -#define __GETOPT_LONG_H__ +/* $OpenBSD: getopt.h,v 1.3 2013/11/22 21:32:49 millert Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ -#ifdef __cplusplus -extern "C" { -#endif +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ -extern int opterr; /* if error message should be printed */ -extern int optind; /* index into parent argv vector */ -extern int optopt; /* character checked for validity */ -extern int optreset; /* reset getopt */ -extern char *optarg; /* argument associated with option */ +#ifndef _GETOPT_H_ +#define _GETOPT_H_ +/* + * GNU-like getopt_long() + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 -struct option -{ - const char *name; - int has_arg; - int *flag; - int val; +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; }; -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 +#if defined(__cplusplus) +extern "C" { +#endif +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED_ +#define _GETOPT_DEFINED_ +int getopt(int, char * const *, const char *); - int getopt(int, char**, char*); - int getopt_long(int, char**, char*, const struct option*, int*); +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +#endif -#ifdef __cplusplus +#if defined(__cplusplus) } #endif -#endif /* __GETOPT_LONG_H__ */ +#endif /* !_GETOPT_H_ */ diff --git a/include/gus_pat.h b/include/gus_pat.h index adc66f62..f9506073 100644 --- a/include/gus_pat.h +++ b/include/gus_pat.h @@ -27,17 +27,48 @@ #ifndef __GUS_PAT_H #define __GUS_PAT_H -/* Guspat Envelope Rate Timings - * Row 1 = (4095.0/(x*(1.0/(1.6*14.0)))) / 1000000.0 - * Row 2 = (4095.0/(x*((1.0/(1.6*14.0))/8.0)))) / 1000000.0 - * Row 3 = (4095.0/(x*((1.0/(1.6*14.0))/64.0))) / 1000000.0 - * Row 4 = (4095.0/(x*((1.0/(1.6*14.0))/512.0))) / 1000000.0 - */ +/* Guspat Envelope Rate Timings */ + static float env_time_table[] = { - 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, - 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, - 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, - 0.0f, 46.964736000f, 23.482368000f, 15.654912000f, 11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f +/* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */ + 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, + 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, + 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, + 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, + 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, + 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, + 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, + 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, + +/* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */ + 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, + 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, + 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, + 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, + 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, + 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, + 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, + 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, + +/* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */ + 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, + 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, + 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, + 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, + 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, + 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, + 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, + 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, + +/* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */ + 0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, + 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, + 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, + 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, + 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, + 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, + 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, + 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f }; extern struct _sample * load_gus_pat (const char *filename, int fix_release); diff --git a/include/mus.h b/include/mus.h new file mode 100644 index 00000000..2f879588 --- /dev/null +++ b/include/mus.h @@ -0,0 +1,30 @@ +/* + MUS2MIDI: DMX (DOOM) MUS to MIDI Library Header + + Copyright (C) 2014 Bret Curtis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef MUSLIB_H +#define MUSLIB_H + +#include + +int mus2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize); + +#endif /* MUSLIB_H */ diff --git a/include/wildmidi_lib.h b/include/wildmidi_lib.h index 1590eb9a..bca8b802 100644 --- a/include/wildmidi_lib.h +++ b/include/wildmidi_lib.h @@ -24,6 +24,9 @@ . */ +#ifndef WILDMIDI_LIB_H +#define WILDMIDI_LIB_H + /* library version number */ #define LIBWILDMIDI_VER_MAJOR 0L #define LIBWILDMIDI_VER_MINOR 4L @@ -38,7 +41,7 @@ #define WM_MO_ENHANCED_RESAMPLING 0x0002 #define WM_MO_REVERB 0x0004 #define WM_MO_WHOLETEMPO 0x8000 -#define WM_MO_ROUNDTEMPO 0xA000 +#define WM_MO_ROUNDTEMPO 0x2000 /* set our symbol export visiblity */ #if defined _WIN32 || defined __CYGWIN__ @@ -93,6 +96,7 @@ WM_SYMBOL midi * WildMidi_Open (const char *midifile); WM_SYMBOL midi * WildMidi_OpenBuffer (uint8_t *midibuffer, uint32_t size); WM_SYMBOL int WildMidi_GetOutput (midi *handle, int8_t *buffer, uint32_t size); WM_SYMBOL int WildMidi_SetOption (midi *handle, uint16_t options, uint16_t setting); +WM_SYMBOL void * WildMidi_ConvertToMidi (const char *file, uint32_t *size); WM_SYMBOL struct _WM_Info * WildMidi_GetInfo (midi * handle); WM_SYMBOL int WildMidi_FastSeek (midi * handle, unsigned long int *sample_pos); WM_SYMBOL int WildMidi_Close (midi * handle); @@ -104,3 +108,6 @@ WM_SYMBOL int WildMidi_Shutdown (void); #if defined(__cplusplus) } #endif + +#endif /* WILDMIDI_LIB_H */ + diff --git a/include/wm_error.h b/include/wm_error.h index 872ad973..f57041da 100644 --- a/include/wm_error.h +++ b/include/wm_error.h @@ -27,21 +27,27 @@ #ifndef __WM_ERROR_H #define __WM_ERROR_H -#define WM_ERR_MEM 0 -#define WM_ERR_STAT 1 -#define WM_ERR_LOAD 2 -#define WM_ERR_OPEN 3 -#define WM_ERR_READ 4 -#define WM_ERR_INVALID 5 -#define WM_ERR_CORUPT 6 -#define WM_ERR_NOT_INIT 7 -#define WM_ERR_INVALID_ARG 8 -#define WM_ERR_ALR_INIT 9 -#define WM_ERR_LONGFIL 10 +enum { + WM_ERR_NONE = 0, + WM_ERR_MEM, + WM_ERR_STAT, + WM_ERR_LOAD, + WM_ERR_OPEN, + WM_ERR_READ, + WM_ERR_INVALID, + WM_ERR_CORUPT, + WM_ERR_NOT_INIT, + WM_ERR_INVALID_ARG, + WM_ERR_ALR_INIT, + WM_ERR_NOT_MIDI, + WM_ERR_LONGFIL, + + WM_ERR_MAX +}; extern void WM_ERROR_NEW(const char * wmfmt, ...) #ifdef __GNUC__ - __attribute__((format(printf, 1, 2))) /* for tracking down formatting issues */ + __attribute__((format(printf, 1, 2))) #endif ; extern void WM_ERROR(const char * func, unsigned int lne, int wmerno, diff --git a/include/wm_tty.h b/include/wm_tty.h new file mode 100644 index 00000000..dab559ee --- /dev/null +++ b/include/wm_tty.h @@ -0,0 +1,36 @@ +/* + wm_tty.h - unix termios code for player + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#ifndef wm_tty_h +#define wm_tty_h + +void wm_inittty(void); +void wm_resetty(void); + +#if defined(_WIN32) || defined(__DJGPP__) +#define wm_inittty() do {} while (0) +#define wm_resetty() do {} while (0) +#endif /* !_WIN32, !__DJGPP__ */ + +#endif /* wm_tty_h */ diff --git a/include/xmidi.h b/include/xmidi.h new file mode 100644 index 00000000..6d46009b --- /dev/null +++ b/include/xmidi.h @@ -0,0 +1,42 @@ +/* + XMIDI: Miles XMIDI to MID Library Header + + Copyright (C) 2001 Ryan Nunn + Copyright (C) 2014 Bret Curtis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* XMIDI Converter */ + +#ifndef XMIDILIB_H +#define XMIDILIB_H + +#include + +/* Conversion types for Midi files */ +#define XMIDI_CONVERT_NOCONVERSION 0 +#define XMIDI_CONVERT_MT32_TO_GM 1 +#define XMIDI_CONVERT_MT32_TO_GS 2 +#define XMIDI_CONVERT_MT32_TO_GS127 3 /* This one is broken, don't use */ +#define XMIDI_CONVERT_MT32_TO_GS127DRUM 4 /* This one is broken, don't use */ +#define XMIDI_CONVERT_GS127_TO_GS 5 + +int xmi2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize, + int convert_type); + +#endif /* XMIDILIB_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05f0327b..b89d15d6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,8 @@ SET(wildmidi_library_SRCS wildmidi_lib.c reverb.c gus_pat.c + xmidi.c + mus.c ) # Headers: @@ -84,9 +86,9 @@ ENDIF() # Set our default and then look at the possible locations SET(WILDMIDILIB "${CMAKE_BINARY_DIR}/lib${LIBRARY_DYN_NAME}.so") -# MS Visual Studio +# MS Visual Studio IF(MSVC) - SET(GETOPT getopt.c getopt_long.c) + SET(GETOPT getopt_long.c) SET(WILDMIDILIB "${CMAKE_BINARY_DIR}\\${CMAKE_BUILD_TYPE}\\${LIBRARY_DYN_NAME}.lib") ENDIF(MSVC) @@ -106,6 +108,7 @@ ENDIF(CMAKE_GENERATOR STREQUAL "Xcode") IF(WANT_PLAYER) SET(wildmidi_executable_SRCS ${GETOPT} + wm_tty.c wildmidi.c ) @@ -123,6 +126,8 @@ IF(WANT_PLAYER) TARGET_INCLUDE_DIRECTORIES(wildmidi PRIVATE ${ALSA_INCLUDE_DIR} ) + ELSEIF(AUDIODRV_OSS) + # no special header paths ENDIF() TARGET_LINK_LIBRARIES(wildmidi @@ -136,9 +141,9 @@ ENDIF(WANT_PLAYER) IF(UNIX AND NOT APPLE) # install our libraries IF(WANT_STATIC) - INSTALL(TARGETS wildmidi_static DESTINATION "lib/${CMAKE_LIBRARY_ARCHITECTURE}") + INSTALL(TARGETS wildmidi_static DESTINATION "lib${LIB_SUFFIX}/${CMAKE_LIBRARY_ARCHITECTURE}") ENDIF(WANT_STATIC) - INSTALL(TARGETS wildmidi_dynamic DESTINATION "lib/${CMAKE_LIBRARY_ARCHITECTURE}") + INSTALL(TARGETS wildmidi_dynamic DESTINATION "lib${LIB_SUFFIX}/${CMAKE_LIBRARY_ARCHITECTURE}") # install our player if asked for IF(WANT_PLAYER) diff --git a/src/DevTest.c b/src/DevTest.c index 57487ffc..e3bd2dcb 100644 --- a/src/DevTest.c +++ b/src/DevTest.c @@ -1,9 +1,7 @@ /* - DevTest.c - - Display Information about the Gravis Ultrasound patch file. - + DevTest.c: Display Information about the Gravis Ultrasound patch file. NOTE: This file is intended for developer use to aide in feature development, and bug hunting. + COMPILING: gcc -Wall -W -O2 -o devtest DevTest.c Copyright (C) Chris Ison 2001-2011 Copyright (C) Bret Curtis 2013-2014 @@ -41,70 +39,62 @@ # include #endif -static struct option const long_options[] = { { "debug-level", 1, 0, 'd' }, { - "version", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { NULL, 0, NULL, 0 } }; - -static float env_time_table[] = { 0.0, 0.091728000, 0.045864000, 0.030576000, - 0.022932000, 0.018345600, 0.015288000, 0.013104000, 0.011466000, - 0.010192000, 0.009172800, 0.008338909, 0.007644000, 0.007056000, - 0.006552000, 0.006115200, 0.005733000, 0.005395765, 0.005096000, - 0.004827789, 0.004586400, 0.004368000, 0.004169455, 0.003988174, - 0.003822000, 0.003669120, 0.003528000, 0.003397333, 0.003276000, - 0.003163034, 0.003057600, 0.002958968, 0.002866500, 0.002779636, - 0.002697882, 0.002620800, 0.002548000, 0.002479135, 0.002413895, - 0.002352000, 0.002293200, 0.002237268, 0.002184000, 0.002133209, - 0.002084727, 0.002038400, 0.001994087, 0.001951660, 0.001911000, - 0.001872000, 0.001834560, 0.001798588, 0.001764000, 0.001730717, - 0.001698667, 0.001667782, 0.001638000, 0.001609263, 0.001581517, - 0.001554712, 0.001528800, 0.001503738, 0.001479484, 0.001456000, 0.0, - 0.733824000, 0.366912000, 0.244608000, 0.183456000, 0.146764800, - 0.122304000, 0.104832000, 0.091728000, 0.081536000, 0.073382400, - 0.066711273, 0.061152000, 0.056448000, 0.052416000, 0.048921600, - 0.045864000, 0.043166118, 0.040768000, 0.038622316, 0.036691200, - 0.034944000, 0.033355636, 0.031905391, 0.030576000, 0.029352960, - 0.028224000, 0.027178667, 0.026208000, 0.025304276, 0.024460800, - 0.023671742, 0.022932000, 0.022237091, 0.021583059, 0.020966400, - 0.020384000, 0.019833081, 0.019311158, 0.018816000, 0.018345600, - 0.017898146, 0.017472000, 0.017065674, 0.016677818, 0.016307200, - 0.015952696, 0.015613277, 0.015288000, 0.014976000, 0.014676480, - 0.014388706, 0.014112000, 0.013845736, 0.013589333, 0.013342255, - 0.013104000, 0.012874105, 0.012652138, 0.012437695, 0.012230400, - 0.012029902, 0.011835871, 0.011648000, 0.0, 5.870592000, 2.935296000, - 1.956864000, 1.467648000, 1.174118400, 0.978432000, 0.838656000, - 0.733824000, 0.652288000, 0.587059200, 0.533690182, 0.489216000, - 0.451584000, 0.419328000, 0.391372800, 0.366912000, 0.345328941, - 0.326144000, 0.308978526, 0.293529600, 0.279552000, 0.266845091, - 0.255243130, 0.244608000, 0.234823680, 0.225792000, 0.217429333, - 0.209664000, 0.202434207, 0.195686400, 0.189373935, 0.183456000, - 0.177896727, 0.172664471, 0.167731200, 0.163072000, 0.158664649, - 0.154489263, 0.150528000, 0.146764800, 0.143185171, 0.139776000, - 0.136525395, 0.133422545, 0.130457600, 0.127621565, 0.124906213, - 0.122304000, 0.119808000, 0.117411840, 0.115109647, 0.112896000, - 0.110765887, 0.108714667, 0.106738036, 0.104832000, 0.102992842, - 0.101217103, 0.099501559, 0.097843200, 0.096239213, 0.094686968, - 0.093184000, 0.0, 46.964736000, 23.482368000, 15.654912000, - 11.741184000, 9.392947200, 7.827456000, 6.709248000, 5.870592000, - 5.218304000, 4.696473600, 4.269521455, 3.913728000, 3.612672000, - 3.354624000, 3.130982400, 2.935296000, 2.762631529, 2.609152000, - 2.471828211, 2.348236800, 2.236416000, 2.134760727, 2.041945043, - 1.956864000, 1.878589440, 1.806336000, 1.739434667, 1.677312000, - 1.619473655, 1.565491200, 1.514991484, 1.467648000, 1.423173818, - 1.381315765, 1.341849600, 1.304576000, 1.269317189, 1.235914105, - 1.204224000, 1.174118400, 1.145481366, 1.118208000, 1.092203163, - 1.067380364, 1.043660800, 1.020972522, 0.999249702, 0.978432000, - 0.958464000, 0.939294720, 0.920877176, 0.903168000, 0.886127094, - 0.869717333, 0.853904291, 0.838656000, 0.823942737, 0.809736828, - 0.796012475, 0.782745600, 0.769913705, 0.757495742, 0.745472000 }; +static struct option const long_options[] = { + { "debug-level", 1, 0, 'd' }, + { "version", 0, 0, 'v' }, + { "help", 0, 0, 'h' }, + { NULL, 0, NULL, 0 } +}; + +static float env_time_table[] = { + 0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f, + 0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f, + 0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f, + 0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f, + 0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f, + 0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f, + 0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f, + 0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f, + + 0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f, + 0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f, + 0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f, + 0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f, + 0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f, + 0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f, + 0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f, + 0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f, + + 0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f, + 0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f, + 0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f, + 0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f, + 0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f, + 0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f, + 0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f, + 0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f, + + 0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f, + 5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f, + 2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f, + 1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f, + 1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f, + 1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f, + 0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f, + 0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f +}; + +/* the following hardcoded to avoid the need for a config.h : */ +static const char *PACKAGE_URL = "http://www.mindwerks.net/projects/wildmidi/"; +static const char *PACKAGE_BUGREPORT = "https://github.com/Mindwerks/wildmidi/issues"; +static const char *PACKAGE_VERSION = "0.3"; void do_version(void) { - printf("DevTest for WildMIDI %s - For testing purposes only\n\n", - PACKAGE_VERSION); - printf( - "Copyright (C) Chris Ison 2001-2010 wildcode@users.sourceforge.net\n\n"); + printf("DevTest for WildMIDI %s - For testing purposes only\n\n", PACKAGE_VERSION); + printf("Copyright (C) Chris Ison 2001-2010 wildcode@users.sourceforge.net\n\n"); printf("DevTest comes with ABSOLUTELY NO WARRANTY\n"); printf("This is free software, and you are welcome to redistribute it\n"); - printf( - "under the terms and conditions of the GNU General Public License version 3.\n"); + printf("under the terms and conditions of the GNU General Public License version 3.\n"); printf("For more information see COPYING\n\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); printf("WildMIDI homepage at %s\n", PACKAGE_URL); @@ -125,70 +115,67 @@ DT_BufferFile(const char *filename, unsigned long int *size) { char *ret_data = NULL; struct stat buffer_stat; #ifndef _WIN32 - char *home = NULL; + const char *home = NULL; struct passwd *pwd_ent; char buffer_dir[1024]; #endif - char *buffer_file = malloc(strlen(filename) + 1); - - if (buffer_file == NULL) { - printf("Unable to get ram to expand %s: %s\n", filename, - strerror(errno)); - return NULL ; - } + char *buffer_file = NULL; - strcpy(buffer_file, filename); #ifndef _WIN32 - if (strncmp(buffer_file, "~/", 2) == 0) { + if (strncmp(filename, "~/", 2) == 0) { if ((pwd_ent = getpwuid(getuid()))) { home = pwd_ent->pw_dir; } else { home = getenv("HOME"); } if (home) { - buffer_file = realloc(buffer_file, - (strlen(buffer_file) + strlen(home) + 1)); + buffer_file = malloc(strlen(filename) + strlen(home) + 1); if (buffer_file == NULL) { printf("Unable to get ram to expand %s: %s\n", filename, strerror(errno)); - free(buffer_file); - return NULL ; + return NULL; } - memmove((buffer_file + strlen(home)), (buffer_file + 1), - (strlen(buffer_file))); - strncpy(buffer_file, home, strlen(home)); + strcpy(buffer_file, home); + strcat(buffer_file, filename + 1); } - } else if (buffer_file[0] != '/') { + } else if (filename[0] != '/') { ret_data = getcwd(buffer_dir, 1024); - if (buffer_dir[strlen(buffer_dir) - 1] != '/') { - buffer_dir[strlen(buffer_dir) + 1] = '\0'; - buffer_dir[strlen(buffer_dir)] = '/'; + if (ret_data != NULL) + buffer_file = malloc(strlen(filename) + strlen(buffer_dir) + 2); + if (buffer_file == NULL || ret_data == NULL) { + printf("Unable to get ram to expand %s: %s\n", filename, + strerror(errno)); + return NULL; } - buffer_file = realloc(buffer_file, - (strlen(buffer_file) + strlen(buffer_dir) + 1)); + strcpy(buffer_file, buffer_dir); + if (buffer_dir[strlen(buffer_dir) - 1] != '/') + strcat(buffer_file, "/"); + strcat(buffer_file, filename); + } +#endif + + if (buffer_file == NULL) { + buffer_file = malloc(strlen(filename) + 1); if (buffer_file == NULL) { printf("Unable to get ram to expand %s: %s\n", filename, strerror(errno)); - free(buffer_file); - return NULL ; + return NULL; } - memmove((buffer_file + strlen(buffer_dir)), buffer_file, - strlen(buffer_file) + 1); - strncpy(buffer_file, buffer_dir, strlen(buffer_dir)); + strcpy(buffer_file, filename); } -#endif + if (stat(buffer_file, &buffer_stat)) { printf("Unable to stat %s: %s\n", filename, strerror(errno)); free(buffer_file); - return NULL ; + return NULL; } *size = buffer_stat.st_size; data = malloc(*size); if (data == NULL) { printf("Unable to get ram for %s: %s\n", filename, strerror(errno)); free(buffer_file); - return NULL ; + return NULL; } #ifdef _WIN32 if ((buffer_fd = open(buffer_file,(O_RDONLY | O_BINARY))) == -1) { @@ -198,14 +185,14 @@ DT_BufferFile(const char *filename, unsigned long int *size) { printf("Unable to open %s: %s\n", filename, strerror(errno)); free(buffer_file); free(data); - return NULL ; + return NULL; } if (read(buffer_fd, data, *size) != buffer_stat.st_size) { printf("Unable to read %s: %s\n", filename, strerror(errno)); free(buffer_file); free(data); close(buffer_fd); - return NULL ; + return NULL; } close(buffer_fd); free(buffer_file); @@ -213,7 +200,7 @@ DT_BufferFile(const char *filename, unsigned long int *size) { } int test_midi(unsigned char * midi_data, unsigned long int midi_size, - unsigned int verbose) { + int verbose) { unsigned int tmp_val; unsigned int track_size; unsigned char *next_track; @@ -312,7 +299,7 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, return -1; } - //Slow but needed for accuracy + /* Slow but needed for accuracy */ beats_per_minute = 60000000.0 / (float) tempo; microseconds_per_pulse = (float) tempo / (float) divisions; pulses_per_second = 1000000.0 / microseconds_per_pulse; @@ -320,7 +307,6 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, if (verbose) printf("BPM: %f, SPD @ 44100: %f\n", beats_per_minute, samples_per_delta_f); - } for (i = 0; i < no_tracks; i++) { if (midi_size < 8) { @@ -379,11 +365,11 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, } midi_size--; delta_accum += delta; - // tempo microseconds per quarter note - // divisions pulses per quarter note - //if (verbose) printf("Est Seconds: %f\n",(((float)tempo/(float)divisions*(float)delta_accum)/1000000.0)); + /* tempo microseconds per quarter note + * divisions pulses per quarter note */ + /*if (verbose) printf("Est Seconds: %f\n",(((float)tempo/(float)divisions*(float)delta_accum)/1000000.0));*/ if (verbose) - printf("Delta: %i, Accumilated Delta: %i\n", delta, + printf("Delta: %i, Accumilated Delta: %ld\n", delta, delta_accum); if (*midi_data < 0x80) { @@ -539,7 +525,7 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, if (sysex_store[4] == 0x40) { if (((sysex_store[5] & 0xF0) == 0x10) && (sysex_store[6] == 0x15)) { - // Roland Drum Track Setting + /* Roland Drum Track Setting */ unsigned char sysex_ch = 0x0F & sysex_store[5]; if (sysex_ch == 0x00) { @@ -548,13 +534,12 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, sysex_ch -= 1; } if (verbose) - printf( - "Additional Drum Channel(0x%02x) Setting: 0x%02x\n", + printf("Additional Drum Channel(0x%02x) Setting: 0x%02x\n", sysex_ch, sysex_store[7]); } else if ((sysex_store[5] == 0x00) && (sysex_store[6] == 0x7F) && (sysex_store[7] == 0x00)) { - // Roland GS Reset + /* Roland GS Reset */ if (verbose) printf("GS Reset\n"); } else { @@ -630,7 +615,6 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, if (verbose) printf("BPM: %f, SPD @ 44100: %f\n", beats_per_minute, samples_per_delta_f); - } else { if (verbose) printf("Meta Event: Unsupported (%i)\n", @@ -644,8 +628,7 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, meta_length = (meta_length << 7) | (*midi_data & 0x7F); midi_data++; if (midi_size == 0) { - printf( - "Corrupt Midi, Missing or Corrupt Track Data\n"); + printf("Corrupt Midi, Missing or Corrupt Track Data\n"); return -1; } midi_size--; @@ -667,8 +650,7 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, break; } if (midi_data > next_track) { - printf( - "Corrupt Midi, Track Data went beyond track boundries.\n"); + printf("Corrupt Midi, Track Data went beyond track boundries.\n"); return -1; } } @@ -677,16 +659,10 @@ int test_midi(unsigned char * midi_data, unsigned long int midi_size, } int test_guspat(unsigned char * gus_patch, unsigned long int filesize, - unsigned int verbose) { + int verbose) { unsigned long int gus_ptr = 0; unsigned char no_of_samples = 0; - unsigned long int tmp_lint = 0; - unsigned short int tmp_sint = 0; - unsigned char tmp_char = 0; - - unsigned int i = 0; - if (filesize < 239) { printf("File too short\n"); return -1; @@ -759,8 +735,7 @@ int test_guspat(unsigned char * gus_patch, unsigned long int filesize, printf("Sustain Level: %i, Sustain Time: %fsecs\n", gus_patch[gus_ptr + 45], env_time_table[gus_patch[gus_ptr + 39]]); - printf( - "Sustained Release Level: %i, Sustained Release Time: %fsecs\n", + printf("Sustained Release Level: %i, Sustained Release Time: %fsecs\n", gus_patch[gus_ptr + 46], env_time_table[gus_patch[gus_ptr + 40]]); printf("Normal Release Level: %i, Normal Release Time: %fsecs\n", @@ -769,17 +744,13 @@ int test_guspat(unsigned char * gus_patch, unsigned long int filesize, printf("Clamped Release Level: %i, Clamped Release Time: %fsecs\n", gus_patch[gus_ptr + 48], env_time_table[gus_patch[gus_ptr + 42]]); - } if (env_time_table[gus_patch[gus_ptr + 40]] < env_time_table[gus_patch[gus_ptr + 41]]) { - printf( - "WARNING!! Normal release envelope longer than sustained release envelope\n"); - printf( - " Caused by patch editor not following the file format set by Gravis\n"); - printf( - " Add guspat_editor_author_cant_read_so_fix_release_time_for_me to top of wildmidi.cfg\n"); + printf("WARNING!! Normal release envelope longer than sustained release envelope\n"); + printf(" Caused by patch editor not following the file format set by Gravis\n"); + printf(" Add guspat_editor_author_cant_read_so_fix_release_time_for_me to top of wildmidi.cfg\n"); } if (verbose) { @@ -811,13 +782,14 @@ int test_guspat(unsigned char * gus_patch, unsigned long int filesize, | (gus_patch[gus_ptr + 9] << 8) | gus_patch[gus_ptr + 8]); } while (--no_of_samples); + return 0; } int main(int argc, char ** argv) { int i; int option_index = 0; - unsigned int verbose = 0; + int verbose = 0; int testret = 0; unsigned char *filebuffer = NULL; @@ -829,12 +801,12 @@ int main(int argc, char ** argv) { if (i == -1) break; switch (i) { - case 'd': // Verbose + case 'd': /* Verbose */ verbose = atoi(optarg); break; - case 'v': // Version + case 'v': /* Version */ return 0; - case 'h': // help + case 'h': /* help */ do_help(); return 0; default: diff --git a/src/file_io.c b/src/file_io.c index c2e36631..a100e82f 100644 --- a/src/file_io.c +++ b/src/file_io.c @@ -36,6 +36,12 @@ #ifdef _WIN32 #include +#undef close +#define close _close +#undef open +#define open _open +#undef read +#define read _read #elif defined(__DJGPP__) #include #include @@ -59,7 +65,7 @@ void *WM_BufferFile(const char *filename, uint32_t *size) { int buffer_fd; - void *data; + uint8_t *data; #ifdef __DJGPP__ struct ffblk f; #else @@ -138,7 +144,8 @@ void *WM_BufferFile(const char *filename, uint32_t *size) { return NULL; } - data = malloc(*size); + /* +1 needed for parsing text files without a newline at the end */ + data = (uint8_t *) malloc(*size + 1); if (data == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, errno); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno); @@ -159,6 +166,7 @@ void *WM_BufferFile(const char *filename, uint32_t *size) { close(buffer_fd); return NULL; } + data[*size] = '\0'; close(buffer_fd); free(buffer_file); diff --git a/src/getopt.c b/src/getopt.c deleted file mode 100644 index 2b21c7be..00000000 --- a/src/getopt.c +++ /dev/null @@ -1,152 +0,0 @@ -/* $NetBSD: getopt.c,v 1.16 1999/12/02 13:15:56 kleink Exp $ */ - -/* - * Copyright (c) 1987, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; -#endif - -#include -#include -#include -#include - -#define __P(x) x -#define _DIAGASSERT(x) assert(x) - -#ifdef __weak_alias -__weak_alias(getopt,_getopt); -#endif - - -int opterr = 1, /* if error message should be printed */ - optind = 1, /* index into parent argv vector */ - optopt, /* character checked for validity */ - optreset; /* reset getopt */ -char *optarg; /* argument associated with option */ - -static char * _progname __P((char *)); -int getopt_internal __P((int, char * const *, const char *)); - -static char * -_progname(nargv0) - char * nargv0; -{ - char * tmp; - - _DIAGASSERT(nargv0 != NULL); - - tmp = strrchr(nargv0, '/'); - if (tmp) - tmp++; - else - tmp = nargv0; - return(tmp); -} - -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" - -/* - * getopt -- - * Parse argc/argv argument vector. - */ -int -getopt(nargc, nargv, ostr) - int nargc; - char * const nargv[]; - const char *ostr; -{ - static char *__progname = 0; - static char *place = EMSG; /* option letter processing */ - char *oli; /* option letter list index */ - __progname = __progname?__progname:_progname(*nargv); - - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(ostr != NULL); - - if (optreset || !*place) { /* update scanning pointer */ - optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { - place = EMSG; - return (-1); - } - if (place[1] && *++place == '-' /* found "--" */ - && place[1] == '\0') { - ++optind; - place = EMSG; - return (-1); - } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { - /* - * if the user didn't specify '-' as an option, - * assume it means -1. - */ - if (optopt == (int)'-') - return (-1); - if (!*place) - ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, - "%s: illegal option -- %c\n", __progname, optopt); - return (BADCH); - } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; - if (!*place) - ++optind; - } - else { /* need an argument */ - if (*place) /* no white space */ - optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if (*ostr == ':') - return (BADARG); - if (opterr) - (void)fprintf(stderr, - "%s: option requires an argument -- %c\n", - __progname, optopt); - return (BADCH); - } - else /* white space */ - optarg = nargv[optind]; - place = EMSG; - ++optind; - } - return (optopt); /* dump back option letter */ -} diff --git a/src/getopt_long.c b/src/getopt_long.c index 66fdf011..dbb424ae 100644 --- a/src/getopt_long.c +++ b/src/getopt_long.c @@ -1,6 +1,31 @@ +/* $OpenBSD: getopt_long.c,v 1.26 2013/06/08 22:47:56 millert Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + /* - * Copyright (c) 1987, 1993, 1994, 1996 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,227 +35,481 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -#include + #include -#include #include #include +#include #include "getopt_long.h" -extern int opterr; /* if error message should be printed */ -extern int optind; /* index into parent argv vector */ -extern int optopt; /* character checked for validity */ -extern int optreset; /* reset getopt */ -extern char *optarg; /* argument associated with option */ +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ -#define __P(x) x -#define _DIAGASSERT(x) assert(x) +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ -static char * __progname __P((char *)); -int getopt_internal __P((int, char * const *, const char *)); +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; -static char * -__progname(nargv0) - char * nargv0; +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) { - char * tmp; + int c; - _DIAGASSERT(nargv0 != NULL); + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } - tmp = strrchr(nargv0, '/'); - if (tmp) - tmp++; - else - tmp = nargv0; - return(tmp); + return (b); } -#define BADCH (int)'?' -#define BADARG (int)':' -#define EMSG "" +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} /* - * getopt -- - * Parse argc/argv argument vector. + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. */ -int -getopt_internal(nargc, nargv, ostr) - int nargc; - char * const *nargv; - const char *ostr; +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too, int flags) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match, exact_match, second_partial_match; + + current_argv = place; + match = -1; + exact_match = 0; + second_partial_match = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + exact_match = 1; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* first partial match */ + match = i; + else if ((flags & FLAG_LONGONLY) || + long_options[i].has_arg != long_options[match].has_arg || + long_options[i].flag != long_options[match].flag || + long_options[i].val != long_options[match].val) + second_partial_match = 1; + } + if (!exact_match && second_partial_match) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + fprintf(stderr, ambig, (int)current_argv_len, current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + fprintf(stderr, noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + fprintf(stderr, recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + fprintf(stderr, illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) { - static char *place = EMSG; /* option letter processing */ char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(ostr != NULL); + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1 || optreset) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; - if (optind >= nargc || *(place = nargv[optind]) != '-') { + if (optind >= nargc) { /* end of argument vector */ place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; return (-1); } - if (place[1] && *++place == '-') { /* found "--" */ - /* ++optind; */ + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too, flags); + if (optchar != -1) { place = EMSG; - return (-2); + return (optchar); } - } /* option letter okay? */ - if ((optopt = (int)*place++) == (int)':' || - !(oli = strchr(ostr, optopt))) { + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { /* - * if the user didn't specify '-' as an option, - * assume it means -1. + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). */ - if (optopt == (int)'-') + if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; - if (opterr && *ostr != ':') - (void)fprintf(stderr, - "%s: illegal option -- %c\n", __progname(nargv[0]), optopt); + if (PRINT_ERROR) + fprintf(stderr, illoptchar, optchar); + optopt = optchar; return (BADCH); } - if (*++oli != ':') { /* don't need argument */ - optarg = NULL; + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0, flags); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; - } else { /* need an argument */ + } else { /* takes (optional) argument */ + optarg = NULL; if (*place) /* no white space */ optarg = place; - else if (nargc <= ++optind) { /* no arg */ - place = EMSG; - if ((opterr) && (*ostr != ':')) - (void)fprintf(stderr, - "%s: option requires an argument -- %c\n", - __progname(nargv[0]), optopt); - return (BADARG); - } else /* white space */ - optarg = nargv[optind]; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + fprintf(stderr, recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } place = EMSG; ++optind; } - return (optopt); /* dump back option letter */ + /* dump back option letter */ + return (optchar); } -#if 0 /* * getopt -- * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] */ int -getopt2(nargc, nargv, ostr) - int nargc; - char * const *nargv; - const char *ostr; +getopt(int nargc, char * const *nargv, const char *options) { - int retval; - - if ((retval = getopt_internal(nargc, nargv, ostr)) == -2) { - retval = -1; - ++optind; - } - return(retval); + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } -#endif /* * getopt_long -- * Parse argc/argv argument vector. */ int -getopt_long(nargc, nargv, options, long_options, index) - int nargc; - char ** nargv; - char * options; - const struct option * long_options; - int * index; +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) { - int retval; - - _DIAGASSERT(nargv != NULL); - _DIAGASSERT(options != NULL); - _DIAGASSERT(long_options != NULL); - /* index may be NULL */ - - if ((retval = getopt_internal(nargc, nargv, options)) == -2) { - char *current_argv = nargv[optind++] + 2, *has_equal; - int i, current_argv_len, match = -1; + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} - if (*current_argv == '\0') { - return(-1); - } - if ((has_equal = strchr(current_argv, '=')) != NULL) { - current_argv_len = has_equal - current_argv; - has_equal++; - } else - current_argv_len = strlen(current_argv); - - for (i = 0; long_options[i].name; i++) { - if (strncmp(current_argv, long_options[i].name, current_argv_len)) - continue; - - if (strlen(long_options[i].name) == (unsigned)current_argv_len) { - match = i; - break; - } - if (match == -1) - match = i; - } - if (match != -1) { - if (long_options[match].has_arg == required_argument || - long_options[match].has_arg == optional_argument) { - if (has_equal) - optarg = has_equal; - else - optarg = nargv[optind++]; - } - if ((long_options[match].has_arg == required_argument) - && (optarg == NULL)) { - /* - * Missing argument, leading : - * indicates no error should be generated - */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, - "%s: option requires an argument -- %s\n", - __progname(nargv[0]), current_argv); - return (BADARG); - } - } else { /* No matching argument */ - if ((opterr) && (*options != ':')) - (void)fprintf(stderr, - "%s: illegal option -- %s\n", __progname(nargv[0]), current_argv); - return (BADCH); - } - if (long_options[match].flag) { - *long_options[match].flag = long_options[match].val; - retval = 0; - } else - retval = long_options[match].val; - if (index) - *index = match; - } - return(retval); +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); } diff --git a/src/gus_pat.c b/src/gus_pat.c index 03a0a6da..06b98dd1 100644 --- a/src/gus_pat.c +++ b/src/gus_pat.c @@ -77,7 +77,6 @@ static int convert_8s(uint8_t *data, struct _sample *gus_sample) { WM_ERROR_NEW("(%s:%i) ERROR: calloc failed (%s)", __FUNCTION__, __LINE__, strerror(errno)); -// WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse sample", errno); return -1; } @@ -809,7 +808,7 @@ struct _sample * load_gus_pat(const char *filename, int fix_release) { gus_sample->inc_div = ((gus_sample->freq_root * 512) / gus_sample->rate) * 2; #if 0 - // We dont use this info at this time ... kept in here for info + /* We dont use this info at this time, kept in here for info */ printf("\rTremolo Sweep: %i, Rate: %i, Depth %i\n", gus_patch[gus_ptr+49], gus_patch[gus_ptr+50], gus_patch[gus_ptr+51]); printf("\rVibrato Sweep: %i, Rate: %i, Depth %i\n", @@ -844,27 +843,26 @@ struct _sample * load_gus_pat(const char *filename, int fix_release) { uint8_t env_rate = gus_patch[gus_ptr + 37 + i]; gus_sample->env_target[i] = 16448 * gus_patch[gus_ptr + 43 + i]; GUSPAT_INT_DEBUG("Envelope Level",gus_patch[gus_ptr+43+i]); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[env_rate]); - gus_sample->env_rate[i] = (uint32_t) (4194303.0 + gus_sample->env_rate[i] = (int32_t) (4194303.0 / ((float) WM_SampleRate * env_time_table[env_rate])); - + GUSPAT_INT_DEBUG("Envelope Rate",gus_sample->env_rate[i]); GUSPAT_INT_DEBUG("GUSPAT Rate",env_rate); if (gus_sample->env_rate[i] == 0) { - fprintf(stderr, - "\rWarning: libWildMidi %s found invalid envelope(%u) rate setting in %s. Using %f instead.\n", + WM_ERROR_NEW("%s: Warning: found invalid envelope(%u) rate setting in %s. Using %f instead.", __FUNCTION__, i, filename, env_time_table[63]); - gus_sample->env_rate[i] = (uint32_t) (4194303.0 + gus_sample->env_rate[i] = (int32_t) (4194303.0 / ((float) WM_SampleRate * env_time_table[63])); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); } } else { gus_sample->env_target[i] = 4194303; - gus_sample->env_rate[i] = (uint32_t) (4194303.0 + gus_sample->env_rate[i] = (int32_t) (4194303.0 / ((float) WM_SampleRate * env_time_table[63])); GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]); } } gus_sample->env_target[6] = 0; - gus_sample->env_rate[6] = (uint32_t) (4194303.0 + gus_sample->env_rate[6] = (int32_t) (4194303.0 / ((float) WM_SampleRate * env_time_table[63])); gus_ptr += 96; diff --git a/src/internal_midi.c b/src/internal_midi.c index 6fb62baa..a5091db2 100644 --- a/src/internal_midi.c +++ b/src/internal_midi.c @@ -44,9 +44,7 @@ struct _event_list { void free_event_list(struct _event_list *event_list) { if (event_list) { - if (event_list->events) { - free(event_list->events); - } + free(event_list->events); free(event_list); } } diff --git a/src/mus.c b/src/mus.c new file mode 100644 index 00000000..e4ab1435 --- /dev/null +++ b/src/mus.c @@ -0,0 +1,408 @@ +/* + MUS2MIDI: DMX (DOOM) MUS to MIDI Library + + Copyright (C) 2014 Bret Curtis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include "mus.h" +#include "wm_error.h" + +#define TEMPO 0x001aa309 + +#define MUSEVENT_KEYOFF 0 +#define MUSEVENT_KEYON 1 +#define MUSEVENT_PITCHWHEEL 2 +#define MUSEVENT_CHANNELMODE 3 +#define MUSEVENT_CONTROLLERCHANGE 4 +#define MUSEVENT_END 6 + +#define MIDI_MAXCHANNELS 16 + +static char MUS_ID[] = { 'M', 'U', 'S', 0x1A }; + +static uint8_t midimap[] = +{/* MIDI Number Description */ + 0, /* 0 program change */ + 0, /* 1 bank selection */ + 0x01, /* 2 Modulation pot (frequency vibrato depth) */ + 0x07, /* 3 Volume: 0-silent, ~100-normal, 127-loud */ + 0x0A, /* 4 Pan (balance) pot: 0-left, 64-center (default), 127-right */ + 0x0B, /* 5 Expression pot */ + 0x5B, /* 6 Reverb depth */ + 0x5D, /* 7 Chorus depth */ + 0x40, /* 8 Sustain pedal */ + 0x43, /* 9 Soft pedal */ + 0x78, /* 10 All sounds off */ + 0x7B, /* 11 All notes off */ + 0x7E, /* 12 Mono (use numchannels + 1) */ + 0x7F, /* 13 Poly */ + 0x79, /* 14 reset all controllers */ +}; + +typedef struct MUSHeader { + char ID[4]; /* identifier: "MUS" 0x1A */ + uint16_t scoreLen; + uint16_t scoreStart; + uint16_t channels; /* count of primary channels */ + uint16_t sec_channels; /* count of secondary channels */ + uint16_t instrCnt; +} MUSHeader ; +#define MUS_HEADERSIZE 14 + +typedef struct MidiHeaderChunk { + char name[4]; + int32_t length; + int16_t format; /* make 0 */ + int16_t ntracks;/* make 1 */ + int16_t division; /* 0xe250 ?? */ +} MidiHeaderChunk; +#define MIDI_HEADERSIZE 14 + +typedef struct MidiTrackChunk { + char name[4]; + int32_t length; +} MidiTrackChunk; +#define TRK_CHUNKSIZE 8 + +struct mus_ctx { + uint8_t *src, *src_ptr; + uint32_t srcsize; + uint32_t datastart; + uint8_t *dst, *dst_ptr; + uint32_t dstsize, dstrem; +}; + +#define DST_CHUNK 8192 +static void resize_dst(struct mus_ctx *ctx) { + uint32_t pos = ctx->dst_ptr - ctx->dst; + ctx->dst = realloc(ctx->dst, ctx->dstsize + DST_CHUNK); + ctx->dstsize += DST_CHUNK; + ctx->dstrem += DST_CHUNK; + ctx->dst_ptr = ctx->dst + pos; +} + +static void write1(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 1) + resize_dst(ctx); + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem--; +} + +static void write2(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 2) + resize_dst(ctx); + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 2; +} + +static void write4(struct mus_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 4) + resize_dst(ctx); + *ctx->dst_ptr++ = (val>>24)&0xff; + *ctx->dst_ptr++ = (val>>16)&0xff; + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 4; +} + +static void seekdst(struct mus_ctx *ctx, uint32_t pos) { + ctx->dst_ptr = ctx->dst + pos; + while (ctx->dstsize < pos) + resize_dst(ctx); + ctx->dstrem = ctx->dstsize - pos; +} + +static void skipdst(struct mus_ctx *ctx, int32_t pos) { + size_t newpos; + ctx->dst_ptr += pos; + newpos = ctx->dst_ptr - ctx->dst; + while (ctx->dstsize < newpos) + resize_dst(ctx); + ctx->dstrem = ctx->dstsize - newpos; +} + +static uint32_t getdstpos(struct mus_ctx *ctx) { + return (ctx->dst_ptr - ctx->dst); +} + +/* writes a variable length integer to a buffer, and returns bytes written */ +static int32_t writevarlen(int32_t value, uint8_t *out) +{ + int32_t buffer, count = 0; + + buffer = value & 0x7f; + while ((value >>= 7) > 0) { + buffer <<= 8; + buffer += 0x80; + buffer += (value & 0x7f); + } + + while (1) { + ++count; + *out = (uint8_t)buffer; + ++out; + if (buffer & 0x80) + buffer >>= 8; + else + break; + } + return (count); +} + +#define READ_INT16(b) ((b)[0] | ((b)[1] << 8)) +#define READ_INT32(b) ((b)[0] | ((b)[1] << 8) | ((b)[2] << 16) | ((b)[3] << 24)) + +int mus2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize) { + struct mus_ctx ctx; + MUSHeader header; + uint8_t *cur, *end; + uint32_t track_size_pos, begin_track_pos, current_pos; + int32_t delta_time;/* Delta time for midi event */ + int temp, ret = -1; + int channel_volume[MIDI_MAXCHANNELS]; + int channelMap[MIDI_MAXCHANNELS], currentChannel; + + if (insize < MUS_HEADERSIZE) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return (-1); + } + + /* read the MUS header and set our location */ + memcpy(header.ID, in, 4); + header.scoreLen = READ_INT16(&in[4]); + header.scoreStart = READ_INT16(&in[6]); + header.channels = READ_INT16(&in[8]); + header.sec_channels = READ_INT16(&in[10]); + header.instrCnt = READ_INT16(&in[12]); + + if (memcmp(header.ID, MUS_ID, 4)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); + return (-1); + } + if (insize < (uint32_t)header.scoreLen + (uint32_t)header.scoreStart) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return (-1); + } + /* channel #15 should be excluded in the numchannels field: */ + if (header.channels > MIDI_MAXCHANNELS - 1) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + return (-1); + } + + memset(&ctx, 0, sizeof(struct mus_ctx)); + ctx.src = ctx.src_ptr = in; + ctx.srcsize = insize; + + ctx.dst = calloc(DST_CHUNK, sizeof(uint8_t)); + ctx.dst_ptr = ctx.dst; + ctx.dstsize = DST_CHUNK; + ctx.dstrem = DST_CHUNK; + + /* Map channel 15 to 9 (percussions) */ + for (temp = 0; temp < MIDI_MAXCHANNELS; ++temp) { + channelMap[temp] = -1; + channel_volume[temp] = 0x40; + } + channelMap[15] = 9; + + /* Header is 14 bytes long and add the rest as well */ + write1(&ctx, 'M'); + write1(&ctx, 'T'); + write1(&ctx, 'h'); + write1(&ctx, 'd'); + write4(&ctx, 6); /* length of header */ + write2(&ctx, 0); /* MIDI type (always 0) */ + write2(&ctx, 1); /* MUS files only have 1 track */ + write2(&ctx, 0x0059); /* division */ + + /* Write out track header and track length position for later */ + begin_track_pos = getdstpos(&ctx); + write1(&ctx, 'M'); + write1(&ctx, 'T'); + write1(&ctx, 'r'); + write1(&ctx, 'k'); + track_size_pos = getdstpos(&ctx); + skipdst(&ctx, 4); + + /* write tempo: microseconds per quarter note */ + write1(&ctx, 0x00); /* delta time */ + write1(&ctx, 0xff); /* sys command */ + write2(&ctx, 0x5103); /* command - set tempo */ + write1(&ctx, TEMPO & 0x000000ff); + write1(&ctx, (TEMPO & 0x0000ff00) >> 8); + write1(&ctx, (TEMPO & 0x00ff0000) >> 16); + + /* Percussions channel starts out at full volume */ + write1(&ctx, 0x00); + write1(&ctx, 0xB9); + write1(&ctx, 0x07); + write1(&ctx, 127); + + /* get current position in source, and end of position */ + cur = in + header.scoreStart; + end = cur + header.scoreLen; + + currentChannel = 0; + delta_time = 0; + + /* main loop */ + while(cur < end){ + /*printf("LOOP DEBUG: %d\r\n",iterator++);*/ + uint8_t channel; + uint8_t event; + uint8_t temp_buffer[32]; /* temp buffer for current iterator */ + uint8_t *out_local = temp_buffer; + uint8_t status, bit1, bit2, bitc = 2; + + /* read in current bit */ + event = *cur++; + channel = (event & 15); /* current channel */ + + /* write variable length delta time */ + out_local += writevarlen(delta_time, out_local); + + /* set all channels to 127 (max) volume */ + if (channelMap[channel] < 0) { + *out_local++ = 0xB0 + currentChannel; + *out_local++ = 0x07; + *out_local++ = 127; + *out_local++ = 0x00; + channelMap[channel] = currentChannel++; + if (currentChannel == 9) + ++currentChannel; + } + status = channelMap[channel]; + + /* handle events */ + switch ((event & 122) >> 4){ + case MUSEVENT_KEYOFF: + status |= 0x80; + bit1 = *cur++; + bit2 = 0x40; + break; + case MUSEVENT_KEYON: + status |= 0x90; + bit1 = *cur & 127; + if (*cur++ & 128) /* volume bit? */ + channel_volume[channelMap[channel]] = *cur++; + bit2 = channel_volume[channelMap[channel]]; + break; + case MUSEVENT_PITCHWHEEL: + status |= 0xE0; + bit1 = (*cur & 1) >> 6; + bit2 = (*cur++ >> 1) & 127; + break; + case MUSEVENT_CHANNELMODE: + status |= 0xB0; + if (*cur >= sizeof(midimap) / sizeof(midimap[0])) { + WM_ERROR_NEW("%s:%i: can't map %u to midi", + __FUNCTION__, __LINE__, *cur); + goto _end; + } + bit1 = midimap[*cur++]; + bit2 = (*cur++ == 12) ? header.channels + 1 : 0x00; + break; + case MUSEVENT_CONTROLLERCHANGE: + if (*cur == 0) { + cur++; + status |= 0xC0; + bit1 = *cur++; + bit2 = 0;/* silence bogus warnings */ + bitc = 1; + } else { + status |= 0xB0; + if (*cur >= sizeof(midimap) / sizeof(midimap[0])) { + WM_ERROR_NEW("%s:%i: can't map %u to midi", + __FUNCTION__, __LINE__, *cur); + goto _end; + } + bit1 = midimap[*cur++]; + bit2 = *cur++; + } + break; + case MUSEVENT_END: /* End */ + status = 0xff; + bit1 = 0x2f; + bit2 = 0x00; + if (cur != end) { /* should we error here or report-only? */ + WM_ERROR_NEW("%s:%i: MUS buffer off by %ld bytes", + __FUNCTION__, __LINE__, (long)(cur - end)); + } + break; + case 5:/* Unknown */ + case 7:/* Unknown */ + default:/* shouldn't happen */ + WM_ERROR_NEW("%s:%i: unrecognized event (%u)", + __FUNCTION__, __LINE__, event); + goto _end; + } + + /* write it out */ + *out_local++ = status; + *out_local++ = bit1; + if (bitc == 2) + *out_local++ = bit2; + + /* write out our temp buffer */ + if (out_local != temp_buffer) + { + if (ctx.dstrem < sizeof(temp_buffer)) + resize_dst(&ctx); + + memcpy(ctx.dst_ptr, temp_buffer, out_local - temp_buffer); + ctx.dst_ptr += out_local - temp_buffer; + ctx.dstrem -= out_local - temp_buffer; + } + + if (event & 128) { + delta_time = 0; + do { + delta_time = delta_time * 128 + (*cur & 127); + } while ((*cur++ & 128)); + } else { + delta_time = 0; + } + } + + /* write out track length */ + current_pos = getdstpos(&ctx); + seekdst(&ctx, track_size_pos); + write4(&ctx, current_pos - begin_track_pos - TRK_CHUNKSIZE); + seekdst(&ctx, current_pos); /* reseek to end position */ + + *out = ctx.dst; + *outsize = ctx.dstsize - ctx.dstrem; + ret = 0; + +_end: /* cleanup */ + if (ret < 0) { + free(ctx.dst); + *out = NULL; + *outsize = 0; + } + + return (ret); +} diff --git a/src/reverb.c b/src/reverb.c index 3f7097c8..d2107ab0 100644 --- a/src/reverb.c +++ b/src/reverb.c @@ -72,7 +72,6 @@ void reset_reverb(struct _rvb *rvb) { The combined sounds are also sent to the reflective points on the opposite side. */ - struct _rvb * init_reverb(int rate, float room_x, float room_y, float listen_x, float listen_y) { @@ -108,7 +107,7 @@ init_reverb(int rate, float room_x, float room_y, float listen_x, }; */ - //distance + /* distance */ double SPL_DST[8] = {0.0}; double SPR_DST[8] = {0.0}; double RFN_DST[8] = {0.0}; @@ -135,9 +134,9 @@ init_reverb(int rate, float room_x, float room_y, float listen_x, }; #if 0 - struct _coord SPL = {2.5, 5.0}; // Left Speaker Position - struct _coord SPR = {7.5, 5.0}; // Right Speaker Position - // position of the reflective points + struct _coord SPL = {2.5, 5.0}; /* Left Speaker Position */ + struct _coord SPR = {7.5, 5.0}; /* Right Speaker Position */ + /* position of the reflective points */ struct _coord RFN[] = { { 5.0, 0.0}, { 0.0, 6.66666}, @@ -149,9 +148,9 @@ init_reverb(int rate, float room_x, float room_y, float listen_x, { 10.0, 0.0} }; #else - struct _coord SPL; // Left Speaker Position - struct _coord SPR; // Right Speaker Position - // position of the reflective points + struct _coord SPL; /* Left Speaker Position */ + struct _coord SPR; /* Right Speaker Position */ + /* position of the reflective points */ struct _coord RFN[8]; SPL.x = room_x / 4.0; @@ -259,9 +258,8 @@ init_reverb(int rate, float room_x, float room_y, float listen_x, double sn = sin(omega); double cs = cos(omega); double alpha = sn * sinh(M_LN2 / 2 * bandwidth * omega / sn); - double A = pow(10.0, - ((dbAttn[j][i] + (dbAirAbs[i] * RFN_DST[j])) / 40.0)); - // double A = pow(10.0, ((dbAttn[i] + (dbAirAbs[i] * RFN_DST[j])) / 40.0)); + double A = pow(10.0, ((/*dbAttn[i]*/dbAttn[j][i] + + (dbAirAbs[i] * RFN_DST[j])) / 40.0) ); /* Peaking band EQ filter */ @@ -310,6 +308,7 @@ init_reverb(int rate, float room_x, float room_y, float listen_x, free_reverb - free up memory used for reverb */ void free_reverb(struct _rvb *rvb) { + if (!rvb) return; free(rvb->l_buf); free(rvb->r_buf); free(rvb); diff --git a/src/wildmidi.c b/src/wildmidi.c index 30c006d6..c17b40b8 100644 --- a/src/wildmidi.c +++ b/src/wildmidi.c @@ -26,6 +26,7 @@ #include "config.h" +#include #include #include #include @@ -33,20 +34,6 @@ #include #include #include -#include - -#if !defined(_WIN32) && !defined(__DJGPP__) -# if (defined __gnu_hurd__) -# define __USE_XOPEN 1 -# endif -#include -#include -#include -#include -#include -#include -static int msleep(unsigned long millisec); -#endif #if defined(__DJGPP__) #include "getopt_long.h" @@ -56,6 +43,9 @@ static int msleep(unsigned long millisec); #undef getopt #define msleep(s) usleep((s)*1000) #include +#ifdef AUDIODRV_DOSSB +#include "dossb.h" +#endif #endif #if (defined _WIN32) || (defined __CYGWIN__) @@ -63,38 +53,47 @@ static int msleep(unsigned long millisec); #include #include #define msleep(s) Sleep((s)) -#undef strdup -#define strdup _strdup #include +#undef close +#define close _close +#undef open +#define open _open +#undef read +#define read _read +#undef write +#define write _write +#undef lseek +#define lseek _lseek #include "getopt_long.h" -#else -# ifdef AUDIODRV_ALSA +#endif + +#if !defined(_WIN32) && !defined(__DJGPP__) /* unix build */ +static int msleep(unsigned long millisec); +#include +#include +#include +#include +#ifdef AUDIODRV_ALSA # include -# elif defined AUDIODRV_OSS +#elif defined AUDIODRV_OSS # if defined HAVE_SYS_SOUNDCARD_H # include -# elif defined HAVE_LINUX_SOUNDCARD_H -# include # elif defined HAVE_MACHINE_SOUNDCARD_H # include +# elif defined HAVE_SOUNDCARD_H +# include /* less common, but exists. */ # endif -# elif defined AUDIODRV_OPENAL +#elif defined AUDIODRV_OPENAL # include # include -# endif -#endif - -#ifndef FNONBLOCK -#define FNONBLOCK O_NONBLOCK -#endif - -#ifndef MAP_FILE -#define MAP_FILE 0 #endif +#endif /* !_WIN32, !__DJGPP__ (unix build) */ #include "wildmidi_lib.h" +#include "wm_tty.h" #include "filenames.h" + struct _midi_test { uint8_t *data; uint32_t size; @@ -208,42 +207,87 @@ static int midi_test_max = 1; */ static unsigned int rate = 32072; -static char *pcmname = NULL; static int (*send_output)(int8_t *output_data, int output_size); static void (*close_output)(void); static void (*pause_output)(void); +static void (*resume_output)(void); static int audio_fd = -1; static void pause_output_nop(void) { } +static void resume_output_nop(void) { +} + +/* + MIDI Output Functions + */ +static char midi_file[1024]; + +static int write_midi_output(void *output_data, int output_size) { + if (midi_file[0] == '\0') + return (-1); + +#if defined(_WIN32) || defined(__DJGPP__) + audio_fd = open(midi_file, (O_RDWR | O_CREAT | O_TRUNC | O_BINARY), 0664); +#else + audio_fd = open(midi_file, (O_RDWR | O_CREAT | O_TRUNC), + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)); +#endif + if (audio_fd < 0) { + fprintf(stderr, "Error: unable to open file for writing (%s)\r\n", strerror(errno)); + return (-1); + } + + if (write(audio_fd, output_data, output_size) < 0) { + fprintf(stderr, "\nERROR: failed writing midi (%s)\r\n", strerror(errno)); + close(audio_fd); + audio_fd = -1; + return (-1); + } + + close(audio_fd); + audio_fd = -1; + return (0); +} /* Wav Output Functions */ -static char wav_file[1024] = "\0"; +static char wav_file[1024]; static uint32_t wav_size; static int write_wav_output(int8_t *output_data, int output_size); static void close_wav_output(void); static int open_wav_output(void) { - - uint8_t wav_hdr[] = { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, - 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, - 0x00, 0x00, 0x00 }; + uint8_t wav_hdr[] = { + 0x52, 0x49, 0x46, 0x46, /* "RIFF" */ + 0x00, 0x00, 0x00, 0x00, /* riffsize: pcm size + 36 (filled when closing.) */ + 0x57, 0x41, 0x56, 0x45, /* "WAVE" */ + 0x66, 0x6D, 0x74, 0x20, /* "fmt " */ + 0x10, 0x00, 0x00, 0x00, /* length of this RIFF block: 16 */ + 0x01, 0x00, /* wave format == 1 (WAVE_FORMAT_PCM) */ + 0x02, 0x00, /* channels == 2 */ + 0x00, 0x00, 0x00, 0x00, /* sample rate (filled below) */ + 0x00, 0x00, 0x00, 0x00, /* bytes_per_sec: rate * channels * format bytes */ + 0x04, 0x00, /* block alignment: channels * format bytes == 4 */ + 0x10, 0x00, /* format bits == 16 */ + 0x64, 0x61, 0x74, 0x61, /* "data" */ + 0x00, 0x00, 0x00, 0x00 /* datasize: the pcm size (filled when closing.) */ + }; if (wav_file[0] == '\0') return (-1); + #if defined(_WIN32) || defined(__DJGPP__) - if ((audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC | O_BINARY), 0666)) < 0) { + audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC | O_BINARY), 0664); #else - if ((audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC), - (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))) < 0) { + audio_fd = open(wav_file, (O_RDWR | O_CREAT | O_TRUNC), + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)); #endif + if (audio_fd < 0) { fprintf(stderr, "Error: unable to open file for writing (%s)\r\n", strerror(errno)); return (-1); } else { @@ -270,6 +314,7 @@ static int open_wav_output(void) { send_output = write_wav_output; close_output = close_wav_output; pause_output = pause_output_nop; + resume_output = resume_output_nop; return (0); } @@ -347,19 +392,16 @@ typedef DWORD DWORD_PTR; #endif static void CALLBACK mmOutProc (HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { - int* freeBlockCounter = (int*)dwInstance; - HWAVEOUT tmp_hWaveOut = hWaveOut; - DWORD tmp_dwParam1 = dwParam1; - DWORD tmp_dwParam2 = dwParam2; - - tmp_hWaveOut = hWaveOut; - tmp_dwParam1 = dwParam2; - tmp_dwParam2 = dwParam1; + /* unused params */ + (void)hWaveOut; + (void)dwParam1; + (void)dwParam2; if(uMsg != WOM_DONE) return; + /* increment mm_free_blocks */ EnterCriticalSection(&waveCriticalSection); - (*freeBlockCounter)++; + (*(DWORD *)dwInstance)++; LeaveCriticalSection(&waveCriticalSection); } @@ -393,7 +435,7 @@ open_mm_output (void) { wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; - if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)mmOutProc, (DWORD_PTR)&mm_free_blocks, CALLBACK_FUNCTION ) != MMSYSERR_NOERROR) { + if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)mmOutProc, (DWORD_PTR)&mm_free_blocks, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { fprintf(stderr, "unable to open WAVE_MAPPER device\r\n"); HeapFree(GetProcessHeap(), 0, mm_blocks); hWaveOut = NULL; @@ -404,6 +446,7 @@ open_mm_output (void) { send_output = write_mm_output; close_output = close_mm_output; pause_output = pause_output_nop; + resume_output = resume_output_nop; return (0); } @@ -416,7 +459,7 @@ write_mm_output (int8_t *output_data, int output_size) { while (output_size) { if(current->dwFlags & WHDR_PREPARED) - waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); free_size = MM_BLOCK_SIZE - current->dwUser; if (free_size > output_size) free_size = output_size; @@ -448,18 +491,17 @@ write_mm_output (int8_t *output_data, int output_size) { static void close_mm_output (void) { - WAVEHDR* current; - int i, j; + int i; if (!hWaveOut) return; printf("Shutting down sound output\r\n"); - - current = &mm_blocks[mm_current_block]; - i = MM_BLOCK_SIZE - current->dwUser; - - for (j = i; i; i--) - write_mm_output (0, 0); + for (i = 0; i < MM_BLOCK_COUNT; i++) { + while (waveOutUnprepareHeader(hWaveOut, &mm_blocks[i], sizeof(WAVEHDR)) + == WAVERR_STILLPLAYING) { + Sleep(10); + } + } waveOutClose (hWaveOut); HeapFree(GetProcessHeap(), 0, mm_blocks); @@ -467,11 +509,181 @@ close_mm_output (void) { mm_blocks = NULL; } +#elif defined(__DJGPP__) && defined(AUDIODRV_DOSSB) +/* SoundBlaster/Pro/16/AWE32 driver for DOS -- adapted from + * libMikMod, written by Andrew Zabolotny , + * further fixes by O.Sezer . + * Timer callback functionality replaced by a push mechanism + * to keep the wildmidi player changes to a minimum, for now. + */ + +/* The last buffer byte filled with sound */ +static unsigned int buff_tail = 0; + +static int write_sb_output(int8_t *data, unsigned int siz) { + unsigned int dma_size, dma_pos; + unsigned int cnt; + + sb_query_dma(&dma_size, &dma_pos); + /* There isn't much sense in filling less than 256 bytes */ + dma_pos &= ~255; + + /* If nothing to mix, quit */ + if (buff_tail == dma_pos) + return 0; + + /* If DMA pointer still didn't wrapped around ... */ + if (dma_pos > buff_tail) { + if ((cnt = dma_pos - buff_tail) > siz) + cnt = siz; + memcpy(sb.dma_buff->linear + buff_tail, data, cnt); + buff_tail += cnt; + /* If we arrived right to the DMA buffer end, jump to the beginning */ + if (buff_tail >= dma_size) + buff_tail = 0; + } else { + /* If wrapped around, fill first to the end of buffer */ + if ((cnt = dma_size - buff_tail) > siz) + cnt = siz; + memcpy(sb.dma_buff->linear + buff_tail, data, cnt); + buff_tail += cnt; + siz -= cnt; + if (!siz) return cnt; + + /* Now fill from buffer beginning to current DMA pointer */ + if (dma_pos > siz) dma_pos = siz; + data += cnt; + cnt += dma_pos; + + memcpy(sb.dma_buff->linear, data, dma_pos); + buff_tail = dma_pos; + } + return cnt; +} + +static int write_sb_s16stereo(int8_t *data, int siz) { +/* libWildMidi sint16 stereo -> SB16 sint16 stereo */ + int i; + while (1) { + i = write_sb_output(data, siz); + if ((siz -= i) <= 0) return 0; + data += i; + /*usleep(100);*/ + } +} + +static int write_sb_u8stereo(int8_t *data, int siz) { +/* libWildMidi sint16 stereo -> SB uint8 stereo */ + int16_t *src = (int16_t *) data; + uint8_t *dst = (uint8_t *) data; + int i = (siz /= 2); + for (; i >= 0; --i) { + *dst++ = (*src++ >> 8) + 128; + } + while (1) { + i = write_sb_output(data, siz); + if ((siz -= i) <= 0) return 0; + data += i; + /*usleep(100);*/ + } +} + +static int write_sb_u8mono(int8_t *data, int siz) { +/* libWildMidi sint16 stereo -> SB uint8 mono */ + int16_t *src = (int16_t *) data; + uint8_t *dst = (uint8_t *) data; + int i = (siz /= 4); int val; + for (; i >= 0; --i) { + /* do a cheap (left+right)/2 */ + val = *src++; + val += *src++; + *dst++ = (val >> 9) + 128; + } + while (1) { + i = write_sb_output(data, siz); + if ((siz -= i) <= 0) return 0; + data += i; + /*usleep(100);*/ + } +} + +static void sb_silence_s16(void) { + memset(sb.dma_buff->linear, 0, sb.dma_buff->size); +} + +static void sb_silence_u8(void) { + memset(sb.dma_buff->linear, 0x80, sb.dma_buff->size); +} + +static void close_sb_output(void) +{ + sb.timer_callback = NULL; + sb_output(FALSE); + sb_stop_dma(); + sb_close(); +} + +#define open_audio_output open_sb_output +static int open_sb_output(void) +{ + if (!sb_open()) { + fprintf(stderr, "Sound Blaster initialization failed.\n"); + return -1; + } + + if (rate < 4000) rate = 4000; + if (sb.caps & SBMODE_STEREO) { + if (rate > sb.maxfreq_stereo) + rate = sb.maxfreq_stereo; + } else { + if (rate > sb.maxfreq_mono) + rate = sb.maxfreq_mono; + } + + /* Enable speaker output */ + sb_output(TRUE); + + /* Set our routine to be called during SB IRQs */ + buff_tail = 0; + sb.timer_callback = NULL;/* see above */ + + /* Start cyclic DMA transfer */ + if (!sb_start_dma(((sb.caps & SBMODE_16BITS) ? SBMODE_16BITS | SBMODE_SIGNED : 0) | + (sb.caps & SBMODE_STEREO), rate)) { + sb_output(FALSE); + sb_close(); + fprintf(stderr, "Sound Blaster: DMA start failed.\n"); + return -1; + } + + if (sb.caps & SBMODE_16BITS) { /* can do stereo, too */ + send_output = write_sb_s16stereo; + pause_output = sb_silence_s16; + resume_output = resume_output_nop; + printf("Sound Blaster 16 or compatible (16 bit, stereo, %u Hz)\n", rate); + } else if (sb.caps & SBMODE_STEREO) { + send_output = write_sb_u8stereo; + pause_output = sb_silence_u8; + resume_output = resume_output_nop; + printf("Sound Blaster Pro or compatible (8 bit, stereo, %u Hz)\n", rate); + } else { + send_output = write_sb_u8mono; + pause_output = sb_silence_u8; + resume_output = resume_output_nop; + printf("Sound Blaster %c or compatible (8 bit, mono, %u Hz)\n", + (sb.dspver < SBVER_20)? '1' : '2', rate); + } + close_output = close_sb_output; + + return 0; +} + #else #ifdef AUDIODRV_ALSA static int alsa_first_time = 1; static snd_pcm_t *pcm = NULL; +static char pcmname[64]; #define open_audio_output open_alsa_output static int write_alsa_output(int8_t *output_data, int output_size); @@ -485,8 +697,8 @@ static int open_alsa_output(void) { unsigned int alsa_period_time; unsigned int r; - if (!pcmname) { - pcmname = strdup("default"); + if (!pcmname[0]) { + strcpy(pcmname, "default"); } if ((err = snd_pcm_open(&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { @@ -557,9 +769,7 @@ static int open_alsa_output(void) { send_output = write_alsa_output; close_output = close_alsa_output; pause_output = pause_output_nop; - if (pcmname != NULL) { - free(pcmname); - } + resume_output = resume_output_nop; return (0); fail: close_alsa_output(); @@ -600,11 +810,6 @@ static void close_alsa_output(void) { } #elif defined AUDIODRV_OSS -/* - OSS Output Functions - -------------------- - uses mmap'd audio - */ #if !defined(AFMT_S16_NE) #ifdef WORDS_BIGENDIAN @@ -614,47 +819,38 @@ static void close_alsa_output(void) { #endif #endif -static int8_t *buffer = NULL; -static unsigned long int max_buffer; -static int counter; -static struct audio_buf_info info; +#define DEFAULT_FRAGSIZE 14 +#define DEFAULT_NUMFRAGS 16 + +static char pcmname[64]; #define open_audio_output open_oss_output static int write_oss_output(int8_t *output_data, int output_size); static void close_oss_output(void); static void pause_output_oss(void) { - memset(buffer, 0, max_buffer); + ioctl(audio_fd, SNDCTL_DSP_POST, 0); } static int open_oss_output(void) { - int caps, rc, tmp; - unsigned long int sz = sysconf(_SC_PAGESIZE); + int tmp; unsigned int r; - if (!pcmname) { - pcmname = strdup("/dev/dsp"); + if (!pcmname[0]) { + strcpy(pcmname, "/dev/dsp"); } - if ((audio_fd = open(pcmname, O_RDWR)) < 0) { + if ((audio_fd = open(pcmname, O_WRONLY)) < 0) { fprintf(stderr, "ERROR: Unable to open dsp (%s)\r\n", strerror(errno)); - return -1; + return (-1); } if (ioctl(audio_fd, SNDCTL_DSP_RESET, 0) < 0) { fprintf(stderr, "ERROR: Unable to reset dsp\r\n"); goto fail; } - if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) < 0) { - fprintf(stderr, "ERROR: Unable to retrieve soundcard capabilities\r\n"); - goto fail; - } - if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { - fprintf(stderr, "ERROR: Audio driver doesn't support mmap or trigger\r\n"); - goto fail; - } - rc = AFMT_S16_NE; - if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc) < 0) { + tmp = AFMT_S16_NE; + if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp) < 0) { fprintf(stderr, "ERROR: Unable to set 16bit sound format\r\n"); goto fail; } @@ -674,97 +870,50 @@ static int open_oss_output(void) { fprintf(stderr, "OSS: sample rate set to %uHz instead of %u\r\n", rate, r); } - if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { - fprintf(stderr, "ERROR: Unable to retrieve buffer status\r\n"); - goto fail; - } - - max_buffer = (info.fragstotal * info.fragsize + sz - 1) & ~(sz - 1); - buffer = (int8_t *) mmap(NULL, max_buffer, PROT_WRITE|PROT_READ, - MAP_FILE|MAP_SHARED, audio_fd, 0); - - if (buffer == MAP_FAILED) { - buffer = NULL; - fprintf(stderr, "ERROR: couldn't mmap dsp (%s)\r\n", strerror(errno)); - goto fail; - } - - tmp = 0; - if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) { - fprintf(stderr, "ERROR: Could not toggle dsp\r\n"); + tmp = (DEFAULT_NUMFRAGS<<16)|DEFAULT_FRAGSIZE; + if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp) < 0) { + fprintf(stderr, "ERROR: Unable to set fragment size\r\n"); goto fail; } - tmp = PCM_ENABLE_OUTPUT; - if (ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) { - fprintf(stderr, "ERROR: Could not toggle dsp\r\n"); - goto fail; - } send_output = write_oss_output; close_output = close_oss_output; pause_output = pause_output_oss; + resume_output = resume_output_nop; return (0); fail: close_oss_output(); - return -1; + return (-1); } static int write_oss_output(int8_t *output_data, int output_size) { - struct count_info count; - int data_read = 0; - int free_size = 0; - while (output_size != 0) { - while (1) { - if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) < 0) { - fprintf(stderr, "\nERROR: Sound dead\r\n"); - return -1; - } - if ((count.ptr < counter) || (count.ptr >= (counter + 4))) { - break; - } - msleep(5); - } - if (count.ptr < counter) { - free_size = max_buffer - counter; + int res = 0; + while (output_size > 0) { + res = write(audio_fd, output_data, output_size); + if (res > 0) { + output_size -= res; + output_data += res; } else { - free_size = count.ptr - counter; + fprintf(stderr, "\nOSS: write failure to dsp: %s.\r\n", + strerror(errno)); + return (-1); } - if (free_size > output_size) - free_size = output_size; - - memcpy(&buffer[counter], &output_data[data_read], free_size); - data_read += free_size; - counter += free_size; - if (counter >= (long)max_buffer) - counter = 0; - output_size -= free_size; } return (0); } static void close_oss_output(void) { - if (!buffer && audio_fd < 0) + if (audio_fd < 0) return; printf("Shutting down sound output\r\n"); - /* unmap before closing audio_fd */ - if (buffer != NULL) - munmap(buffer, max_buffer); - buffer = NULL; - if (audio_fd >= 0) - close(audio_fd); + ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + close(audio_fd); audio_fd = -1; } #elif defined AUDIODRV_OPENAL -#define NUM_BUFFERS 16 -#define PRIME 8 - -struct position { - ALfloat x; - ALfloat y; - ALfloat z; -}; +#define NUM_BUFFERS 8 static ALCdevice *device; static ALCcontext *context; @@ -780,66 +929,61 @@ static void pause_output_openal(void) { static int write_openal_output(int8_t *output_data, int output_size) { ALint processed, state; + ALuint bufid; - if (frames <= PRIME) { /* prime the pump */ + if (frames < NUM_BUFFERS) { /* initial state: fill the buffers */ alBufferData(buffers[frames], AL_FORMAT_STEREO16, output_data, output_size, rate); /* Now queue and start playback! */ - if (frames == PRIME) { + if (++frames == NUM_BUFFERS) { alSourceQueueBuffers(sourceId, frames, buffers); alSourcePlay(sourceId); } - frames++; return 0; } /* Get relevant source info */ alGetSourcei(sourceId, AL_SOURCE_STATE, &state); - alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &processed); - - /* Unqueue and handle each processed buffer */ - while (processed > 0) { - ALuint bufid; - - alSourceUnqueueBuffers(sourceId, 1, &bufid); - processed--; - - /* Read the next chunk of data, refill the buffer, and queue it - * back on the source */ - if (output_data != NULL) { - alBufferData(bufid, AL_FORMAT_STEREO16, output_data, output_size, rate); - alSourceQueueBuffers(sourceId, 1, &bufid); - } + if (state == AL_PAUSED) { /* resume it, then.. */ + alSourcePlay(sourceId); if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "\nError buffering data\r\n"); + fprintf(stderr, "\nError restarting playback\r\n"); return (-1); } } + processed = 0; + while (processed == 0) { /* Wait until we have a processed buffer */ + alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &processed); + } + + /* Unqueue and handle each processed buffer */ + alSourceUnqueueBuffers(sourceId, 1, &bufid); + + /* Read the next chunk of data, refill the buffer, and queue it + * back on the source */ + alBufferData(bufid, AL_FORMAT_STEREO16, output_data, output_size, rate); + alSourceQueueBuffers(sourceId, 1, &bufid); + if (alGetError() != AL_NO_ERROR) { + fprintf(stderr, "\nError buffering data\r\n"); + return (-1); + } + /* Make sure the source hasn't underrun */ + alGetSourcei(sourceId, AL_SOURCE_STATE, &state); + /*printf("STATE: %#08x - %d\n", state, queued);*/ if (state != AL_PLAYING) { ALint queued; /* If no buffers are queued, playback is finished */ alGetSourcei(sourceId, AL_BUFFERS_QUEUED, &queued); - if(queued == 0) - return (-1); - - /*printf("STATE: %#08x - %d\n", state, queued);*/ - - alSourcePlay(sourceId); - if (alGetError() != AL_NO_ERROR) { - fprintf(stderr, "\nError restarting playback\r\n"); + if (queued == 0) { + fprintf(stderr, "\nNo buffers queued for playback\r\n"); return (-1); } - } - /* block while playing back samples */ - while (state == AL_PLAYING && processed == 0) { - msleep(1); - alGetSourcei(sourceId, AL_SOURCE_STATE, &state); - alGetSourcei(sourceId, AL_BUFFERS_PROCESSED, &processed); + alSourcePlay(sourceId); } return (0); @@ -856,6 +1000,7 @@ static void close_openal_output(void) { alcCloseDevice(device); context = NULL; device = NULL; + frames = 0; } static int open_openal_output(void) { @@ -884,6 +1029,7 @@ static int open_openal_output(void) { send_output = write_openal_output; close_output = close_openal_output; pause_output = pause_output_openal; + resume_output = resume_output_nop; return (0); } @@ -901,72 +1047,76 @@ static struct option const long_options[] = { { "version", 0, 0, 'v' }, { "help", 0, 0, 'h' }, { "rate", 1, 0, 'r' }, - { "master_volume", 1, 0, 'm' }, - { "config_file", 1, 0, 'c' }, + { "mastervol", 1, 0, 'm' }, + { "config", 1, 0, 'c' }, { "wavout", 1, 0, 'o' }, + { "convert", 1, 0, 'x' }, { "log_vol", 0, 0, 'l' }, { "reverb", 0, 0, 'b' }, { "test_midi", 0, 0, 't' }, { "test_bank", 1, 0, 'k' }, { "test_patch", 1, 0, 'p' }, - { "enhanced_resample", 0, 0, 'e' }, - { "auddev", 1, 0, 'd' }, + { "enhanced", 0, 0, 'e' }, +#if defined(AUDIODRV_OSS) || defined(AUDIODRV_ALSA) + { "device", 1, 0, 'd' }, +#endif { "wholetempo", 0, 0, 'w' }, { "roundtempo", 0, 0, 'n' }, { NULL, 0, NULL, 0 } }; static void do_help(void) { - printf(" -v --version Display version\n"); - printf(" -h --help This help.\n"); -#ifndef _WIN32 - printf(" -d D --device=D Use device D for audio output instead\n"); - printf(" of the default\n"); + printf(" -v --version Display version info and exit\n"); + printf(" -h --help Display this help and exit\n"); +#if defined(AUDIODRV_OSS) || defined(AUDIODRV_ALSA) + printf(" -d D --device=D Use device D for audio output instead of default\n"); #endif - printf("MIDI Options\n"); - printf(" -w --wholetempo round down tempo to whole number\n"); - printf(" -n --roundtempo round tempo to nearest whole number\n"); - printf("Software Wavetable Options\n"); - printf(" -o W --wavout=W Saves the output to W in wav format\n"); - printf(" at 44100Hz 16 bit stereo\n"); - printf(" -l --log_vol Use log volume adjustments\n"); - printf(" -r N --rate=N output at N samples per second\n"); - printf(" -c P --config_file=P P is the path and filename to your wildmidi.cfg\n"); - printf(" Defaults to %s\n", WILDMIDI_CFG); - printf(" -m V --master_volume=V Sets the master volumes, default is 100\n"); - printf(" range is 0-127 with 127 being the loudest\n"); - printf(" -b --reverb Enable final output reverb engine\n"); + printf("MIDI Options:\n"); + printf(" -w --wholetempo Round down tempo to whole number\n"); + printf(" -n --roundtempo Round tempo to nearest whole number\n"); + printf(" -t --test_midi Listen to test MIDI\n"); + printf("Non-MIDI Options:\n"); + printf(" -x --convert Convert file to midi and save to file\n"); + printf("Software Wavetable Options:\n"); + printf(" -o W --wavout=W Save output to W in 16bit stereo format wav file\n"); + printf(" -l --log_vol Use log volume adjustments\n"); + printf(" -r N --rate=N Set sample rate to N samples per second (Hz)\n"); + printf(" -c P --config=P Point to your wildmidi.cfg config file name/path\n"); + printf(" defaults to: %s\n", WILDMIDI_CFG); + printf(" -m V --mastervol=V Set the master volume (0..127), default is 100\n"); + printf(" -b --reverb Enable final output reverb engine\n"); } static void do_version(void) { printf("\nWildMidi %s Open Source Midi Sequencer\n", PACKAGE_VERSION); printf("Copyright (C) WildMIDI Developers 2001-2014\n\n"); printf("WildMidi comes with ABSOLUTELY NO WARRANTY\n"); - printf("This is free software, and you are welcome to redistribute it\n"); - printf("under the terms and conditions of the GNU General Public License version 3.\n"); + printf("This is free software, and you are welcome to redistribute it under\n"); + printf("the terms and conditions of the GNU General Public License version 3.\n"); printf("For more information see COPYING\n\n"); printf("Report bugs to %s\n", PACKAGE_BUGREPORT); - printf("WildMIDI homepage at %s\n\n", PACKAGE_URL); + printf("WildMIDI homepage is at %s\n\n", PACKAGE_URL); } static void do_syntax(void) { - printf("wildmidi [options] filename.mid\n\n"); + printf("Usage: wildmidi [options] filename.mid\n\n"); } +static char config_file[1024]; + int main(int argc, char **argv) { struct _WM_Info *wm_info; int i, res; int option_index = 0; - uint32_t mixer_options = 0; - char *config_file = NULL; + uint16_t mixer_options = 0; void *midi_ptr; uint8_t master_volume = 100; int8_t *output_buffer; - uint32_t perc_play = 0; - uint32_t pro_mins = 0; - uint32_t pro_secs = 0; - uint32_t apr_mins = 0; - uint32_t apr_secs = 0; + uint32_t perc_play; + uint32_t pro_mins; + uint32_t pro_secs; + uint32_t apr_mins; + uint32_t apr_secs; char modes[4]; uint32_t count_diff; uint8_t ch; @@ -977,27 +1127,20 @@ int main(int argc, char **argv) { uint8_t test_patch = 0; static char spinner[] = "|/-\\"; static int spinpoint = 0; - unsigned long int seek_to_sample = 0; + unsigned long int seek_to_sample; int inpause = 0; long libraryver; -#if !defined(_WIN32) && !defined(__DJGPP__) - int my_tty; - struct termios _tty; - tcflag_t _res_oflg = 0; - tcflag_t _res_lflg = 0; - -#define raw() (_tty.c_lflag &= ~(ICANON | ICRNL | ISIG), \ - _tty.c_oflag &= ~ONLCR, tcsetattr(my_tty, TCSANOW, &_tty)) -#define savetty() ((void) tcgetattr(my_tty, &_tty), \ - _res_oflg = _tty.c_oflag, _res_lflg = _tty.c_lflag) -#define resetty() (_tty.c_oflag = _res_oflg, _tty.c_lflag = _res_lflg,\ - (void) tcsetattr(my_tty, TCSADRAIN, &_tty)) -#endif /* !_WIN32, !__DJGPP__ */ +#if defined(AUDIODRV_OSS) || defined(AUDIODRV_ALSA) + pcmname[0] = 0; +#endif + config_file[0] = 0; + wav_file[0] = 0; + midi_file[0] = 0; do_version(); while (1) { - i = getopt_long(argc, argv, "vho:lr:c:m:btk:p:ed:wn", long_options, + i = getopt_long(argc, argv, "vho:tx:lr:c:m:btk:p:ed:wn", long_options, &option_index); if (i == -1) break; @@ -1012,7 +1155,7 @@ int main(int argc, char **argv) { res = atoi(optarg); if (res < 0 || res > 65535) { fprintf(stderr, "Error: bad rate %i.\n", res); - return (0); + return (1); } rate = res; break; @@ -1025,20 +1168,37 @@ int main(int argc, char **argv) { case 'o': /* Wav Output */ if (!*optarg) { fprintf(stderr, "Error: empty wavfile name.\n"); - return (0); + return (1); + } + strncpy(wav_file, optarg, sizeof(wav_file)); + wav_file[sizeof(wav_file) - 1] = 0; + break; + case 'x': /* MIDI Output */ + if (!*optarg) { + fprintf(stderr, "Error: empty midi name.\n"); + return (1); } - strcpy(wav_file, optarg); + strncpy(midi_file, optarg, sizeof(midi_file)); + midi_file[sizeof(midi_file) - 1] = 0; break; case 'c': /* Config File */ - config_file = strdup(optarg); + if (!*optarg) { + fprintf(stderr, "Error: empty config name.\n"); + return (1); + } + strncpy(config_file, optarg, sizeof(config_file)); + config_file[sizeof(config_file) - 1] = 0; break; +#if defined(AUDIODRV_OSS) || defined(AUDIODRV_ALSA) case 'd': /* Output device */ if (!*optarg) { fprintf(stderr, "Error: empty device name.\n"); - return (0); + return (1); } - pcmname = strdup(optarg); + strncpy(pcmname, optarg, sizeof(pcmname)); + pcmname[sizeof(pcmname) - 1] = 0; break; +#endif case 'e': /* Enhanced Resampling */ mixer_options |= WM_MO_ENHANCED_RESAMPLING; break; @@ -1061,270 +1221,283 @@ int main(int argc, char **argv) { mixer_options |= WM_MO_ROUNDTEMPO; break; default: - fprintf(stderr, "Error: Unknown option -%o\n", i); - return (0); + do_syntax(); + return (1); } } - if (!config_file) { - config_file = strdup(WILDMIDI_CFG); + if (optind >= argc && !test_midi) { + fprintf(stderr, "ERROR: No midi file given\r\n"); + do_syntax(); + return (1); } - if (optind < argc || test_midi) { - printf("Initializing Sound System\n"); - if (wav_file[0] != '\0') { - if (open_wav_output() == -1) { - return (0); - } - } else { - if (open_audio_output() == -1) { - return (0); - } + if (test_midi) { + if (midi_file[0] != '\0') { + fprintf(stderr, "--test_midi and --convert cannot be used together.\n"); + return (1); } + } - libraryver = WildMidi_GetVersion(); - printf("Initializing libWildMidi %ld.%ld.%ld\n\n", - (libraryver>>16) & 255, - (libraryver>> 8) & 255, - (libraryver ) & 255); - if (WildMidi_Init(config_file, rate, mixer_options) == -1) { - return (0); - } + /* check if we only need to convert file MIDI */ + if (midi_file[0] != '\0') { + const char *real_file = FIND_LAST_DIRSEP(argv[optind]); + uint32_t size; + uint8_t *data; - printf(" + Volume up e Better resampling n Next Midi\n"); - printf(" - Volume down l Log volume q Quit\n"); - printf(" , 1sec Seek Back r Reverb . 1sec Seek Forward\n"); - printf(" p Pause On/Off\n\n"); + if (!real_file) real_file = argv[optind]; + else real_file++; - output_buffer = malloc(16384); - if (output_buffer == NULL) { - fprintf(stderr, "Not enough memory, exiting\n"); - WildMidi_Shutdown(); - return (0); + printf("Converting %s\r\n", real_file); + data = (uint8_t*) WildMidi_ConvertToMidi(argv[optind], &size); + if (!data) { + fprintf(stderr, "Conversion failed.\r\n"); + return (1); } -#if !defined(_WIN32) && !defined(__DJGPP__) - my_tty = fileno(stdin); - if (isatty(my_tty)) { - savetty(); - raw(); - fcntl(0, F_SETFL, FNONBLOCK); + printf("Writing %s: %u bytes.\r\n", midi_file, size); + write_midi_output(data, size); + free(data); + return (0); + } + + if (!config_file[0]) { + strncpy(config_file, WILDMIDI_CFG, sizeof(config_file)); + config_file[sizeof(config_file) - 1] = 0; + } + + printf("Initializing Sound System\n"); + if (wav_file[0] != '\0') { + if (open_wav_output() == -1) { + return (1); } -#endif + } else { + if (open_audio_output() == -1) { + return (1); + } + } - WildMidi_MasterVolume(master_volume); + libraryver = WildMidi_GetVersion(); + printf("Initializing libWildMidi %ld.%ld.%ld\n\n", + (libraryver>>16) & 255, + (libraryver>> 8) & 255, + (libraryver ) & 255); + if (WildMidi_Init(config_file, rate, mixer_options) == -1) { + return (1); + } - while ((optind < argc) || (test_midi)) { - if (!test_midi) { - const char *real_file = FIND_LAST_DIRSEP(argv[optind]); + printf(" + Volume up e Better resampling n Next Midi\n"); + printf(" - Volume down l Log volume q Quit\n"); + printf(" , 1sec Seek Back r Reverb . 1sec Seek Forward\n"); + printf(" p Pause On/Off\n\n"); - printf("Playing "); - if (real_file != NULL) { - printf("%s \r\n", (real_file + 1)); - } else { - printf("%s \r\n", argv[optind]); - } + output_buffer = malloc(16384); + if (output_buffer == NULL) { + fprintf(stderr, "Not enough memory, exiting\n"); + WildMidi_Shutdown(); + return (1); + } - midi_ptr = WildMidi_Open(argv[optind]); - if (midi_ptr == NULL) { - printf("\r"); - optind++; - continue; - } - wm_info = WildMidi_GetInfo(midi_ptr); + wm_inittty(); - optind++; - } else { - if (test_count == midi_test_max) { - break; - } - test_data = malloc(midi_test[test_count].size); - memcpy(test_data, midi_test[test_count].data, - midi_test[test_count].size); - test_data[25] = test_bank; - test_data[28] = test_patch; - midi_ptr = WildMidi_OpenBuffer(test_data, 633); - wm_info = WildMidi_GetInfo(midi_ptr); - test_count++; - printf("Playing test midi no. %i\r\n", test_count); - } + WildMidi_MasterVolume(master_volume); - apr_mins = wm_info->approx_total_samples / (rate * 60); - apr_secs = (wm_info->approx_total_samples % (rate * 60)) / rate; + while (optind < argc || test_midi) { + if (!test_midi) { + const char *real_file = FIND_LAST_DIRSEP(argv[optind]); + if (!real_file) real_file = argv[optind]; + else real_file++; + printf("Playing %s \r\n", real_file); + + midi_ptr = WildMidi_Open(argv[optind]); + optind++; if (midi_ptr == NULL) { - fprintf(stderr, "Skipping %s\r\n", argv[optind]); - optind++; + fprintf(stderr, "\rSkipping %s\r\n", real_file); continue; } - mixer_options = wm_info->mixer_options; - fprintf(stderr, "\r"); + } else { + if (test_count == midi_test_max) { + break; + } + test_data = malloc(midi_test[test_count].size); + memcpy(test_data, midi_test[test_count].data, + midi_test[test_count].size); + test_data[25] = test_bank; + test_data[28] = test_patch; + midi_ptr = WildMidi_OpenBuffer(test_data, 633); + test_count++; + if (midi_ptr == NULL) { + fprintf(stderr, "\rFailed loading test midi no. %i\r\n", test_count); + continue; + } + printf("Playing test midi no. %i\r\n", test_count); + } - while (1) { - count_diff = wm_info->approx_total_samples + wm_info = WildMidi_GetInfo(midi_ptr); + apr_mins = wm_info->approx_total_samples / (rate * 60); + apr_secs = (wm_info->approx_total_samples % (rate * 60)) / rate; + mixer_options = wm_info->mixer_options; + modes[0] = (mixer_options & WM_MO_LOG_VOLUME)? 'l' : ' '; + modes[1] = (mixer_options & WM_MO_REVERB)? 'r' : ' '; + modes[2] = (mixer_options & WM_MO_ENHANCED_RESAMPLING)? 'e' : ' '; + modes[3] = '\0'; + fprintf(stderr, "\r"); + + while (1) { + count_diff = wm_info->approx_total_samples - wm_info->current_sample; - if (count_diff == 0) - break; + if (count_diff == 0) + break; - ch = 0; + ch = 0; #ifdef _WIN32 - if (_kbhit()) { - ch = _getch(); - putch(ch); - } + if (_kbhit()) { + ch = _getch(); + _putch(ch); + } #elif defined(__DJGPP__) - if (kbhit()) { - ch = getch(); - putch(ch); - } + if (kbhit()) { + ch = getch(); + putch(ch); + } #else - if (read(my_tty, &ch, 1) != 1) - ch = 0; + if (read(STDIN_FILENO, &ch, 1) != 1) + ch = 0; #endif - if (ch) { - switch (ch) { - case 'l': - WildMidi_SetOption(midi_ptr, WM_MO_LOG_VOLUME, - ((mixer_options & WM_MO_LOG_VOLUME) - ^ WM_MO_LOG_VOLUME)); - mixer_options ^= WM_MO_LOG_VOLUME; - break; - case 'r': - WildMidi_SetOption(midi_ptr, WM_MO_REVERB, - ((mixer_options & WM_MO_REVERB) ^ WM_MO_REVERB)); - mixer_options ^= WM_MO_REVERB; - break; - case 'e': - WildMidi_SetOption(midi_ptr, WM_MO_ENHANCED_RESAMPLING, - ((mixer_options & WM_MO_ENHANCED_RESAMPLING) - ^ WM_MO_ENHANCED_RESAMPLING)); - mixer_options ^= WM_MO_ENHANCED_RESAMPLING; - break; - case 'n': - goto NEXTMIDI; - case 'p': - if (inpause) { - inpause = 0; - fprintf(stderr, " \r"); - } else { - inpause = 1; - fprintf(stderr, "Paused \r"); - continue; - } - break; - case 'q': - printf("\r\n"); - goto end1; - case '-': - if (master_volume > 0) { - master_volume--; - WildMidi_MasterVolume(master_volume); - } - break; - case '+': - if (master_volume < 127) { - master_volume++; - WildMidi_MasterVolume(master_volume); - } - break; - case ',': /* fast seek backwards */ - if (wm_info->current_sample < rate) { - seek_to_sample = 0; - } else { - seek_to_sample = wm_info->current_sample - rate; - } - WildMidi_FastSeek(midi_ptr, &seek_to_sample); - break; - case '.': /* fast seek forwards */ - if ((wm_info->approx_total_samples - - wm_info->current_sample) < rate) { - seek_to_sample = wm_info->approx_total_samples; - } else { - seek_to_sample = wm_info->current_sample + rate; - } - WildMidi_FastSeek(midi_ptr, &seek_to_sample); - break; - default: - break; - } - } - - if (inpause) { - wm_info = WildMidi_GetInfo(midi_ptr); - perc_play = (wm_info->current_sample * 100) - / wm_info->approx_total_samples; - pro_mins = wm_info->current_sample / (rate * 60); - pro_secs = (wm_info->current_sample % (rate * 60)) / rate; + if (ch) { + switch (ch) { + case 'l': + WildMidi_SetOption(midi_ptr, WM_MO_LOG_VOLUME, + ((mixer_options & WM_MO_LOG_VOLUME) + ^ WM_MO_LOG_VOLUME)); + mixer_options ^= WM_MO_LOG_VOLUME; modes[0] = (mixer_options & WM_MO_LOG_VOLUME)? 'l' : ' '; + break; + case 'r': + WildMidi_SetOption(midi_ptr, WM_MO_REVERB, + ((mixer_options & WM_MO_REVERB) ^ WM_MO_REVERB)); + mixer_options ^= WM_MO_REVERB; modes[1] = (mixer_options & WM_MO_REVERB)? 'r' : ' '; + break; + case 'e': + WildMidi_SetOption(midi_ptr, WM_MO_ENHANCED_RESAMPLING, + ((mixer_options & WM_MO_ENHANCED_RESAMPLING) + ^ WM_MO_ENHANCED_RESAMPLING)); + mixer_options ^= WM_MO_ENHANCED_RESAMPLING; modes[2] = (mixer_options & WM_MO_ENHANCED_RESAMPLING)? 'e' : ' '; - modes[3] = '\0'; - fprintf(stderr, - " [Approx %2um %2us Total] [%s] [%3i] [%2um %2us Processed] [%2u%%] 0 \r", - apr_mins, apr_secs, modes, master_volume, pro_mins, - pro_secs, perc_play); - - msleep(5); - pause_output(); - continue; - } - - res = WildMidi_GetOutput(midi_ptr, output_buffer, - (count_diff >= 4096)? 16384 : (count_diff * 4)); - if (res <= 0) break; + case 'n': + goto NEXTMIDI; + case 'p': + if (inpause) { + inpause = 0; + fprintf(stderr, " \r"); + resume_output(); + } else { + inpause = 1; + fprintf(stderr, "Paused \r"); + pause_output(); + continue; + } + break; + case 'q': + printf("\r\n"); + if (inpause) goto end2; + goto end1; + case '-': + if (master_volume > 0) { + master_volume--; + WildMidi_MasterVolume(master_volume); + } + break; + case '+': + if (master_volume < 127) { + master_volume++; + WildMidi_MasterVolume(master_volume); + } + break; + case ',': /* fast seek backwards */ + if (wm_info->current_sample < rate) { + seek_to_sample = 0; + } else { + seek_to_sample = wm_info->current_sample - rate; + } + WildMidi_FastSeek(midi_ptr, &seek_to_sample); + break; + case '.': /* fast seek forwards */ + if ((wm_info->approx_total_samples + - wm_info->current_sample) < rate) { + seek_to_sample = wm_info->approx_total_samples; + } else { + seek_to_sample = wm_info->current_sample + rate; + } + WildMidi_FastSeek(midi_ptr, &seek_to_sample); + break; + default: + break; + } + } + if (inpause) { wm_info = WildMidi_GetInfo(midi_ptr); perc_play = (wm_info->current_sample * 100) / wm_info->approx_total_samples; pro_mins = wm_info->current_sample / (rate * 60); pro_secs = (wm_info->current_sample % (rate * 60)) / rate; - modes[0] = (mixer_options & WM_MO_LOG_VOLUME)? 'l' : ' '; - modes[1] = (mixer_options & WM_MO_REVERB)? 'r' : ' '; - modes[2] = (mixer_options & WM_MO_ENHANCED_RESAMPLING)? 'e' : ' '; - modes[3] = '\0'; fprintf(stderr, - " [Approx %2um %2us Total] [%s] [%3i] [%2um %2us Processed] [%2u%%] %c \r", - apr_mins, apr_secs, modes, master_volume, pro_mins, - pro_secs, perc_play, spinner[spinpoint++ % 4]); - - if (send_output(output_buffer, res) < 0) { - /* driver prints an error message already. */ - printf("\r"); - goto end2; - } + " [Approx %2um %2us Total] [%s] [%3i] [%2um %2us Processed] [%2u%%] 0 \r", + apr_mins, apr_secs, modes, master_volume, pro_mins, + pro_secs, perc_play); + + msleep(5); + continue; } - NEXTMIDI: fprintf(stderr, "\r\n"); - if (WildMidi_Close(midi_ptr) == -1) { - fprintf(stderr, "OOPS: failed closing midi handle!\r\n"); + + res = WildMidi_GetOutput(midi_ptr, output_buffer, + (count_diff >= 4096)? 16384 : (count_diff * 4)); + if (res <= 0) + break; + + wm_info = WildMidi_GetInfo(midi_ptr); + perc_play = (wm_info->current_sample * 100) + / wm_info->approx_total_samples; + pro_mins = wm_info->current_sample / (rate * 60); + pro_secs = (wm_info->current_sample % (rate * 60)) / rate; + fprintf(stderr, + " [Approx %2um %2us Total] [%s] [%3i] [%2um %2us Processed] [%2u%%] %c \r", + apr_mins, apr_secs, modes, master_volume, pro_mins, + pro_secs, perc_play, spinner[spinpoint++ % 4]); + + if (send_output(output_buffer, res) < 0) { + /* driver prints an error message already. */ + printf("\r"); + goto end2; } - memset(output_buffer, 0, 16384); - send_output(output_buffer, 16384); } -end1: memset(output_buffer, 0, 16384); + NEXTMIDI: fprintf(stderr, "\r\n"); + if (WildMidi_Close(midi_ptr) == -1) { + fprintf(stderr, "OOPS: failed closing midi handle!\r\n"); + } + memset(output_buffer, 0, 16384); send_output(output_buffer, 16384); - msleep(5); -end2: close_output(); - free(output_buffer); - free(config_file); - if (WildMidi_Shutdown() == -1) - fprintf(stderr, "OOPS: failure shutting down libWildMidi\r\n"); -#if !defined(_WIN32) && !defined(__DJGPP__) - if (isatty(my_tty)) - resetty(); -#endif - } else { - fprintf(stderr, "ERROR: No midi file given\r\n"); - free(config_file); - do_syntax(); - return (0); } +end1: memset(output_buffer, 0, 16384); + send_output(output_buffer, 16384); + msleep(5); +end2: close_output(); + free(output_buffer); + if (WildMidi_Shutdown() == -1) + fprintf(stderr, "OOPS: failure shutting down libWildMidi\r\n"); + wm_resetty(); printf("\r\n"); return (0); } +/* helper / replacement functions: */ + #if !defined(_WIN32) && !defined(__DJGPP__) static int msleep(unsigned long milisec) { struct timespec req = { 0, 0 }; diff --git a/src/wildmidi_lib.c b/src/wildmidi_lib.c index 86245890..d2fcc8e0 100644 --- a/src/wildmidi_lib.c +++ b/src/wildmidi_lib.c @@ -29,7 +29,6 @@ #define UNUSED(x) (void)(x) #include -#include #include #include #include @@ -52,8 +51,6 @@ #define strcasecmp _stricmp #undef strncasecmp #define strncasecmp _tcsnicmp -#undef strdup -#define strdup _strdup #endif #include "wm_error.h" @@ -64,6 +61,8 @@ #include "common.h" #include "wildmidi_lib.h" #include "filenames.h" +#include "xmidi.h" +#include "mus.h" /* * ========================= @@ -81,17 +80,17 @@ uint16_t WM_SampleRate; static struct _patch *patch[128]; -static float reverb_room_width = 16.875; -static float reverb_room_length = 22.5; +static float reverb_room_width = 16.875f; +static float reverb_room_length = 22.5f; -static float reverb_listen_posx = 8.4375; -static float reverb_listen_posy = 16.875; +static float reverb_listen_posx = 8.4375f; +static float reverb_listen_posy = 16.875f; static int fix_release = 0; static int auto_amp = 0; static int auto_amp_with_amp = 0; -static int patch_lock = 0; +static int patch_lock; struct _channel { uint8_t bank; @@ -123,7 +122,7 @@ struct _note { uint32_t sample_inc; int32_t env_inc; uint8_t env; - uint32_t env_level; + int32_t env_level; uint8_t modes; uint8_t hold; uint8_t active; @@ -191,7 +190,7 @@ static double newt_coeffs[58][58]; /* for start/end of samples */ #define MAX_GAUSS_ORDER 34 /* 34 is as high as we can go before errors crop up */ static double *gauss_table = NULL; /* *gauss_table[1<filename) { - if (patch[i]->first_sample) { - while (patch[i]->first_sample) { - tmp_sample = patch[i]->first_sample->next; - if (patch[i]->first_sample->data) - free(patch[i]->first_sample->data); - free(patch[i]->first_sample); - patch[i]->first_sample = tmp_sample; - } - } - free(patch[i]->filename); - } - tmp_patch = patch[i]->next; - free(patch[i]); - patch[i] = tmp_patch; + while (patch[i]) { + while (patch[i]->first_sample) { + tmp_sample = patch[i]->first_sample->next; + free(patch[i]->first_sample->data); + free(patch[i]->first_sample); + patch[i]->first_sample = tmp_sample; } + free(patch[i]->filename); + tmp_patch = patch[i]->next; + free(patch[i]); + patch[i] = tmp_patch; } } WM_Unlock(&patch_lock); } +/* wm_strdup -- adds extra space for appending up to 4 chars */ +static char *wm_strdup (const char *str) { + size_t l = strlen(str) + 5; + char *d = (char *) malloc(l * sizeof(char)); + if (d) { + strcpy(d, str); + return (d); + } + return (NULL); +} + +static inline int wm_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + #define TOKEN_CNT_INC 8 static char** WM_LC_Tokenize_Line(char *line_data) { int line_length = strlen(line_data); @@ -574,8 +583,7 @@ static char** WM_LC_Tokenize_Line(char *line_data) { char **token_data = NULL; int token_count = 0; - if (line_length == 0) - return NULL; + if (line_length == 0) return (NULL); do { /* ignore everything after # */ @@ -598,7 +606,7 @@ static char** WM_LC_Tokenize_Line(char *line_data) { token_data = realloc(token_data, token_data_length * sizeof(char *)); if (token_data == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM,"to parse config", errno); - return NULL; + return (NULL); } } @@ -618,7 +626,7 @@ static char** WM_LC_Tokenize_Line(char *line_data) { token_data[token_count] = NULL; } - return token_data; + return (token_data); } static int WM_LoadConfig(const char *config_file) { @@ -629,15 +637,14 @@ static int WM_LoadConfig(const char *config_file) { uint32_t config_ptr = 0; uint32_t line_start_ptr = 0; uint16_t patchid = 0; - char *new_config = NULL; struct _patch * tmp_patch; char **line_tokens = NULL; int token_count = 0; - if ((config_buffer = (char *) WM_BufferFile(config_file, &config_size)) - == NULL) { + config_buffer = (char *) WM_BufferFile(config_file, &config_size); + if (!config_buffer) { WM_FreePatches(); - return -1; + return (-1); } dir_end = FIND_LAST_DIRSEP(config_file); @@ -649,27 +656,40 @@ static int WM_LoadConfig(const char *config_file) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); free(config_buffer); - return -1; + return (-1); } strncpy(config_dir, config_file, (dir_end - config_file + 1)); config_dir[dir_end - config_file + 1] = '\0'; } + config_ptr = 0; line_start_ptr = 0; - while (config_ptr < config_size) { - if (config_buffer[config_ptr] == '\r') { - config_buffer[config_ptr] = ' '; - } else if (config_buffer[config_ptr] == '\n') { + + /* handle files without a newline at the end: this relies on + * WM_BufferFile() allocating the buffer with one extra byte */ + config_buffer[config_size] = '\n'; + + while (config_ptr <= config_size) { + if (config_buffer[config_ptr] == '\r' || + config_buffer[config_ptr] == '\n') + { config_buffer[config_ptr] = '\0'; if (config_ptr != line_start_ptr) { - if ((line_tokens = WM_LC_Tokenize_Line( - &config_buffer[line_start_ptr]))) { + line_tokens = WM_LC_Tokenize_Line(&config_buffer[line_start_ptr]); + if (line_tokens) { if (strcasecmp(line_tokens[0], "dir") == 0) { - if (config_dir) { - free(config_dir); - } - if (!line_tokens[1] || !(config_dir = strdup(line_tokens[1]))) { + free(config_dir); + if (!line_tokens[1]) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in dir line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return (-1); + } else if (!(config_dir = wm_strdup(line_tokens[1]))) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, @@ -677,19 +697,24 @@ static int WM_LoadConfig(const char *config_file) { WM_FreePatches(); free(line_tokens); free(config_buffer); - return -1; + return (-1); } if (!IS_DIR_SEPARATOR(config_dir[strlen(config_dir) - 1])) { - config_dir = realloc(config_dir, - (strlen(config_dir) + 2)); config_dir[strlen(config_dir) + 1] = '\0'; config_dir[strlen(config_dir)] = DIR_SEPARATOR_CHAR; } } else if (strcasecmp(line_tokens[0], "source") == 0) { - if (line_tokens[1] && - !IS_ABSOLUTE_PATH(line_tokens[1]) && - config_dir) { - + char *new_config = NULL; + if (!line_tokens[1]) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in source line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(line_tokens); + free(config_buffer); + return (-1); + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { new_config = malloc( strlen(config_dir) + strlen(line_tokens[1]) + 1); @@ -702,13 +727,12 @@ static int WM_LoadConfig(const char *config_file) { free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } strcpy(new_config, config_dir); - strcpy(&new_config[strlen(config_dir)], - line_tokens[1]); + strcpy(&new_config[strlen(config_dir)], line_tokens[1]); } else { - if (!line_tokens[1] || !(new_config = malloc(strlen(line_tokens[1]) + 1))) { + if (!(new_config = wm_strdup(line_tokens[1]))) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config", errno); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, @@ -716,119 +740,109 @@ static int WM_LoadConfig(const char *config_file) { WM_FreePatches(); free(line_tokens); free(config_buffer); - return -1; + return (-1); } - strcpy(new_config, line_tokens[1]); } if (WM_LoadConfig(new_config) == -1) { free(new_config); free(line_tokens); free(config_buffer); - if (config_dir) - free(config_dir); - return -1; + free(config_dir); + return (-1); } free(new_config); } else if (strcasecmp(line_tokens[0], "bank") == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { - WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in bank line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } patchid = (atoi(line_tokens[1]) & 0xFF) << 8; } else if (strcasecmp(line_tokens[0], "drumset") == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { - WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in drumset line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } patchid = ((atoi(line_tokens[1]) & 0xFF) << 8) | 0x80; - } else if (strcasecmp(line_tokens[0], "reverb_room_width") - == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { + } else if (strcasecmp(line_tokens[0], "reverb_room_width") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_room_width line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } reverb_room_width = (float) atof(line_tokens[1]); if (reverb_room_width < 1.0f) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(reverb_room_width < 1.0 meters, setting to minimum of 1.0 meter)", + "(reverb_room_width < 1 meter, setting to minimum of 1 meter)", 0); reverb_room_width = 1.0f; } else if (reverb_room_width > 100.0f) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(reverb_room_width > 100.0 meters, setting to maximum of 100.0 meters)", + "(reverb_room_width > 100 meters, setting to maximum of 100 meters)", 0); - reverb_room_width = 100.0; + reverb_room_width = 100.0f; } - } else if (strcasecmp(line_tokens[0], "reverb_room_length") - == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { + } else if (strcasecmp(line_tokens[0], "reverb_room_length") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_room_length line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } reverb_room_length = (float) atof(line_tokens[1]); if (reverb_room_length < 1.0f) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(reverb_room_length < 1.0 meters, setting to minimum of 1.0 meter)", + "(reverb_room_length < 1 meter, setting to minimum of 1 meter)", 0); reverb_room_length = 1.0f; } else if (reverb_room_length > 100.0f) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, - "(reverb_room_length > 100.0 meters, setting to maximum of 100.0 meters)", + "(reverb_room_length > 100 meters, setting to maximum of 100 meters)", 0); - reverb_room_length = 100.0; + reverb_room_length = 100.0f; } - } else if (strcasecmp(line_tokens[0], - "reverb_listener_posx") == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { + } else if (strcasecmp(line_tokens[0], "reverb_listener_posx") == 0) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_listen_posx line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } reverb_listen_posx = (float) atof(line_tokens[1]); if ((reverb_listen_posx > reverb_room_width) - || (reverb_listen_posx < 0.0)) { + || (reverb_listen_posx < 0.0f)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(reverb_listen_posx set outside of room)", 0); @@ -836,22 +850,21 @@ static int WM_LoadConfig(const char *config_file) { } } else if (strcasecmp(line_tokens[0], "reverb_listener_posy") == 0) { - if (!line_tokens[1] || !isdigit(line_tokens[1][0])) { + if (!line_tokens[1] || !wm_isdigit(line_tokens[1][0])) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(syntax error in reverb_listen_posy line)", 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } reverb_listen_posy = (float) atof(line_tokens[1]); if ((reverb_listen_posy > reverb_room_width) - || (reverb_listen_posy < 0.0)) { + || (reverb_listen_posy < 0.0f)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(reverb_listen_posy set outside of room)", 0); @@ -867,7 +880,7 @@ static int WM_LoadConfig(const char *config_file) { == 0) { auto_amp = 1; auto_amp_with_amp = 1; - } else if (isdigit(line_tokens[0][0])) { + } else if (wm_isdigit(line_tokens[0][0])) { patchid = (patchid & 0xFF80) | (atoi(line_tokens[0]) & 0x7F); if (patch[(patchid & 0x7F)] == NULL) { @@ -879,11 +892,10 @@ static int WM_LoadConfig(const char *config_file) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } tmp_patch = patch[(patchid & 0x7F)]; tmp_patch->patchid = patchid; @@ -918,11 +930,10 @@ static int WM_LoadConfig(const char *config_file) { WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } tmp_patch = tmp_patch->next; tmp_patch->patchid = patchid; @@ -949,11 +960,10 @@ static int WM_LoadConfig(const char *config_file) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } tmp_patch = tmp_patch->next; tmp_patch->patchid = patchid; @@ -967,10 +977,20 @@ static int WM_LoadConfig(const char *config_file) { } } } - if (line_tokens[1] && !IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { + if (!line_tokens[1]) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, + "(missing name in patch line)", 0); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, + config_file, 0); + WM_FreePatches(); + free(config_dir); + free(line_tokens); + free(config_buffer); + return (-1); + } else if (!IS_ABSOLUTE_PATH(line_tokens[1]) && config_dir) { tmp_patch->filename = malloc( strlen(config_dir) + strlen(line_tokens[1]) - + 1); + + 5); if (tmp_patch->filename == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); @@ -980,42 +1000,26 @@ static int WM_LoadConfig(const char *config_file) { free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } strcpy(tmp_patch->filename, config_dir); strcat(tmp_patch->filename, line_tokens[1]); } else { - if (!line_tokens[1] || !(tmp_patch->filename = malloc(strlen(line_tokens[1]) + 1))) { + if (!(tmp_patch->filename = wm_strdup(line_tokens[1]))) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, NULL, 0); WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0); WM_FreePatches(); - if (config_dir) - free(config_dir); + free(config_dir); free(line_tokens); free(config_buffer); - return -1; + return (-1); } - strcpy(tmp_patch->filename, line_tokens[1]); } if (strncasecmp( &tmp_patch->filename[strlen(tmp_patch->filename) - 4], ".pat", 4) != 0) { - tmp_patch->filename = realloc(tmp_patch->filename, - strlen(tmp_patch->filename) + 5); - if (tmp_patch->filename == NULL) { - WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, - NULL, 0); - WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, - config_file, 0); - WM_FreePatches(); - if (config_dir) - free(config_dir); - free(line_tokens); - free(config_buffer); - return -1; - } strcat(tmp_patch->filename, ".pat"); } tmp_patch->env[0].set = 0x00; @@ -1031,9 +1035,9 @@ static int WM_LoadConfig(const char *config_file) { while (line_tokens[token_count]) { if (strncasecmp(line_tokens[token_count], "amp=", 4) == 0) { - if (!isdigit(line_tokens[token_count][4])) { + if (!wm_isdigit(line_tokens[token_count][4])) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { tmp_patch->amp = (atoi( @@ -1042,9 +1046,9 @@ static int WM_LoadConfig(const char *config_file) { } } else if (strncasecmp(line_tokens[token_count], "note=", 5) == 0) { - if (!isdigit(line_tokens[token_count][5])) { + if (!wm_isdigit(line_tokens[token_count][5])) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { tmp_patch->note = atoi( @@ -1052,19 +1056,19 @@ static int WM_LoadConfig(const char *config_file) { } } else if (strncasecmp(line_tokens[token_count], "env_time", 8) == 0) { - if ((!isdigit(line_tokens[token_count][8])) - || (!isdigit( + if ((!wm_isdigit(line_tokens[token_count][8])) + || (!wm_isdigit( line_tokens[token_count][10])) || (line_tokens[token_count][9] != '=')) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { uint32_t env_no = atoi( &line_tokens[token_count][8]); if (env_no > 5) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { @@ -1076,7 +1080,7 @@ static int WM_LoadConfig(const char *config_file) { || (tmp_patch->env[env_no].time < 1.47f)) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(range error in patch line)", 0); tmp_patch->env[env_no].set &= 0xFE; @@ -1087,19 +1091,19 @@ static int WM_LoadConfig(const char *config_file) { } } else if (strncasecmp(line_tokens[token_count], "env_level", 9) == 0) { - if ((!isdigit(line_tokens[token_count][9])) - || (!isdigit( + if ((!wm_isdigit(line_tokens[token_count][9])) + || (!wm_isdigit( line_tokens[token_count][11])) || (line_tokens[token_count][10] != '=')) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { uint32_t env_no = atoi( &line_tokens[token_count][9]); if (env_no > 5) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(syntax error in patch line)", 0); } else { @@ -1110,7 +1114,7 @@ static int WM_LoadConfig(const char *config_file) { || (tmp_patch->env[env_no].level < 0.0f)) { WM_ERROR(__FUNCTION__, __LINE__, - WM_ERR_INVALID, + WM_ERR_INVALID_ARG, "(range error in patch line)", 0); tmp_patch->env[env_no].set &= 0xFD; @@ -1136,21 +1140,18 @@ static int WM_LoadConfig(const char *config_file) { } } } - /* - free up tokens - */ + /* free up tokens */ free(line_tokens); } line_start_ptr = config_ptr + 1; } config_ptr++; } - free(config_buffer); - if (config_dir) - free(config_dir); + free(config_buffer); + free(config_dir); - return 0; + return (0); } /* sample loading */ @@ -1164,7 +1165,7 @@ static int load_sample(struct _patch *sample_patch) { sample_patch->loaded = 1; if ((guspat = load_gus_pat(sample_patch->filename, fix_release)) == NULL) { - return -1; + return (-1); } if (auto_amp) { @@ -1255,25 +1256,25 @@ static int load_sample(struct _patch *sample_patch) { if (guspat->modes & SAMPLE_ENVELOPE) { if (sample_patch->env[i].set & 0x02) { guspat->env_target[i] = 16448 - * (uint32_t) (255.0 + * (int32_t) (255.0 * sample_patch->env[i].level); } if (sample_patch->env[i].set & 0x01) { - guspat->env_rate[i] = (uint32_t) (4194303.0 + guspat->env_rate[i] = (int32_t) (4194303.0 / ((float) WM_SampleRate * (sample_patch->env[i].time / 1000.0))); } } else { guspat->env_target[i] = 4194303; - guspat->env_rate[i] = (uint32_t) (4194303.0 + guspat->env_rate[i] = (int32_t) (4194303.0 / ((float) WM_SampleRate * env_time_table[63])); } } guspat = guspat->next; } while (guspat); - return 0; + return (0); } static struct _patch * @@ -1286,13 +1287,13 @@ get_patch_data(struct _mdi *mdi, uint16_t patchid) { if (search_patch == NULL) { WM_Unlock(&patch_lock); - return NULL; + return (NULL); } while (search_patch) { if (search_patch->patchid == patchid) { WM_Unlock(&patch_lock); - return search_patch; + return (search_patch); } search_patch = search_patch->next; } @@ -1301,7 +1302,7 @@ get_patch_data(struct _mdi *mdi, uint16_t patchid) { return (get_patch_data(mdi, patchid & 0x00FF)); } WM_Unlock(&patch_lock); - return NULL; + return (NULL); } static void load_patch(struct _mdi *mdi, uint16_t patchid) { @@ -1348,15 +1349,15 @@ get_sample_data(struct _patch *sample_patch, uint32_t freq) { WM_Lock(&patch_lock); if (sample_patch == NULL) { WM_Unlock(&patch_lock); - return NULL; + return (NULL); } if (sample_patch->first_sample == NULL) { WM_Unlock(&patch_lock); - return NULL; + return (NULL); } if (freq == 0) { WM_Unlock(&patch_lock); - return sample_patch->first_sample; + return (sample_patch->first_sample); } return_sample = sample_patch->first_sample; @@ -1365,7 +1366,7 @@ get_sample_data(struct _patch *sample_patch, uint32_t freq) { if (freq > last_sample->freq_low) { if (freq < last_sample->freq_high) { WM_Unlock(&patch_lock); - return last_sample; + return (last_sample); } else { return_sample = last_sample; } @@ -1373,7 +1374,7 @@ get_sample_data(struct _patch *sample_patch, uint32_t freq) { last_sample = last_sample->next; } WM_Unlock(&patch_lock); - return return_sample; + return (return_sample); } static void do_note_off_extra(struct _note *nte) { @@ -1611,13 +1612,13 @@ static void do_pan_adjust(struct _mdi *mdi, uint8_t ch) { } pan_adjust += 64; -// if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) { +/* if (mdi->info.mixer_options & WM_MO_LOG_VOLUME) {*/ left = (pan_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576; right = (pan_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576; -// } else { -// left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576; -// right= (lin_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576; -// } +/* } else { + left = (lin_volume[127 - pan_adjust] * WM_MasterVolume * amp) / 1048576; + right= (lin_volume[pan_adjust] * WM_MasterVolume * amp) / 1048576; + }*/ mdi->channel[ch].left_adjust = left; mdi->channel[ch].right_adjust = right; @@ -1634,11 +1635,11 @@ static void do_control_data_entry_course(struct _mdi *mdi, int data_tmp; if ((mdi->channel[ch].reg_non == 0) - && (mdi->channel[ch].reg_data == 0x0000)) { // Pitch Bend Range + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ data_tmp = mdi->channel[ch].pitch_range % 100; mdi->channel[ch].pitch_range = data->data * 100 + data_tmp; - // printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range); - // printf("Data Entry Course: data %li\n\r",data->data); + /* printf("Data Entry Course: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Course: data %li\n\r",data->data);*/ } } @@ -1703,11 +1704,11 @@ static void do_control_data_entry_fine(struct _mdi *mdi, int data_tmp; if ((mdi->channel[ch].reg_non == 0) - && (mdi->channel[ch].reg_data == 0x0000)) { // Pitch Bend Range + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ data_tmp = mdi->channel[ch].pitch_range / 100; mdi->channel[ch].pitch_range = (data_tmp * 100) + data->data; -// printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range); -// printf("Data Entry Fine: data: %li\n\r", data->data); + /* printf("Data Entry Fine: pitch_range: %i\n\r",mdi->channel[ch].pitch_range);*/ + /* printf("Data Entry Fine: data: %li\n\r", data->data);*/ } } @@ -1768,7 +1769,7 @@ static void do_control_data_increment(struct _mdi *mdi, uint8_t ch = data->channel; if ((mdi->channel[ch].reg_non == 0) - && (mdi->channel[ch].reg_data == 0x0000)) { // Pitch Bend Range + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ if (mdi->channel[ch].pitch_range < 0x3FFF) mdi->channel[ch].pitch_range++; } @@ -1779,7 +1780,7 @@ static void do_control_data_decrement(struct _mdi *mdi, uint8_t ch = data->channel; if ((mdi->channel[ch].reg_non == 0) - && (mdi->channel[ch].reg_data == 0x0000)) { // Pitch Bend Range + && (mdi->channel[ch].reg_data == 0x0000)) { /* Pitch Bend Range */ if (mdi->channel[ch].pitch_range > 0) mdi->channel[ch].pitch_range--; } @@ -2020,7 +2021,7 @@ static int midi_setup_noteoff(struct _mdi *mdi, uint8_t channel, mdi->events[mdi->event_count].samples_to_next = 0; mdi->event_count++; } - return 0; + return (0); } static int midi_setup_noteon(struct _mdi *mdi, uint8_t channel, @@ -2042,7 +2043,7 @@ static int midi_setup_noteon(struct _mdi *mdi, uint8_t channel, if (mdi->channel[channel].isdrum) load_patch(mdi, ((mdi->channel[channel].bank << 8) | (note | 0x80))); - return 0; + return (0); } static int midi_setup_aftertouch(struct _mdi *mdi, uint8_t channel, @@ -2061,7 +2062,7 @@ static int midi_setup_aftertouch(struct _mdi *mdi, uint8_t channel, mdi->events[mdi->event_count].samples_to_next = 0; mdi->event_count++; } - return 0; + return (0); } static int midi_setup_control(struct _mdi *mdi, uint8_t channel, @@ -2121,7 +2122,7 @@ static int midi_setup_control(struct _mdi *mdi, uint8_t channel, tmp_event = *do_control_channel_notes_off; break; default: - return 0; + return (0); } if ((mdi->event_count) && (mdi->events[mdi->event_count - 1].do_event == NULL)) { @@ -2136,7 +2137,7 @@ static int midi_setup_control(struct _mdi *mdi, uint8_t channel, mdi->events[mdi->event_count].samples_to_next = 0; mdi->event_count++; } - return 0; + return (0); } static int midi_setup_patch(struct _mdi *mdi, uint8_t channel, uint8_t patch) { @@ -2160,7 +2161,7 @@ static int midi_setup_patch(struct _mdi *mdi, uint8_t channel, uint8_t patch) { mdi->channel[channel].patch = get_patch_data(mdi, ((mdi->channel[channel].bank << 8) | patch)); } - return 0; + return (0); } static int midi_setup_channel_pressure(struct _mdi *mdi, uint8_t channel, @@ -2180,7 +2181,7 @@ static int midi_setup_channel_pressure(struct _mdi *mdi, uint8_t channel, mdi->event_count++; } - return 0; + return (0); } static int midi_setup_pitch(struct _mdi *mdi, uint8_t channel, uint16_t pitch) { @@ -2197,7 +2198,7 @@ static int midi_setup_pitch(struct _mdi *mdi, uint8_t channel, uint16_t pitch) { mdi->events[mdi->event_count].samples_to_next = 0; mdi->event_count++; } - return 0; + return (0); } static int midi_setup_sysex_roland_drum_track(struct _mdi *mdi, @@ -2223,7 +2224,7 @@ static int midi_setup_sysex_roland_drum_track(struct _mdi *mdi, mdi->channel[channel].isdrum = 0; } - return 0; + return (0); } static int midi_setup_sysex_roland_reset(struct _mdi *mdi) { @@ -2240,7 +2241,7 @@ static int midi_setup_sysex_roland_reset(struct _mdi *mdi) { mdi->events[mdi->event_count].samples_to_next = 0; mdi->event_count++; } - return 0; + return (0); } static int add_handle(void * handle) { @@ -2250,7 +2251,7 @@ static int add_handle(void * handle) { first_handle = malloc(sizeof(struct _hndl)); if (first_handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); - return -1; + return (-1); } first_handle->handle = handle; first_handle->prev = NULL; @@ -2264,14 +2265,14 @@ static int add_handle(void * handle) { tmp_handle->next = malloc(sizeof(struct _hndl)); if (tmp_handle->next == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, " to get ram", errno); - return -1; + return (-1); } tmp_handle->next->prev = tmp_handle; tmp_handle = tmp_handle->next; tmp_handle->next = NULL; tmp_handle->handle = handle; } - return 0; + return (0); } static struct _mdi * @@ -2302,7 +2303,37 @@ Init_MDI(void) { do_sysex_roland_reset(mdi, NULL); - return mdi; + return (mdi); +} + +static void freeMDI(struct _mdi *mdi) { + struct _sample *tmp_sample; + uint32_t i; + + if (mdi->patch_count != 0) { + WM_Lock(&patch_lock); + for (i = 0; i < mdi->patch_count; i++) { + mdi->patches[i]->inuse_count--; + if (mdi->patches[i]->inuse_count == 0) { + /* free samples here */ + while (mdi->patches[i]->first_sample) { + tmp_sample = mdi->patches[i]->first_sample->next; + free(mdi->patches[i]->first_sample->data); + free(mdi->patches[i]->first_sample); + mdi->patches[i]->first_sample = tmp_sample; + } + mdi->patches[i]->loaded = 0; + } + } + WM_Unlock(&patch_lock); + free(mdi->patches); + } + + free(mdi->events); + free(mdi->tmp_info); + free_reverb(mdi->reverb); + free(mdi->mix_buffer); + free(mdi); } static uint32_t get_decay_samples(struct _patch *patch, uint8_t note) { @@ -2311,12 +2342,11 @@ static uint32_t get_decay_samples(struct _patch *patch, uint8_t note) { uint32_t freq = 0; uint32_t decay_samples = 0; - if (patch == NULL) - return 0; + if (patch == NULL) return (0); - // first get the freq we need so we can check the right sample + /* first get the freq we need so we can check the right sample */ if (patch->patchid & 0x80) { - // is a drum patch + /* is a drum patch */ if (patch->note) { freq = freq_table[(patch->note % 12) * 100] >> (10 - (patch->note / 12)); @@ -2327,31 +2357,72 @@ static uint32_t get_decay_samples(struct _patch *patch, uint8_t note) { freq = freq_table[(note % 12) * 100] >> (10 - (note / 12)); } - // get the sample + /* get the sample */ sample = get_sample_data(patch, (freq / 100)); - if (sample == NULL) - return 0; + if (sample == NULL) return (0); if (patch->patchid & 0x80) { float sratedata = ((float) sample->rate / (float) WM_SampleRate) * (float) (sample->data_length >> 10); decay_samples = (uint32_t) sratedata; - // printf("Drums (%i / %i) * %lu = %f\n", sample->rate, WM_SampleRate, (sample->data_length >> 10), sratedata); + /* printf("Drums (%i / %i) * %lu = %f\n", sample->rate, WM_SampleRate, (sample->data_length >> 10), sratedata);*/ } else if (sample->modes & SAMPLE_CLAMPED) { decay_samples = (4194303 / sample->env_rate[5]); - // printf("clamped 4194303 / %lu = %lu\n", sample->env_rate[5], decay_samples); + /* printf("clamped 4194303 / %lu = %lu\n", sample->env_rate[5], decay_samples);*/ } else { decay_samples = ((4194303 - sample->env_target[4]) / sample->env_rate[4]) + (sample->env_target[4] / sample->env_rate[5]); - // printf("NOT clamped ((4194303 - %lu) / %lu) + (%lu / %lu)) = %lu\n", sample->env_target[4], sample->env_rate[4], sample->env_target[4], sample->env_rate[5], decay_samples); + /* printf("NOT clamped ((4194303 - %lu) / %lu) + (%lu / %lu)) = %lu\n", sample->env_target[4], sample->env_rate[4], sample->env_target[4], sample->env_rate[5], decay_samples);*/ } - return decay_samples; + return (decay_samples); +} + +WM_SYMBOL void* WildMidi_ConvertToMidi (const char *file, uint32_t *size) { + uint8_t *file_buffer, *midi_buffer; + + if (file == NULL) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)", + 0); + return (NULL); + } + /* pull in file data */ + if ((file_buffer = (uint8_t *) WM_BufferFile(file, size)) == NULL) { + return (NULL); + } + + /* determine data contents */ + if (!memcmp(file_buffer, "FORM", 4)) { + if (xmi2midi(file_buffer, *size, &midi_buffer, size, XMIDI_CONVERT_MT32_TO_GS) < 0) { + free(file_buffer); + return (NULL); + } + } + else if (!memcmp(file_buffer, "MUS", 3)) { + if (mus2midi(file_buffer, *size, &midi_buffer, size) < 0) { + free(file_buffer); + return (NULL); + } + } + else if (!memcmp(file_buffer, "MThd", 4)) { + WM_ERROR_NEW("%s:%i: %s is already a midi file.", __FUNCTION__, __LINE__, file); + free(file_buffer); + return (NULL); + } + else { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + free(file_buffer); + return (NULL); + } + + free(file_buffer); + return (midi_buffer); } static struct _mdi * WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { struct _mdi *mdi; + uint32_t tmp_val; uint32_t track_size; uint8_t **tracks; @@ -2380,20 +2451,40 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { uint8_t *running_event; uint32_t decay_samples = 0; - if (memcmp(midi_data, "RIFF", 4) == 0) { + uint8_t *cvt = NULL; + uint32_t cvt_size; + + if (!memcmp(midi_data, "FORM", 4)) { + if (xmi2midi(midi_data, midi_size, &cvt, &cvt_size, XMIDI_CONVERT_MT32_TO_GS) < 0) { + return (NULL); + } + midi_data = cvt; + midi_size = cvt_size; + } + else if (!memcmp(midi_data, "MUS", 3)) { + if (mus2midi(midi_data, midi_size, &cvt, &cvt_size) < 0) { + return (NULL); + } + midi_data = cvt; + midi_size = cvt_size; + } + else if (!memcmp(midi_data, "RIFF", 4)) { midi_data += 20; midi_size -= 20; } - if (memcmp(midi_data, "MThd", 4) != 0) { - printf("Not a midi file\n"); - return NULL; + + if (memcmp(midi_data, "MThd", 4)) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); + free(cvt); + return (NULL); } midi_data += 4; midi_size -= 4; if (midi_size < 10) { - printf("Midi File Too Short\n"); - return NULL; + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + free(cvt); + return (NULL); } /* @@ -2405,8 +2496,9 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tmp_val |= *midi_data++; midi_size -= 4; if (tmp_val != 6) { - printf("Corrupt Midi Header\n"); - return NULL; + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0); + free(cvt); + return (NULL); } /* @@ -2416,8 +2508,9 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tmp_val |= *midi_data++; midi_size -= 2; if (tmp_val > 1) { - printf("Midi Format Not Supported\n"); - return NULL; + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + free(cvt); + return (NULL); } /* @@ -2427,8 +2520,9 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tmp_val |= *midi_data++; midi_size -= 2; if (tmp_val < 1) { - printf("Midi Contains No Tracks\n"); - return NULL; + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(no tracks)", 0); + free(cvt); + return (NULL); } no_tracks = tmp_val; @@ -2439,8 +2533,9 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { divisions |= *midi_data++; midi_size -= 2; if (divisions & 0x00008000) { - printf("Division Type Not Supported\n"); - return NULL; + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID, NULL, 0); + free(cvt); + return (NULL); } if ((WM_MixerOptions & WM_MO_WHOLETEMPO)) { @@ -2450,7 +2545,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { float bpm_fr = (float) (60000000 / tempo) + 0.5f; tempo = 60000000 / (uint32_t) bpm_fr; } - //Slow but needed for accuracy + /* Slow but needed for accuracy */ microseconds_per_pulse = (float) tempo / (float) divisions; pulses_per_second = 1000000.0f / microseconds_per_pulse; samples_per_delta_f = (float) WM_SampleRate / pulses_per_second; @@ -2464,11 +2559,11 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { for (i = 0; i < no_tracks; i++) { if (midi_size < 8) { - printf("Midi File Too Short\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); goto _end; } if (memcmp(midi_data, "MTrk", 4) != 0) { - printf("Expected Track Header\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing track header)", 0); goto _end; } midi_data += 4; @@ -2480,13 +2575,13 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { track_size |= *midi_data++; midi_size -= 4; if (midi_size < track_size) { - printf("Midi File Too Short\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); goto _end; } if ((midi_data[track_size - 3] != 0xFF) || (midi_data[track_size - 2] != 0x2F) || (midi_data[track_size - 1] != 0x00)) { - printf("Corrupt Midi, Expected EOT\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing EOT)", 0); goto _end; } tracks[i] = midi_data; @@ -2527,7 +2622,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { } else { current_event = running_event[i]; if (running_event[i] < 0x80) { - printf("Invalid Data in Midi, Expected Event\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(missing event)", 0); goto _end; } } @@ -2536,8 +2631,8 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { case 0x8: NOTEOFF: midi_setup_noteoff(mdi, current_event_ch, tracks[i][0], tracks[i][1]); - // To better calculate samples needed after the end of midi, - // we calculate samples for decay for note off + /* To better calculate samples needed after the end of midi, + * we calculate samples for decay for note off */ { uint32_t tmp_decay_samples = 0; struct _patch *tmp_patch = NULL; @@ -2545,17 +2640,17 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tmp_patch = get_patch_data(mdi, ((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80)); - // if (tmp_patch == NULL) - // printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch); + /* if (tmp_patch == NULL) + printf("Drum patch not loaded 0x%02x on channel %i\n",((mdi->channel[current_event_ch].bank << 8) | tracks[i][0] | 0x80),current_event_ch);*/ } else { tmp_patch = mdi->channel[current_event_ch].patch; - // if (tmp_patch == NULL) - // printf("Channel %i patch not loaded\n", current_event_ch); + /* if (tmp_patch == NULL) + printf("Channel %i patch not loaded\n", current_event_ch);*/ } tmp_decay_samples = get_decay_samples(tmp_patch, tracks[i][0]); - // if the note off decay is more than the decay we currently tracking then - // we set it to new decay. + /* if the note off decay is more than the decay we currently tracking then + * we set it to new decay. */ if (tmp_decay_samples > decay_samples) { decay_samples = tmp_decay_samples; } @@ -2602,10 +2697,10 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tracks[i] += 2; running_event[i] = current_event; break; - case 0xF: // Meta Event + case 0xF: /* Meta Event */ if (current_event == 0xFF) { - if (tracks[i][0] == 0x02) { // Copyright Event - // Get Length + if (tracks[i][0] == 0x02) { /* Copyright Event */ + /* Get Length */ tmp_length = 0; tracks[i]++; while (*tracks[i] > 0x7f) { @@ -2615,7 +2710,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { } tmp_length = (tmp_length << 7) + (*tracks[i] & 0x7f); - // Copy copyright info in the getinfo struct + /* Copy copyright info in the getinfo struct */ if (mdi->info.copyright) { mdi->info.copyright = realloc( mdi->info.copyright, @@ -2638,13 +2733,13 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { tracks[i] += tmp_length + 1; } else if ((tracks[i][0] == 0x2F) && (tracks[i][1] == 0x00)) { - // End of Track + /* End of Track */ end_of_tracks++; track_end[i] = 1; goto NEXT_TRACK; } else if ((tracks[i][0] == 0x51) && (tracks[i][1] == 0x03)) { - // Tempo + /* Tempo */ tempo = (tracks[i][2] << 16) + (tracks[i][3] << 8) + tracks[i][4]; tracks[i] += 5; @@ -2659,7 +2754,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { + 0.5f; tempo = 60000000 / (uint32_t) bpm_fr; } - //Slow but needed for accuracy + /* Slow but needed for accuracy */ microseconds_per_pulse = (float) tempo / (float) divisions; pulses_per_second = 1000000.0f @@ -2681,7 +2776,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { } } else if ((current_event == 0xF0) || (current_event == 0xF7)) { - // Roland Sysex Events + /* Roland Sysex Events */ uint32_t sysex_len = 0; while (*tracks[i] > 0x7F) { sysex_len = (sysex_len << 7) + (*tracks[i] & 0x7F); @@ -2701,7 +2796,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { if (sysex_store[sysex_store_len - 1] == 0xF7) { uint8_t tmpsysexdata[] = { 0x41, 0x10, 0x42, 0x12 }; if (memcmp(tmpsysexdata, sysex_store, 4) == 0) { - //checksum + /* checksum */ uint8_t sysex_cs = 0; uint32_t sysex_ofs = 4; do { @@ -2712,13 +2807,13 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { sysex_ofs++; } while (sysex_store[sysex_ofs + 1] != 0xF7); sysex_cs = 128 - sysex_cs; - // is roland sysex message valid + /* is roland sysex message valid */ if (sysex_cs == sysex_store[sysex_ofs]) { - // process roland sysex event + /* process roland sysex event */ if (sysex_store[4] == 0x40) { if (((sysex_store[5] & 0xF0) == 0x10) && (sysex_store[6] == 0x15)) { - // Roland Drum Track Setting + /* Roland Drum Track Setting */ uint8_t sysex_ch = 0x0F & sysex_store[5]; if (sysex_ch == 0x00) { @@ -2732,7 +2827,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { } else if ((sysex_store[5] == 0x00) && (sysex_store[6] == 0x7F) && (sysex_store[7] == 0x00)) { - // Roland GS Reset + /* Roland GS Reset */ midi_setup_sysex_roland_reset(mdi); } } @@ -2744,12 +2839,12 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { } tracks[i] += sysex_len; } else { - printf("Um, WTF is this?\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized meta event)", 0); goto _end; } break; default: - printf("Should Never of Gotten Here\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); goto _end; } while (*tracks[i] > 0x7F) { @@ -2783,7 +2878,7 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { mdi->event_count++; } mdi->info.approx_total_samples += sample_count; - //printf("Decay Samples = %lu\n",decay_samples); + /* printf("Decay Samples = %lu\n",decay_samples);*/ if (decay_samples > sample_count) { decay_samples -= sample_count; } else { @@ -2796,19 +2891,19 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { mdi->events[mdi->event_count - 1].samples_to_next; mdi->event_count--; } - // Set total MIDI time to 1/1000's seconds + /* Set total MIDI time to 1/1000's seconds */ mdi->info.total_midi_time = (mdi->info.approx_total_samples * 1000) / WM_SampleRate; - //mdi->info.approx_total_samples += WM_SampleRate * 3; + /*mdi->info.approx_total_samples += WM_SampleRate * 3;*/ - // Add additional samples needed for decay + /* Add additional samples needed for decay */ mdi->info.approx_total_samples += decay_samples; - //printf("decay_samples = %lu\n",decay_samples); + /*printf("decay_samples = %lu\n",decay_samples);*/ if ((mdi->reverb = init_reverb(WM_SampleRate, reverb_room_width, reverb_room_length, reverb_listen_posx, reverb_listen_posy)) == NULL) { - printf("Reverb Init Failed\n"); + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0); goto _end; } @@ -2819,15 +2914,15 @@ WM_ParseNewMidi(uint8_t *midi_data, uint32_t midi_size) { WM_ResetToStart(mdi); -_end: if (sysex_store) free(sysex_store); +_end: free(sysex_store); + free(cvt); free(track_end); free(track_delta); free(running_event); free(tracks); - if (mdi->reverb) return mdi; - if (mdi->events) free(mdi->events); - free(mdi); - return NULL; + if (mdi->reverb) return (mdi); + freeMDI(mdi); + return (NULL); } static int WM_GetOutput_Linear(midi * handle, int8_t *buffer, uint32_t size) { @@ -2894,7 +2989,7 @@ static int WM_GetOutput_Linear(midi * handle, int8_t *buffer, uint32_t size) { } } - // do mixing here + /* do mixing here */ count = real_samples_to_mix; do { note_data = mdi->note; @@ -3004,7 +3099,7 @@ static int WM_GetOutput_Linear(midi * handle, int8_t *buffer, uint32_t size) { if (__builtin_expect((note_data->env_level == 0), 1)) { goto KILL_NOTE; } - // sample release + /* sample release */ if (note_data->modes & SAMPLE_LOOP) note_data->modes ^= SAMPLE_LOOP; note_data->env_inc = 0; @@ -3133,7 +3228,7 @@ static int WM_GetOutput_Linear(midi * handle, int8_t *buffer, uint32_t size) { } WM_Unlock(&mdi->lock); - return buffer_used; + return (buffer_used); } static int WM_GetOutput_Gauss(midi * handle, int8_t *buffer, uint32_t size) { @@ -3202,7 +3297,7 @@ static int WM_GetOutput_Gauss(midi * handle, int8_t *buffer, uint32_t size) { } } - // do mixing here + /* do mixing here */ count = real_samples_to_mix; do { note_data = mdi->note; @@ -3345,7 +3440,7 @@ static int WM_GetOutput_Gauss(midi * handle, int8_t *buffer, uint32_t size) { if (__builtin_expect((note_data->env_level == 0), 1)) { goto KILL_NOTE; } - // sample release + /* sample release */ if (note_data->modes & SAMPLE_LOOP) note_data->modes ^= SAMPLE_LOOP; note_data->env_inc = 0; @@ -3473,7 +3568,7 @@ static int WM_GetOutput_Gauss(midi * handle, int8_t *buffer, uint32_t size) { #endif } WM_Unlock(&mdi->lock); - return buffer_used; + return (buffer_used); } /* @@ -3483,30 +3578,30 @@ static int WM_GetOutput_Gauss(midi * handle, int8_t *buffer, uint32_t size) { */ WM_SYMBOL long WildMidi_GetVersion (void) { - return LIBWILDMIDI_VERSION; + return (LIBWILDMIDI_VERSION); } WM_SYMBOL int WildMidi_Init(const char *config_file, uint16_t rate, uint16_t options) { if (WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0); - return -1; + return (-1); } if (config_file == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL config file pointer)", 0); - return -1; + return (-1); } WM_InitPatches(); if (WM_LoadConfig(config_file) == -1) { - return -1; + return (-1); } - if (options & 0x7FD8) { + if (options & 0x5FF8) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0); WM_FreePatches(); - return -1; + return (-1); } WM_MixerOptions = options; @@ -3514,13 +3609,15 @@ WM_SYMBOL int WildMidi_Init(const char *config_file, uint16_t rate, uint16_t opt WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(rate out of bounds, range is 11025 - 65535)", 0); WM_FreePatches(); - return -1; + return (-1); } WM_SampleRate = rate; - WM_Initialized = 1; + + gauss_lock = 0; patch_lock = 0; + WM_Initialized = 1; - return 0; + return (0); } WM_SYMBOL int WildMidi_MasterVolume(uint8_t master_volume) { @@ -3530,12 +3627,12 @@ WM_SYMBOL int WildMidi_MasterVolume(uint8_t master_volume) { if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } if (master_volume > 127) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(master volume out of range, range is 0-127)", 0); - return -1; + return (-1); } WM_MasterVolume = lin_volume[master_volume]; @@ -3550,28 +3647,26 @@ WM_SYMBOL int WildMidi_MasterVolume(uint8_t master_volume) { } } - return 0; + return (0); } WM_SYMBOL int WildMidi_Close(midi * handle) { struct _mdi *mdi = (struct _mdi *) handle; struct _hndl * tmp_handle; - struct _sample *tmp_sample; - uint32_t i; if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } if (handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); - return -1; + return (-1); } if (first_handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(no midi's open)", 0); - return -1; + return (-1); } WM_Lock(&mdi->lock); if (first_handle->handle == handle) { @@ -3597,46 +3692,9 @@ WM_SYMBOL int WildMidi_Close(midi * handle) { } } - if (mdi->patch_count != 0) { - WM_Lock(&patch_lock); - for (i = 0; i < mdi->patch_count; i++) { - mdi->patches[i]->inuse_count--; - if (mdi->patches[i]->inuse_count == 0) { - //free samples here - if (mdi->patches[i]->first_sample) { - while (mdi->patches[i]->first_sample) { - tmp_sample = mdi->patches[i]->first_sample->next; - if (mdi->patches[i]->first_sample->data) - free(mdi->patches[i]->first_sample->data); - free(mdi->patches[i]->first_sample); - mdi->patches[i]->first_sample = tmp_sample; - } - mdi->patches[i]->loaded = 0; - } - } - } - WM_Unlock(&patch_lock); - free(mdi->patches); - } + freeMDI(mdi); - if (mdi->events) { - free(mdi->events); - } - - if (mdi->tmp_info) { - free(mdi->tmp_info); - } - - if (mdi->reverb) { - free_reverb(mdi->reverb); - } - - if (mdi->mix_buffer) { - free(mdi->mix_buffer); - } - - free(mdi); - return 0; + return (0); } WM_SYMBOL midi *WildMidi_Open(const char *midifile) { @@ -3646,16 +3704,16 @@ WM_SYMBOL midi *WildMidi_Open(const char *midifile) { if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return NULL; + return (NULL); } if (midifile == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL filename)", 0); - return NULL; + return (NULL); } if ((mididata = (uint8_t *) WM_BufferFile(midifile, &midisize)) == NULL) { - return NULL; + return (NULL); } ret = (void *) WM_ParseNewMidi(mididata, midisize); @@ -3668,7 +3726,7 @@ WM_SYMBOL midi *WildMidi_Open(const char *midifile) { } } - return ret; + return (ret); } WM_SYMBOL midi *WildMidi_OpenBuffer(uint8_t *midibuffer, uint32_t size) { @@ -3676,17 +3734,17 @@ WM_SYMBOL midi *WildMidi_OpenBuffer(uint8_t *midibuffer, uint32_t size) { if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return NULL; + return (NULL); } if (midibuffer == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL midi data buffer)", 0); - return NULL; + return (NULL); } if (size > WM_MAXFILESIZE) { /* don't bother loading suspiciously long files */ WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LONGFIL, NULL, 0); - return NULL; + return (NULL); } ret = (void *) WM_ParseNewMidi(midibuffer, size); @@ -3697,7 +3755,7 @@ WM_SYMBOL midi *WildMidi_OpenBuffer(uint8_t *midibuffer, uint32_t size) { } } - return ret; + return (ret); } WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { @@ -3709,48 +3767,48 @@ WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } if (handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); - return -1; + return (-1); } if (sample_pos == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL seek position pointer)", 0); - return -1; + return (-1); } mdi = (struct _mdi *) handle; WM_Lock(&mdi->lock); event = mdi->current_event; - // make sure we havent asked for a positions beyond the end of the song. + /* make sure we havent asked for a positions beyond the end of the song. */ if (*sample_pos > mdi->info.approx_total_samples) { - // if so set the position to the end of the song + /* if so set the position to the end of the song */ *sample_pos = mdi->info.approx_total_samples; } - // was end of song requested and are we are there? + /* was end of song requested and are we are there? */ if (*sample_pos == mdi->info.current_sample) { - // yes + /* yes */ WM_Unlock(&mdi->lock); - return 0; + return (0); } - // did we want to fast forward? + /* did we want to fast forward? */ if (mdi->info.current_sample < *sample_pos) { - // yes + /* yes */ count = *sample_pos - mdi->info.current_sample; } else { - // no, reset values to start as the beginning + /* no, reset values to start as the beginning */ count = *sample_pos; WM_ResetToStart(handle); event = mdi->current_event; } - // clear the reverb buffers since we not gonna be using them here + /* clear the reverb buffers since we not gonna be using them here */ reset_reverb(mdi->reverb); do { @@ -3800,42 +3858,37 @@ WM_SYMBOL int WildMidi_FastSeek(midi * handle, unsigned long int *sample_pos) { mdi->note = NULL; WM_Unlock(&mdi->lock); - return 0; + return (0); } WM_SYMBOL int WildMidi_GetOutput(midi * handle, int8_t *buffer, uint32_t size) { - struct _mdi *mdi = (struct _mdi *) handle; - if (__builtin_expect((!WM_Initialized), 0)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } if (__builtin_expect((handle == NULL), 0)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); - return -1; + return (-1); } if (__builtin_expect((buffer == NULL), 0)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL buffer pointer)", 0); - return -1; + return (-1); } - if (__builtin_expect((size == 0), 0)) { - return 0; + return (0); } - - if (__builtin_expect((size % 4), 0)) { + if (__builtin_expect((!!(size % 4)), 0)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(size not a multiple of 4)", 0); - return -1; + return (-1); } - if (mdi->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) { + if (((struct _mdi *) handle)->info.mixer_options & WM_MO_ENHANCED_RESAMPLING) { if (!gauss_table) init_gauss(); - return WM_GetOutput_Gauss(handle, buffer, size); - } else { - return WM_GetOutput_Linear(handle, buffer, size); + return (WM_GetOutput_Gauss(handle, buffer, size)); } + return (WM_GetOutput_Linear(handle, buffer, size)); } WM_SYMBOL int WildMidi_SetOption(midi * handle, uint16_t options, @@ -3846,25 +3899,25 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, uint16_t options, if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } if (handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); - return -1; + return (-1); } WM_Lock(&mdi->lock); if ((!(options & 0x0007)) || (options & 0xFFF8)) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)", 0); WM_Unlock(&mdi->lock); - return -1; + return (-1); } if (setting & 0xFFF8) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid setting)", 0); WM_Unlock(&mdi->lock); - return -1; + return (-1); } mdi->info.mixer_options = ((mdi->info.mixer_options & (0x00FF ^ options)) @@ -3890,7 +3943,7 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, uint16_t options, } WM_Unlock(&mdi->lock); - return 0; + return (0); } WM_SYMBOL struct _WM_Info * @@ -3898,12 +3951,12 @@ WildMidi_GetInfo(midi * handle) { struct _mdi *mdi = (struct _mdi *) handle; if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return NULL; + return (NULL); } if (handle == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)", 0); - return NULL; + return (NULL); } WM_Lock(&mdi->lock); if (mdi->tmp_info == NULL) { @@ -3911,7 +3964,7 @@ WildMidi_GetInfo(midi * handle) { if (mdi->tmp_info == NULL) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to set info", 0); WM_Unlock(&mdi->lock); - return NULL; + return (NULL); } mdi->tmp_info->copyright = NULL; } @@ -3919,35 +3972,28 @@ WildMidi_GetInfo(midi * handle) { mdi->tmp_info->approx_total_samples = mdi->info.approx_total_samples; mdi->tmp_info->mixer_options = mdi->info.mixer_options; if (mdi->info.copyright) { - if (mdi->tmp_info->copyright) { - free(mdi->tmp_info->copyright); - } + free(mdi->tmp_info->copyright); mdi->tmp_info->copyright = malloc(strlen(mdi->info.copyright) + 1); strcpy(mdi->tmp_info->copyright, mdi->info.copyright); } else { mdi->tmp_info->copyright = NULL; } WM_Unlock(&mdi->lock); - return mdi->tmp_info; + return (mdi->tmp_info); } WM_SYMBOL int WildMidi_Shutdown(void) { - struct _hndl * tmp_hdle; - if (!WM_Initialized) { WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0); - return -1; + return (-1); } - if (first_handle) { - while (first_handle) { - tmp_hdle = first_handle->next; - WildMidi_Close((struct _mdi *) first_handle->handle); - free(first_handle); - first_handle = tmp_hdle; - } + while (first_handle) { + /* closes open handle and rotates the handles list. */ + WildMidi_Close((struct _mdi *) first_handle->handle); } WM_FreePatches(); free_gauss(); WM_Initialized = 0; - return 0; + + return (0); } diff --git a/src/wm_error.c b/src/wm_error.c index 039bcd8f..edc0f00e 100644 --- a/src/wm_error.c +++ b/src/wm_error.c @@ -42,7 +42,8 @@ void WM_ERROR_NEW(const char * wmfmt, ...) { void WM_ERROR(const char * func, unsigned int lne, int wmerno, const char * wmfor, int error) { - static const char *errors[] = { + static const char *errors[WM_ERR_MAX+1] = { + "No error", "Unable to obtain memory", "Unable to stat", "Unable to load", @@ -53,9 +54,15 @@ void WM_ERROR(const char * func, unsigned int lne, int wmerno, "Library not Initialized", "Invalid argument", "Library Already Initialized", - "Refusing to load unusually long file" + "Not a midi file", + "Refusing to load unusually long file", + + "Invalid error code" }; + if (wmerno < 0 || wmerno > WM_ERR_MAX) + wmerno = WM_ERR_MAX; + if (wmfor != NULL) { if (error != 0) { fprintf(stderr, "\rlibWildMidi(%s:%u): ERROR %s %s (%s)\n", func, diff --git a/src/wm_tty.c b/src/wm_tty.c new file mode 100644 index 00000000..bef1d578 --- /dev/null +++ b/src/wm_tty.c @@ -0,0 +1,69 @@ +/* + wm_tty.c - unix termios code for player + + Copyright (C) Chris Ison 2001-2011 + Copyright (C) Bret Curtis 2013-2014 + + This file is part of WildMIDI. + + WildMIDI is free software: you can redistribute and/or modify the player + under the terms of the GNU General Public License and you can redistribute + and/or modify the library under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either version 3 of + the licenses, or(at your option) any later version. + + WildMIDI is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and + the GNU Lesser General Public License for more details. + + You should have received a copy of the GNU General Public License and the + GNU Lesser General Public License along with WildMIDI. If not, see + . +*/ + +#include "config.h" + +#if !defined(_WIN32) && !defined(__DJGPP__) + +#define _XOPEN_SOURCE 600 /* for ONLCR */ + +#include +#include +#include + +#ifndef FNONBLOCK +#define FNONBLOCK O_NONBLOCK +#endif + +struct termios _tty; +static tcflag_t _res_oflg = 0; +static tcflag_t _res_lflg = 0; + +void wm_inittty(void) { + if (!isatty(STDIN_FILENO)) + return; + + /* save tty: */ + tcgetattr(STDIN_FILENO, &_tty); + _res_oflg = _tty.c_oflag; + _res_lflg = _tty.c_lflag; + + /* set raw: */ + _tty.c_lflag &= ~(ICANON | ICRNL | ISIG); + _tty.c_oflag &= ~ONLCR; + tcsetattr(STDIN_FILENO, TCSANOW, &_tty); + + fcntl(STDIN_FILENO, F_SETFL, FNONBLOCK); +} + +void wm_resetty(void) { + if (!isatty(STDIN_FILENO)) + return; + + /* reset tty: */ + _tty.c_oflag = _res_oflg; + _tty.c_lflag = _res_lflg; + tcsetattr(STDIN_FILENO, TCSADRAIN, &_tty); +} +#endif /* !_WIN32, !__DJGPP__ */ diff --git a/src/xmidi.c b/src/xmidi.c new file mode 100644 index 00000000..15703f8a --- /dev/null +++ b/src/xmidi.c @@ -0,0 +1,1093 @@ +/* + XMIDI: Miles XMIDI to MID Library + + Copyright (C) 2001 Ryan Nunn + Copyright (C) 2014 Bret Curtis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/* XMIDI Converter */ + +#include +#include +#include +#include + +#include "xmidi.h" +#include "wm_error.h" + +/* Midi Status Bytes */ +#define MIDI_STATUS_NOTE_OFF 0x8 +#define MIDI_STATUS_NOTE_ON 0x9 +#define MIDI_STATUS_AFTERTOUCH 0xA +#define MIDI_STATUS_CONTROLLER 0xB +#define MIDI_STATUS_PROG_CHANGE 0xC +#define MIDI_STATUS_PRESSURE 0xD +#define MIDI_STATUS_PITCH_WHEEL 0xE +#define MIDI_STATUS_SYSEX 0xF + +typedef struct _midi_event { + int32_t time; + uint8_t status; + uint8_t data[2]; + uint32_t len; + uint8_t *buffer; + struct _midi_event *next; +} midi_event; + +typedef struct { + uint16_t type; + uint16_t tracks; +} midi_descriptor; + +struct xmi_ctx { + uint8_t *src, *src_ptr; + uint32_t srcsize; + uint32_t datastart; + uint8_t *dst, *dst_ptr; + uint32_t dstsize, dstrem; + int convert_type; + midi_descriptor info; + int bank127[16]; + midi_event **events; + signed short *timing; + midi_event *list; + midi_event *current; +}; + +/* forward declarations of private functions */ +static void DeleteEventList(midi_event *mlist); +static void CreateNewEvent(struct xmi_ctx *ctx, int32_t time); /* List manipulation */ +static int GetVLQ(struct xmi_ctx *ctx, uint32_t *quant); /* Variable length quantity */ +static int GetVLQ2(struct xmi_ctx *ctx, uint32_t *quant);/* Variable length quantity */ +static int PutVLQ(struct xmi_ctx *ctx, uint32_t value); /* Variable length quantity */ +static int ConvertEvent(struct xmi_ctx *ctx, + const int32_t time, const uint8_t status, const int size); +static int32_t ConvertSystemMessage(struct xmi_ctx *ctx, + const int32_t time, const uint8_t status); +static int32_t ConvertFiletoList(struct xmi_ctx *ctx); +static uint32_t ConvertListToMTrk(struct xmi_ctx *ctx, midi_event *mlist); +static int ParseXMI(struct xmi_ctx *ctx); +static int ExtractTracks(struct xmi_ctx *ctx); +static uint32_t ExtractTracksFromXmi(struct xmi_ctx *ctx); + +static uint32_t read1(struct xmi_ctx *ctx) +{ + uint8_t b0; + b0 = *ctx->src_ptr++; + return (b0); +} + +static uint32_t read2(struct xmi_ctx *ctx) +{ + uint8_t b0, b1; + b0 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + return (b0 + (b1 << 8)); +} + +static uint32_t read4(struct xmi_ctx *ctx) +{ + uint8_t b0, b1, b2, b3; + b3 = *ctx->src_ptr++; + b2 = *ctx->src_ptr++; + b1 = *ctx->src_ptr++; + b0 = *ctx->src_ptr++; + return (b0 + (b1<<8) + (b2<<16) + (b3<<24)); +} + +static void copy(struct xmi_ctx *ctx, char *b, uint32_t len) +{ + memcpy(b, ctx->src_ptr, len); + ctx->src_ptr += len; +} + +#define DST_CHUNK 8192 +static void resize_dst(struct xmi_ctx *ctx) { + uint32_t pos = ctx->dst_ptr - ctx->dst; + ctx->dst = realloc(ctx->dst, ctx->dstsize + DST_CHUNK); + ctx->dstsize += DST_CHUNK; + ctx->dstrem += DST_CHUNK; + ctx->dst_ptr = ctx->dst + pos; +} + +static void write1(struct xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 1) + resize_dst(ctx); + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem--; +} + +static void write2(struct xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 2) + resize_dst(ctx); + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 2; +} + +static void write4(struct xmi_ctx *ctx, uint32_t val) +{ + if (ctx->dstrem < 4) + resize_dst(ctx); + *ctx->dst_ptr++ = (val>>24)&0xff; + *ctx->dst_ptr++ = (val>>16)&0xff; + *ctx->dst_ptr++ = (val>>8) & 0xff; + *ctx->dst_ptr++ = val & 0xff; + ctx->dstrem -= 4; +} + +static void seeksrc(struct xmi_ctx *ctx, uint32_t pos) { + ctx->src_ptr = ctx->src + pos; +} + +static void seekdst(struct xmi_ctx *ctx, uint32_t pos) { + ctx->dst_ptr = ctx->dst + pos; + while (ctx->dstsize < pos) + resize_dst(ctx); + ctx->dstrem = ctx->dstsize - pos; +} + +static void skipsrc(struct xmi_ctx *ctx, int32_t pos) { + ctx->src_ptr += pos; +} + +static void skipdst(struct xmi_ctx *ctx, int32_t pos) { + size_t newpos; + ctx->dst_ptr += pos; + newpos = ctx->dst_ptr - ctx->dst; + while (ctx->dstsize < newpos) + resize_dst(ctx); + ctx->dstrem = ctx->dstsize - newpos; +} + +static uint32_t getsrcsize(struct xmi_ctx *ctx) { + return (ctx->srcsize); +} + +static uint32_t getsrcpos(struct xmi_ctx *ctx) { + return (ctx->src_ptr - ctx->src); +} + +static uint32_t getdstpos(struct xmi_ctx *ctx) { + return (ctx->dst_ptr - ctx->dst); +} + +/* This is a default set of patches to convert from MT32 to GM + * The index is the MT32 Patch number and the value is the GM Patch + * This is only suitable for music that doesn't do timbre changes + * XMIDIs that contain Timbre changes will not convert properly. */ +static const char mt32asgm[128] = { + 0, /* 0 Piano 1 */ + 1, /* 1 Piano 2 */ + 2, /* 2 Piano 3 (synth) */ + 4, /* 3 EPiano 1 */ + 4, /* 4 EPiano 2 */ + 5, /* 5 EPiano 3 */ + 5, /* 6 EPiano 4 */ + 3, /* 7 Honkytonk */ + 16, /* 8 Organ 1 */ + 17, /* 9 Organ 2 */ + 18, /* 10 Organ 3 */ + 16, /* 11 Organ 4 */ + 19, /* 12 Pipe Organ 1 */ + 19, /* 13 Pipe Organ 2 */ + 19, /* 14 Pipe Organ 3 */ + 21, /* 15 Accordion */ + 6, /* 16 Harpsichord 1 */ + 6, /* 17 Harpsichord 2 */ + 6, /* 18 Harpsichord 3 */ + 7, /* 19 Clavinet 1 */ + 7, /* 20 Clavinet 2 */ + 7, /* 21 Clavinet 3 */ + 8, /* 22 Celesta 1 */ + 8, /* 23 Celesta 2 */ + 62, /* 24 Synthbrass 1 (62) */ + 63, /* 25 Synthbrass 2 (63) */ + 62, /* 26 Synthbrass 3 Bank 8 */ + 63, /* 27 Synthbrass 4 Bank 8 */ + 38, /* 28 Synthbass 1 */ + 39, /* 29 Synthbass 2 */ + 38, /* 30 Synthbass 3 Bank 8 */ + 39, /* 31 Synthbass 4 Bank 8 */ + 88, /* 32 Fantasy */ + 90, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, /* 35 Glass */ + 97, /* 36 Soundtrack */ + 99, /* 37 Atmosphere */ + 14, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, /* 41 IceRain */ + 68, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, /* 44 DoctorSolo Bank 8 */ + 87, /* 45 SchoolDaze, no real equiv */ + 112, /* 46 Bell Singer */ + 80, /* 47 SquareWave */ + 48, /* 48 Strings 1 */ + 48, /* 49 Strings 2 - should be 49 */ + 44, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, /* 51 Pizzicato Strings */ + 40, /* 52 Violin 1 */ + 40, /* 53 Violin 2 ? Viola */ + 42, /* 54 Cello 1 */ + 42, /* 55 Cello 2 */ + 43, /* 56 Contrabass */ + 46, /* 57 Harp 1 */ + 46, /* 58 Harp 2 */ + 24, /* 59 Guitar 1 (Nylon) */ + 25, /* 60 Guitar 2 (Steel) */ + 26, /* 61 Elec Guitar 1 */ + 27, /* 62 Elec Guitar 2 */ + 104, /* 63 Sitar */ + 32, /* 64 Acou Bass 1 */ + 32, /* 65 Acou Bass 2 */ + 33, /* 66 Elec Bass 1 */ + 34, /* 67 Elec Bass 2 */ + 36, /* 68 Slap Bass 1 */ + 37, /* 69 Slap Bass 2 */ + 35, /* 70 Fretless Bass 1 */ + 35, /* 71 Fretless Bass 2 */ + 73, /* 72 Flute 1 */ + 73, /* 73 Flute 2 */ + 72, /* 74 Piccolo 1 */ + 72, /* 75 Piccolo 2 */ + 74, /* 76 Recorder */ + 75, /* 77 Pan Pipes */ + 64, /* 78 Sax 1 */ + 65, /* 79 Sax 2 */ + 66, /* 80 Sax 3 */ + 67, /* 81 Sax 4 */ + 71, /* 82 Clarinet 1 */ + 71, /* 83 Clarinet 2 */ + 68, /* 84 Oboe */ + 69, /* 85 English Horn (Cor Anglais) */ + 70, /* 86 Bassoon */ + 22, /* 87 Harmonica */ + 56, /* 88 Trumpet 1 */ + 56, /* 89 Trumpet 2 */ + 57, /* 90 Trombone 1 */ + 57, /* 91 Trombone 2 */ + 60, /* 92 French Horn 1 */ + 60, /* 93 French Horn 2 */ + 58, /* 94 Tuba */ + 61, /* 95 Brass Section 1 */ + 61, /* 96 Brass Section 2 */ + 11, /* 97 Vibes 1 */ + 11, /* 98 Vibes 2 */ + 99, /* 99 Syn Mallet Bank 1 */ + 112, /* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, /* 101 Glockenspiel */ + 14, /* 102 Tubular Bells */ + 13, /* 103 Xylophone */ + 12, /* 104 Marimba */ + 107, /* 105 Koto */ + 111, /* 106 Sho?? set to Shanai(111) */ + 77, /* 107 Shakauhachi */ + 78, /* 108 Whistle 1 */ + 78, /* 109 Whistle 2 */ + 76, /* 110 Bottle Blow */ + 76, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, /* 112 Timpani */ + 117, /* 113 Melodic Tom */ + 116, /* 114 Deap Snare no equiv, set to Taiko(116) */ + 118, /* 115 Electric Perc 1 */ + 118, /* 116 Electric Perc 2 */ + 116, /* 117 Taiko */ + 115, /* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119, /* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115, /* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112, /* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, /* 122 Orchestral Hit */ + 124, /* 123 Telephone */ + 123, /* 124 BirdTweet */ + 94, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, /* 126 Water Bell set to Crystal Pad(98) */ + 121 /* 127 Jungle Tune set to Breath Noise */ +}; + +/* Same as above, except include patch changes + * so GS instruments can be used */ +static const char mt32asgs[256] = { + 0, 0, /* 0 Piano 1 */ + 1, 0, /* 1 Piano 2 */ + 2, 0, /* 2 Piano 3 (synth) */ + 4, 0, /* 3 EPiano 1 */ + 4, 0, /* 4 EPiano 2 */ + 5, 0, /* 5 EPiano 3 */ + 5, 0, /* 6 EPiano 4 */ + 3, 0, /* 7 Honkytonk */ + 16, 0, /* 8 Organ 1 */ + 17, 0, /* 9 Organ 2 */ + 18, 0, /* 10 Organ 3 */ + 16, 0, /* 11 Organ 4 */ + 19, 0, /* 12 Pipe Organ 1 */ + 19, 0, /* 13 Pipe Organ 2 */ + 19, 0, /* 14 Pipe Organ 3 */ + 21, 0, /* 15 Accordion */ + 6, 0, /* 16 Harpsichord 1 */ + 6, 0, /* 17 Harpsichord 2 */ + 6, 0, /* 18 Harpsichord 3 */ + 7, 0, /* 19 Clavinet 1 */ + 7, 0, /* 20 Clavinet 2 */ + 7, 0, /* 21 Clavinet 3 */ + 8, 0, /* 22 Celesta 1 */ + 8, 0, /* 23 Celesta 2 */ + 62, 0, /* 24 Synthbrass 1 (62) */ + 63, 0, /* 25 Synthbrass 2 (63) */ + 62, 0, /* 26 Synthbrass 3 Bank 8 */ + 63, 0, /* 27 Synthbrass 4 Bank 8 */ + 38, 0, /* 28 Synthbass 1 */ + 39, 0, /* 29 Synthbass 2 */ + 38, 0, /* 30 Synthbass 3 Bank 8 */ + 39, 0, /* 31 Synthbass 4 Bank 8 */ + 88, 0, /* 32 Fantasy */ + 90, 0, /* 33 Harmonic Pan - No equiv closest is polysynth(90) :( */ + 52, 0, /* 34 Choral ?? Currently set to SynthVox(54). Should it be ChoirAhhs(52)??? */ + 92, 0, /* 35 Glass */ + 97, 0, /* 36 Soundtrack */ + 99, 0, /* 37 Atmosphere */ + 14, 0, /* 38 Warmbell, sounds kind of like crystal(98) perhaps Tubular Bells(14) would be better. It is! */ + 54, 0, /* 39 FunnyVox, sounds alot like Bagpipe(109) and Shania(111) */ + 98, 0, /* 40 EchoBell, no real equiv, sounds like Crystal(98) */ + 96, 0, /* 41 IceRain */ + 68, 0, /* 42 Oboe 2001, no equiv, just patching it to normal oboe(68) */ + 95, 0, /* 43 EchoPans, no equiv, setting to SweepPad */ + 81, 0, /* 44 DoctorSolo Bank 8 */ + 87, 0, /* 45 SchoolDaze, no real equiv */ + 112, 0, /* 46 Bell Singer */ + 80, 0, /* 47 SquareWave */ + 48, 0, /* 48 Strings 1 */ + 48, 0, /* 49 Strings 2 - should be 49 */ + 44, 0, /* 50 Strings 3 (Synth) - Experimental set to Tremollo Strings - should be 50 */ + 45, 0, /* 51 Pizzicato Strings */ + 40, 0, /* 52 Violin 1 */ + 40, 0, /* 53 Violin 2 ? Viola */ + 42, 0, /* 54 Cello 1 */ + 42, 0, /* 55 Cello 2 */ + 43, 0, /* 56 Contrabass */ + 46, 0, /* 57 Harp 1 */ + 46, 0, /* 58 Harp 2 */ + 24, 0, /* 59 Guitar 1 (Nylon) */ + 25, 0, /* 60 Guitar 2 (Steel) */ + 26, 0, /* 61 Elec Guitar 1 */ + 27, 0, /* 62 Elec Guitar 2 */ + 104, 0, /* 63 Sitar */ + 32, 0, /* 64 Acou Bass 1 */ + 32, 0, /* 65 Acou Bass 2 */ + 33, 0, /* 66 Elec Bass 1 */ + 34, 0, /* 67 Elec Bass 2 */ + 36, 0, /* 68 Slap Bass 1 */ + 37, 0, /* 69 Slap Bass 2 */ + 35, 0, /* 70 Fretless Bass 1 */ + 35, 0, /* 71 Fretless Bass 2 */ + 73, 0, /* 72 Flute 1 */ + 73, 0, /* 73 Flute 2 */ + 72, 0, /* 74 Piccolo 1 */ + 72, 0, /* 75 Piccolo 2 */ + 74, 0, /* 76 Recorder */ + 75, 0, /* 77 Pan Pipes */ + 64, 0, /* 78 Sax 1 */ + 65, 0, /* 79 Sax 2 */ + 66, 0, /* 80 Sax 3 */ + 67, 0, /* 81 Sax 4 */ + 71, 0, /* 82 Clarinet 1 */ + 71, 0, /* 83 Clarinet 2 */ + 68, 0, /* 84 Oboe */ + 69, 0, /* 85 English Horn (Cor Anglais) */ + 70, 0, /* 86 Bassoon */ + 22, 0, /* 87 Harmonica */ + 56, 0, /* 88 Trumpet 1 */ + 56, 0, /* 89 Trumpet 2 */ + 57, 0, /* 90 Trombone 1 */ + 57, 0, /* 91 Trombone 2 */ + 60, 0, /* 92 French Horn 1 */ + 60, 0, /* 93 French Horn 2 */ + 58, 0, /* 94 Tuba */ + 61, 0, /* 95 Brass Section 1 */ + 61, 0, /* 96 Brass Section 2 */ + 11, 0, /* 97 Vibes 1 */ + 11, 0, /* 98 Vibes 2 */ + 99, 0, /* 99 Syn Mallet Bank 1 */ + 112, 0, /* 100 WindBell no real equiv Set to TinkleBell(112) */ + 9, 0, /* 101 Glockenspiel */ + 14, 0, /* 102 Tubular Bells */ + 13, 0, /* 103 Xylophone */ + 12, 0, /* 104 Marimba */ + 107, 0, /* 105 Koto */ + 111, 0, /* 106 Sho?? set to Shanai(111) */ + 77, 0, /* 107 Shakauhachi */ + 78, 0, /* 108 Whistle 1 */ + 78, 0, /* 109 Whistle 2 */ + 76, 0, /* 110 Bottle Blow */ + 76, 0, /* 111 Breathpipe no real equiv set to bottle blow(76) */ + 47, 0, /* 112 Timpani */ + 117, 0, /* 113 Melodic Tom */ + 116, 0, /* 114 Deap Snare no equiv, set to Taiko(116) */ + 118, 0, /* 115 Electric Perc 1 */ + 118, 0, /* 116 Electric Perc 2 */ + 116, 0, /* 117 Taiko */ + 115, 0, /* 118 Taiko Rim, no real equiv, set to Woodblock(115) */ + 119, 0, /* 119 Cymbal, no real equiv, set to reverse cymbal(119) */ + 115, 0, /* 120 Castanets, no real equiv, in GM set to Woodblock(115) */ + 112, 0, /* 121 Triangle, no real equiv, set to TinkleBell(112) */ + 55, 0, /* 122 Orchestral Hit */ + 124, 0, /* 123 Telephone */ + 123, 0, /* 124 BirdTweet */ + 94, 0, /* 125 Big Notes Pad no equiv, set to halo pad (94) */ + 98, 0, /* 126 Water Bell set to Crystal Pad(98) */ + 121, 0 /* 127 Jungle Tune set to Breath Noise */ +}; + +int xmi2midi(uint8_t *in, uint32_t insize, + uint8_t **out, uint32_t *outsize, + int convert_type) { + struct xmi_ctx ctx; + unsigned int i; + int ret = -1; + + memset(&ctx, 0, sizeof(struct xmi_ctx)); + ctx.src = ctx.src_ptr = in; + ctx.srcsize = insize; + ctx.convert_type = convert_type; + + if (ParseXMI(&ctx) < 0) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); + goto _end; + } + + if (ExtractTracks(&ctx) < 0) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_MIDI, NULL, 0); + goto _end; + } + + ctx.dst = malloc(DST_CHUNK); + ctx.dst_ptr = ctx.dst; + ctx.dstsize = DST_CHUNK; + ctx.dstrem = DST_CHUNK; + + /* Header is 14 bytes long and add the rest as well */ + write1(&ctx, 'M'); + write1(&ctx, 'T'); + write1(&ctx, 'h'); + write1(&ctx, 'd'); + + write4(&ctx, 6); + + write2(&ctx, ctx.info.type); + write2(&ctx, ctx.info.tracks); + write2(&ctx, ctx.timing[0]);/* write divisions from track0 */ + + for (i = 0; i < ctx.info.tracks; i++) + ConvertListToMTrk(&ctx, ctx.events[i]); + *out = ctx.dst; + *outsize = ctx.dstsize - ctx.dstrem; + ret = 0; + +_end: /* cleanup */ + if (ret < 0) { + free(ctx.dst); + *out = NULL; + *outsize = 0; + } + if (ctx.events) { + for (i = 0; i < ctx.info.tracks; i++) + DeleteEventList(ctx.events[i]); + free(ctx.events); + } + free(ctx.timing); + + return (ret); +} + +static void DeleteEventList(midi_event *mlist) { + midi_event *event; + midi_event *next; + + next = mlist; + + while ((event = next) != NULL) { + next = event->next; + free(event->buffer); + free(event); + } +} + +/* Sets current to the new event and updates list */ +static void CreateNewEvent(struct xmi_ctx *ctx, int32_t time) { + if (!ctx->list) { + ctx->list = ctx->current = calloc(1, sizeof(midi_event)); + ctx->current->time = (time < 0)? 0 : time; + return; + } + + if (time < 0) { + midi_event *event = calloc(1, sizeof(midi_event)); + event->next = ctx->list; + ctx->list = ctx->current = event; + return; + } + + if (ctx->current->time > time) + ctx->current = ctx->list; + + while (ctx->current->next) { + if (ctx->current->next->time > time) { + midi_event *event = calloc(1, sizeof(midi_event)); + event->next = ctx->current->next; + ctx->current->next = event; + ctx->current = event; + ctx->current->time = time; + return; + } + + ctx->current = ctx->current->next; + } + + ctx->current->next = calloc(1, sizeof(midi_event)); + ctx->current = ctx->current->next; + ctx->current->time = time; +} + +/* Conventional Variable Length Quantity */ +static int GetVLQ(struct xmi_ctx *ctx, uint32_t *quant) { + int i; + uint32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = read1(ctx); + *quant <<= 7; + *quant |= data & 0x7F; + + if (!(data & 0x80)) { + i++; + break; + } + } + return (i); +} + +/* XMIDI Delta Variable Length Quantity */ +static int GetVLQ2(struct xmi_ctx *ctx, uint32_t *quant) { + int i; + int32_t data; + + *quant = 0; + for (i = 0; i < 4; i++) { + data = read1(ctx); + if (data & 0x80) { + skipsrc(ctx, -1); + break; + } + *quant += data; + } + return (i); +} + +static int PutVLQ(struct xmi_ctx *ctx, uint32_t value) { + int32_t buffer; + int i = 1, j; + buffer = value & 0x7F; + while (value >>= 7) { + buffer <<= 8; + buffer |= ((value & 0x7F) | 0x80); + i++; + } + for (j = 0; j < i; j++) { + write1(ctx, buffer & 0xFF); + buffer >>= 8; + } + + return (i); +} + +/* Converts Events + * + * Source is at the first data byte + * size 1 is single data byte + * size 2 is dual data byte + * size 3 is XMI Note on + * Returns bytes converted */ +static int ConvertEvent(struct xmi_ctx *ctx, const int32_t time, + const uint8_t status, const int size) { + uint32_t delta = 0; + int32_t data; + midi_event *prev; + int i; + + data = read1(ctx); + + /* Bank changes are handled here */ + if ((status >> 4) == 0xB && data == 0) { + data = read1(ctx); + + ctx->bank127[status & 0xF] = 0; + + if ( ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127 || + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM + && (status & 0xF) == 9) ) + return (2); + + CreateNewEvent(ctx, time); + ctx->current->status = status; + ctx->current->data[0] = 0; + ctx->current->data[1] = data; + + if (ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && data == 127) + ctx->bank127[status & 0xF] = 1; + + return (2); + } + + /* Handling for patch change mt32 conversion, probably should go elsewhere */ + if ((status >> 4) == 0xC && (status&0xF) != 9 + && ctx->convert_type != XMIDI_CONVERT_NOCONVERSION) + { + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GM) + { + data = mt32asgm[data]; + } + else if ((ctx->convert_type == XMIDI_CONVERT_GS127_TO_GS && ctx->bank127[status&0xF]) || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS || + ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM) + { + CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = mt32asgs[data*2+1]; + + data = mt32asgs[data*2]; + } + else if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) + { + CreateNewEvent (ctx, time); + ctx->current->status = 0xB0 | (status&0xF); + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + } + /* Drum track handling */ + else if ((status >> 4) == 0xC && (status&0xF) == 9 && + (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127DRUM || ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127)) + { + CreateNewEvent (ctx, time); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + CreateNewEvent(ctx, time); + ctx->current->status = status; + + ctx->current->data[0] = data; + + if (size == 1) + return (1); + + ctx->current->data[1] = read1(ctx); + + if (size == 2) + return (2); + + /* XMI Note On handling */ + prev = ctx->current; + i = GetVLQ(ctx, &delta); + CreateNewEvent(ctx, time + delta * 3); + + ctx->current->status = status; + ctx->current->data[0] = data; + ctx->current->data[1] = 0; + ctx->current = prev; + + return (i + 2); +} + +/* Simple routine to convert system messages */ +static int32_t ConvertSystemMessage(struct xmi_ctx *ctx, const int32_t time, + const uint8_t status) { + int32_t i = 0; + + CreateNewEvent(ctx, time); + ctx->current->status = status; + + /* Handling of Meta events */ + if (status == 0xFF) { + ctx->current->data[0] = read1(ctx); + i++; + } + + i += GetVLQ(ctx, &ctx->current->len); + + if (!ctx->current->len) + return (i); + + ctx->current->buffer = malloc(sizeof(uint8_t)*ctx->current->len); + copy(ctx, (char *) ctx->current->buffer, ctx->current->len); + + return (i + ctx->current->len); +} + +/* XMIDI and Midi to List + * Returns XMIDI PPQN */ +static int32_t ConvertFiletoList(struct xmi_ctx *ctx) { + int32_t time = 0; + uint32_t data; + int32_t end = 0; + int32_t tempo = 500000; + int32_t tempo_set = 0; + uint32_t status = 0; + uint32_t file_size = getsrcsize(ctx); + + /* Set Drum track to correct setting if required */ + if (ctx->convert_type == XMIDI_CONVERT_MT32_TO_GS127) { + CreateNewEvent(ctx, 0); + ctx->current->status = 0xB9; + ctx->current->data[0] = 0; + ctx->current->data[1] = 127; + } + + while (!end && getsrcpos(ctx) < file_size) { + GetVLQ2(ctx, &data); + time += data * 3; + + status = read1(ctx); + + switch (status >> 4) { + case MIDI_STATUS_NOTE_ON: + ConvertEvent(ctx, time, status, 3); + break; + + /* 2 byte data */ + case MIDI_STATUS_NOTE_OFF: + case MIDI_STATUS_AFTERTOUCH: + case MIDI_STATUS_CONTROLLER: + case MIDI_STATUS_PITCH_WHEEL: + ConvertEvent(ctx, time, status, 2); + break; + + /* 1 byte data */ + case MIDI_STATUS_PROG_CHANGE: + case MIDI_STATUS_PRESSURE: + ConvertEvent(ctx, time, status, 1); + break; + + case MIDI_STATUS_SYSEX: + if (status == 0xFF) { + int32_t pos = getsrcpos(ctx); + uint32_t dat = read1(ctx); + + if (dat == 0x2F) /* End */ + end = 1; + else if (dat == 0x51 && !tempo_set) /* Tempo. Need it for PPQN */ + { + skipsrc(ctx, 1); + tempo = read1(ctx) << 16; + tempo += read1(ctx) << 8; + tempo += read1(ctx); + tempo *= 3; + tempo_set = 1; + } else if (dat == 0x51 && tempo_set) /* Skip any other tempo changes */ + { + GetVLQ(ctx, &dat); + skipsrc(ctx, dat); + break; + } + + seeksrc(ctx, pos); + } + ConvertSystemMessage(ctx, time, status); + break; + + default: + break; + } + } + return ((tempo * 3) / 25000); +} + +/* Converts and event list to a MTrk + * Returns bytes of the array + * buf can be NULL */ +static uint32_t ConvertListToMTrk(struct xmi_ctx *ctx, midi_event *mlist) { + int32_t time = 0; + midi_event *event; + uint32_t delta; + uint8_t last_status = 0; + uint32_t i = 8; + uint32_t j; + uint32_t size_pos, cur_pos; + int end = 0; + + write1(ctx, 'M'); + write1(ctx, 'T'); + write1(ctx, 'r'); + write1(ctx, 'k'); + + size_pos = getdstpos(ctx); + skipdst(ctx, 4); + + for (event = mlist; event && !end; event = event->next) { + delta = (event->time - time); + time = event->time; + + i += PutVLQ(ctx, delta); + + if ((event->status != last_status) || (event->status >= 0xF0)) { + write1(ctx, event->status); + i++; + } + + last_status = event->status; + + switch (event->status >> 4) { + /* 2 bytes data + * Note off, Note on, Aftertouch, Controller and Pitch Wheel */ + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xE: + write1(ctx, event->data[0]); + write1(ctx, event->data[1]); + i += 2; + break; + + /* 1 bytes data + * Program Change and Channel Pressure */ + case 0xC: + case 0xD: + write1(ctx, event->data[0]); + i++; + break; + + /* Variable length + * SysEx */ + case 0xF: + if (event->status == 0xFF) { + if (event->data[0] == 0x2f) + end = 1; + write1(ctx, event->data[0]); + i++; + } + i += PutVLQ(ctx, event->len); + if (event->len) { + for (j = 0; j < event->len; j++) { + write1(ctx, event->buffer[j]); + i++; + } + } + break; + + /* Never occur */ + default: + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(unrecognized event)", 0); + break; + } + } + + cur_pos = getdstpos(ctx); + seekdst(ctx, size_pos); + write4(ctx, i - 8); + seekdst(ctx, cur_pos); + + return (i); +} + +/* Assumes correct xmidi */ +static uint32_t ExtractTracksFromXmi(struct xmi_ctx *ctx) { + uint32_t num = 0; + signed short ppqn; + uint32_t len = 0; + int32_t begin; + char buf[32]; + + while (getsrcpos(ctx) < getsrcsize(ctx) && num != ctx->info.tracks) { + /* Read first 4 bytes of name */ + copy(ctx, buf, 4); + len = read4(ctx); + + /* Skip the FORM entries */ + if (!memcmp(buf, "FORM", 4)) { + skipsrc(ctx, 4); + copy(ctx, buf, 4); + len = read4(ctx); + } + + if (memcmp(buf, "EVNT", 4)) { + skipsrc(ctx, (len + 1) & ~1); + continue; + } + + ctx->list = NULL; + begin = getsrcpos(ctx); + + /* Convert it */ + if (!(ppqn = ConvertFiletoList(ctx))) { + WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, NULL, 0); + break; + } + ctx->timing[num] = ppqn; + ctx->events[num] = ctx->list; + + /* Increment Counter */ + num++; + + /* go to start of next track */ + seeksrc(ctx, begin + ((len + 1) & ~1)); + } + + /* Return how many were converted */ + return (num); +} + +static int ParseXMI(struct xmi_ctx *ctx) { + uint32_t i; + uint32_t start; + uint32_t len; + uint32_t chunk_len; + uint32_t file_size; + char buf[32]; + + file_size = getsrcsize(ctx); + if (getsrcpos(ctx) + 8 > file_size) { +badfile: WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_CORUPT, "(too short)", 0); + return (-1); + } + + /* Read first 4 bytes of header */ + copy(ctx, buf, 4); + + /* Could be XMIDI */ + if (!memcmp(buf, "FORM", 4)) { + /* Read length of */ + len = read4(ctx); + + start = getsrcpos(ctx); + if (start + 4 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + copy(ctx, buf, 4); + + /* XDIRless XMIDI, we can handle them here. */ + if (!memcmp(buf, "XMID", 4)) { + WM_ERROR_NEW("Warning: XMIDI without XDIR"); + ctx->info.tracks = 1; + } + /* Not an XMIDI that we recognise */ + else if (memcmp(buf, "XDIR", 4)) { + goto badfile; + } + else { /* Seems Valid */ + ctx->info.tracks = 0; + + for (i = 4; i < len; i++) { + /* check too short files */ + if (getsrcpos(ctx) + 10 > file_size) + break; + + /* Read 4 bytes of type */ + copy(ctx, buf, 4); + + /* Read length of chunk */ + chunk_len = read4(ctx); + + /* Add eight bytes */ + i += 8; + + if (memcmp(buf, "INFO", 4)) { + /* Must align */ + skipsrc(ctx, (chunk_len + 1) & ~1); + i += (chunk_len + 1) & ~1; + continue; + } + + /* Must be at least 2 bytes long */ + if (chunk_len < 2) + break; + + ctx->info.tracks = read2(ctx); + break; + } + + /* Didn't get to fill the header */ + if (ctx->info.tracks == 0) { + goto badfile; + } + + /* Ok now to start part 2 + * Goto the right place */ + seeksrc(ctx, start + ((len + 1) & ~1)); + if (getsrcpos(ctx) + 12 > file_size) + goto badfile; + + /* Read 4 bytes of type */ + copy(ctx, buf, 4); + + if (memcmp(buf, "CAT ", 4)) { + WM_ERROR_NEW("XMI error: expected \"CAT \", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]); + return (-1); + } + + /* Now read length of this track */ + read4(ctx); + + /* Read 4 bytes of type */ + copy(ctx, buf, 4); + + if (memcmp(buf, "XMID", 4)) { + WM_ERROR_NEW("XMI error: expected \"XMID\", found \"%c%c%c%c\".", + buf[0], buf[1], buf[2], buf[3]); + return (-1); + } + + /* Valid XMID */ + ctx->datastart = getsrcpos(ctx); + return (0); + } + } + + return (-1); +} + +static int ExtractTracks(struct xmi_ctx *ctx) { + uint32_t i; + + ctx->events = calloc(ctx->info.tracks, sizeof(midi_event*)); + ctx->timing = calloc(ctx->info.tracks, sizeof(int16_t)); + /* type-2 for multi-tracks, type-0 otherwise */ + ctx->info.type = (ctx->info.tracks > 1)? 2 : 0; + + seeksrc(ctx, ctx->datastart); + i = ExtractTracksFromXmi(ctx); + + if (i != ctx->info.tracks) { + WM_ERROR_NEW("XMI error: extracted only %u out of %u tracks from XMIDI", + ctx->info.tracks, i); + return (-1); + } + + return (0); +}