mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-09-23 03:52:12 -04:00
switch to OpenAl for sound mixing (#967)
* switch to OpeanAl for sound mixing * Rewrote Load_SNDFile() to use float format. Made libsndfile mandatory. * update main.yml workflow * implement OpeanAl music streaming * remove i_sdlmusic.c and SDL_Mixer dependecy * enable mpeg support in libsndfile * require SndFile 1.1.0 * check SF_FORMAT_MPEG_LAYER_III symbol * use AL_GAIN for opl_gain
This commit is contained in:
parent
28a88c41dc
commit
1a278d6105
14
.github/workflows/main.yml
vendored
14
.github/workflows/main.yml
vendored
@ -18,7 +18,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install libfluidsynth-dev libsdl2-dev libsdl2-mixer-dev libsdl2-net-dev ninja-build
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install \
|
||||
libsdl2-dev \
|
||||
libsdl2-net-dev \
|
||||
libopenal-dev \
|
||||
libsndfile1-dev \
|
||||
libfluidsynth-dev \
|
||||
ninja-build
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@ -67,8 +75,10 @@ jobs:
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-SDL2_mixer
|
||||
mingw-w64-x86_64-SDL2_net
|
||||
mingw-w64-x86_64-openal
|
||||
mingw-w64-x86_64-libsndfile
|
||||
mingw-w64-x86_64-fluidsynth
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
4
.github/workflows/win_msvc.yml
vendored
4
.github/workflows/win_msvc.yml
vendored
@ -5,8 +5,8 @@ on:
|
||||
branches: [ master ]
|
||||
tags: ['*']
|
||||
paths-ignore: ['**.md']
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
# pull_request:
|
||||
# branches: [ master ]
|
||||
|
||||
env:
|
||||
VCPKG_ROOT: C:\vcpkg
|
||||
|
@ -1,6 +1,7 @@
|
||||
include(CheckLibraryExists)
|
||||
include(CheckIncludeFile)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckCSourceCompiles)
|
||||
include(ExternalProject)
|
||||
|
||||
# Adds the cmake directory to the CMake include path.
|
||||
@ -72,15 +73,20 @@ option(CMAKE_FIND_PACKAGE_PREFER_CONFIG
|
||||
|
||||
# Library requirements.
|
||||
find_package(SDL2 2.0.7 REQUIRED)
|
||||
find_package(SDL2_mixer 2.0.2 REQUIRED)
|
||||
find_package(SDL2_net REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(SndFile 1.1.0 REQUIRED)
|
||||
|
||||
find_package(FluidSynth 2.2.0)
|
||||
find_package(SndFile 1.0.28)
|
||||
|
||||
# Python 3
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
|
||||
check_c_source_compiles(
|
||||
"#include <sndfile.h>
|
||||
int main(void) {return SF_FORMAT_MPEG_LAYER_III;}"
|
||||
HAVE_SNDFILE_MPEG)
|
||||
|
||||
configure_file(config.h.in config.h)
|
||||
|
||||
if(WIN32)
|
||||
|
@ -1,123 +0,0 @@
|
||||
# FindSDL2_mixer.cmake
|
||||
#
|
||||
# Copyright (c) 2018, Alex Mayfield <alexmax2742@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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.
|
||||
# * Neither the name of the <organization> 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 COPYRIGHT HOLDERS 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 <COPYRIGHT HOLDER> 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.
|
||||
#
|
||||
# Currently works with the following generators:
|
||||
# - Unix Makefiles (Linux, MSYS2, Linux MinGW)
|
||||
# - Ninja (Linux, MSYS2, Linux MinGW)
|
||||
# - Visual Studio
|
||||
|
||||
# Cache variable that allows you to point CMake at a directory containing
|
||||
# an extracted development library.
|
||||
set(SDL2_MIXER_DIR "${SDL2_MIXER_DIR}" CACHE PATH "Location of SDL2_mixer library directory")
|
||||
|
||||
# Use pkg-config to find library locations in *NIX environments.
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_search_module(PC_SDL2_MIXER QUIET SDL2_mixer)
|
||||
endif()
|
||||
|
||||
# Find the include directory.
|
||||
if(CMAKE_SIZEOF_VOID_P STREQUAL 8)
|
||||
find_path(SDL2_MIXER_INCLUDE_DIR "SDL_mixer.h"
|
||||
HINTS
|
||||
"${SDL2_MIXER_DIR}/include"
|
||||
"${SDL2_MIXER_DIR}/include/SDL2"
|
||||
"${SDL2_MIXER_DIR}/x86_64-w64-mingw32/include/SDL2"
|
||||
${PC_SDL2_MIXER_INCLUDE_DIRS})
|
||||
else()
|
||||
find_path(SDL2_MIXER_INCLUDE_DIR "SDL_mixer.h"
|
||||
HINTS
|
||||
"${SDL2_MIXER_DIR}/include"
|
||||
"${SDL2_MIXER_DIR}/include/SDL2"
|
||||
"${SDL2_MIXER_DIR}/i686-w64-mingw32/include/SDL2"
|
||||
${PC_SDL2_MIXER_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
# Find the version. Taken and modified from CMake's FindSDL.cmake.
|
||||
if(SDL2_MIXER_INCLUDE_DIR AND EXISTS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+[0-9]+$")
|
||||
file(STRINGS "${SDL2_MIXER_INCLUDE_DIR}/SDL_mixer.h" SDL2_MIXER_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+[0-9]+$")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MAJOR "${SDL2_MIXER_VERSION_MAJOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_MINOR "${SDL2_MIXER_VERSION_MINOR_LINE}")
|
||||
string(REGEX REPLACE "^#define[ \t]+SDL_MIXER_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_MIXER_VERSION_PATCH "${SDL2_MIXER_VERSION_PATCH_LINE}")
|
||||
set(SDL2_MIXER_VERSION "${SDL2_MIXER_VERSION_MAJOR}.${SDL2_MIXER_VERSION_MINOR}.${SDL2_MIXER_VERSION_PATCH}")
|
||||
unset(SDL2_MIXER_VERSION_MAJOR_LINE)
|
||||
unset(SDL2_MIXER_VERSION_MINOR_LINE)
|
||||
unset(SDL2_MIXER_VERSION_PATCH_LINE)
|
||||
unset(SDL2_MIXER_VERSION_MAJOR)
|
||||
unset(SDL2_MIXER_VERSION_MINOR)
|
||||
unset(SDL2_MIXER_VERSION_PATCH)
|
||||
endif()
|
||||
|
||||
# Find the library.
|
||||
if(CMAKE_SIZEOF_VOID_P STREQUAL 8)
|
||||
find_library(SDL2_MIXER_LIBRARY "SDL2_mixer"
|
||||
HINTS
|
||||
"${SDL2_MIXER_DIR}/lib"
|
||||
"${SDL2_MIXER_DIR}/lib/x64"
|
||||
"${SDL2_MIXER_DIR}/x86_64-w64-mingw32/lib"
|
||||
${PC_SDL2_MIXER_LIBRARY_DIRS})
|
||||
else()
|
||||
find_library(SDL2_MIXER_LIBRARY "SDL2_mixer"
|
||||
HINTS
|
||||
"${SDL2_MIXER_DIR}/lib"
|
||||
"${SDL2_MIXER_DIR}/lib/x86"
|
||||
"${SDL2_MIXER_DIR}/i686-w64-mingw32/lib"
|
||||
${PC_SDL2_MIXER_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SDL2_mixer
|
||||
FOUND_VAR SDL2_MIXER_FOUND
|
||||
REQUIRED_VARS SDL2_MIXER_INCLUDE_DIR SDL2_MIXER_LIBRARY
|
||||
VERSION_VAR SDL2_MIXER_VERSION
|
||||
)
|
||||
|
||||
if(SDL2_MIXER_FOUND)
|
||||
# Imported target.
|
||||
add_library(SDL2_mixer::SDL2_mixer UNKNOWN IMPORTED)
|
||||
set_target_properties(SDL2_mixer::SDL2_mixer PROPERTIES
|
||||
INTERFACE_COMPILE_OPTIONS "${PC_SDL2_MIXER_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${SDL2_MIXER_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES SDL2::SDL2
|
||||
IMPORTED_LOCATION "${SDL2_MIXER_LIBRARY}")
|
||||
|
||||
if(WIN32)
|
||||
# On Windows, we need to figure out the location of our library files
|
||||
# so we can copy and package them.
|
||||
get_filename_component(SDL2_MIXER_DLL_DIR "${SDL2_MIXER_LIBRARY}" DIRECTORY)
|
||||
if(EXISTS "${SDL2_MIXER_DLL_DIR}/SDL2_mixer.dll")
|
||||
set(SDL2_MIXER_DLL_DIR "${SDL2_MIXER_DLL_DIR}" CACHE INTERNAL "")
|
||||
elseif(EXISTS "${SDL2_MIXER_DLL_DIR}/../bin/SDL2_mixer.dll")
|
||||
get_filename_component(SDL2_MIXER_DLL_DIR "${SDL2_MIXER_DLL_DIR}/../bin" REALPATH)
|
||||
set(SDL2_MIXER_DLL_DIR "${SDL2_MIXER_DLL_DIR}" CACHE INTERNAL "")
|
||||
else()
|
||||
message(ERROR "Can't find SDL2_mixer dynamic library directory.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
@ -2,8 +2,3 @@ set(VCPKG_TARGET_ARCHITECTURE x64)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
|
||||
if(${PORT} MATCHES "libsamplerate")
|
||||
set(VCPKG_CXX_FLAGS "/fp:fast")
|
||||
set(VCPKG_C_FLAGS "/fp:fast")
|
||||
endif()
|
||||
|
@ -2,8 +2,3 @@ set(VCPKG_TARGET_ARCHITECTURE x86)
|
||||
set(VCPKG_CRT_LINKAGE dynamic)
|
||||
set(VCPKG_LIBRARY_LINKAGE dynamic)
|
||||
set(VCPKG_BUILD_TYPE release)
|
||||
|
||||
if(${PORT} MATCHES "libsamplerate")
|
||||
set(VCPKG_CXX_FLAGS "/fp:fast")
|
||||
set(VCPKG_C_FLAGS "/fp:fast")
|
||||
endif()
|
||||
|
@ -8,3 +8,4 @@
|
||||
#cmakedefine01 HAVE_DECL_STRCASECMP
|
||||
#cmakedefine01 HAVE_DECL_STRNCASECMP
|
||||
#cmakedefine WOOFDATADIR "@WOOFDATADIR@"
|
||||
#cmakedefine01 HAVE_SNDFILE_MPEG
|
||||
|
@ -12,4 +12,4 @@ target_woof_settings(opl)
|
||||
target_include_directories(opl
|
||||
INTERFACE "."
|
||||
PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../" "../src/")
|
||||
target_link_libraries(opl SDL2_mixer::SDL2_mixer)
|
||||
target_link_libraries(opl SDL2::SDL2)
|
||||
|
152
opl/opl_sdl.c
152
opl/opl_sdl.c
@ -21,11 +21,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
#include "i_oalmusic.h"
|
||||
|
||||
#include "opl3.h"
|
||||
|
||||
@ -75,10 +74,6 @@ static uint64_t pause_offset;
|
||||
static opl3_chip opl_chip;
|
||||
static int opl_opl3mode;
|
||||
|
||||
// Temporary mixing buffer used by the mixing callback.
|
||||
|
||||
static uint8_t *mix_buffer = NULL;
|
||||
|
||||
// Register number that was written.
|
||||
|
||||
static int register_num = 0;
|
||||
@ -88,19 +83,7 @@ static int register_num = 0;
|
||||
static opl_timer_t timer1 = { 12500, 0, 0, 0 };
|
||||
static opl_timer_t timer2 = { 3125, 0, 0, 0 };
|
||||
|
||||
// SDL parameters.
|
||||
|
||||
static int sdl_was_initialized = 0;
|
||||
static int mixing_freq, mixing_channels;
|
||||
static Uint16 mixing_format;
|
||||
|
||||
static int SDLIsInitialized(void)
|
||||
{
|
||||
int freq, channels;
|
||||
Uint16 format;
|
||||
|
||||
return Mix_QuerySpec(&freq, &format, &channels);
|
||||
}
|
||||
|
||||
// Advance time by the specified number of samples, invoking any
|
||||
// callback functions as appropriate.
|
||||
@ -155,59 +138,19 @@ static void AdvanceTime(unsigned int nsamples)
|
||||
SDL_UnlockMutex(callback_queue_mutex);
|
||||
}
|
||||
|
||||
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
|
||||
#define OPL_SHORT SDL_SwapLE16
|
||||
#else
|
||||
#define OPL_SHORT SDL_SwapBE16
|
||||
#endif
|
||||
|
||||
int opl_gain = 200;
|
||||
|
||||
static void MixAudioFormat(Uint8 *dst, const Uint8 *src, Uint32 len)
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
|
||||
while (len--)
|
||||
{
|
||||
src1 = OPL_SHORT(*(Sint16 *)src);
|
||||
src2 = OPL_SHORT(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 * opl_gain / 100 + src2;
|
||||
if (dst_sample > SHRT_MAX)
|
||||
{
|
||||
dst_sample = SHRT_MAX;
|
||||
}
|
||||
else if (dst_sample < SHRT_MIN)
|
||||
{
|
||||
dst_sample = SHRT_MIN;
|
||||
}
|
||||
*(Sint16 *)dst = OPL_SHORT(dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the OPL emulator code to fill the specified buffer.
|
||||
|
||||
static void FillBuffer(uint8_t *buffer, unsigned int nsamples)
|
||||
{
|
||||
// This seems like a reasonable assumption. mix_buffer is
|
||||
// 1 second long, which should always be much longer than the
|
||||
// SDL mix buffer.
|
||||
assert(nsamples < mixing_freq);
|
||||
|
||||
// OPL output is generated into temporary buffer and then mixed
|
||||
// (to avoid overflows etc.)
|
||||
OPL3_GenerateStream(&opl_chip, (Bit16s *) mix_buffer, nsamples);
|
||||
MixAudioFormat(buffer, mix_buffer, nsamples * 2);
|
||||
OPL3_GenerateStream(&opl_chip, (Bit16s *) buffer, nsamples);
|
||||
}
|
||||
|
||||
// Callback function to fill a new sound buffer:
|
||||
|
||||
static void OPL_Mix_Callback(void *udata, Uint8 *stream, int len)
|
||||
static int OPL_Callback(byte *stream, int len)
|
||||
{
|
||||
unsigned int filled, buffer_samples;
|
||||
Uint8 *buffer = (Uint8*)stream;
|
||||
uint8_t *buffer = stream;
|
||||
|
||||
// Repeatedly call the OPL emulator update function until the buffer is
|
||||
// full.
|
||||
@ -253,20 +196,14 @@ static void OPL_Mix_Callback(void *udata, Uint8 *stream, int len)
|
||||
|
||||
AdvanceTime(nsamples);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void OPL_SDL_Shutdown(void)
|
||||
{
|
||||
Mix_HookMusic(NULL, NULL);
|
||||
I_OAL_HookMusic(NULL);
|
||||
|
||||
if (sdl_was_initialized)
|
||||
{
|
||||
Mix_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
OPL_Queue_Destroy(callback_queue);
|
||||
free(mix_buffer);
|
||||
sdl_was_initialized = 0;
|
||||
}
|
||||
OPL_Queue_Destroy(callback_queue);
|
||||
|
||||
/*
|
||||
if (opl_chip != NULL)
|
||||
@ -289,63 +226,10 @@ static void OPL_SDL_Shutdown(void)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int GetSliceSize(void)
|
||||
{
|
||||
int limit;
|
||||
int n;
|
||||
|
||||
limit = (opl_sample_rate * MAX_SOUND_SLICE_TIME) / 1000;
|
||||
|
||||
// Try all powers of two, not exceeding the limit.
|
||||
|
||||
for (n=0;; ++n)
|
||||
{
|
||||
// 2^n <= limit < 2^n+1 ?
|
||||
|
||||
if ((1 << (n + 1)) > limit)
|
||||
{
|
||||
return (1 << n);
|
||||
}
|
||||
}
|
||||
|
||||
// Should never happen?
|
||||
|
||||
return 1024;
|
||||
}
|
||||
int opl_gain = 200;
|
||||
|
||||
static int OPL_SDL_Init(unsigned int port_base)
|
||||
{
|
||||
// Check if SDL_mixer has been opened already
|
||||
// If not, we must initialize it now
|
||||
|
||||
if (!SDLIsInitialized())
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to set up sound.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Mix_OpenAudioDevice(opl_sample_rate, AUDIO_S16SYS, 2, GetSliceSize(), NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_PauseAudio(0);
|
||||
|
||||
// When this module shuts down, it has the responsibility to
|
||||
// shut down SDL.
|
||||
|
||||
sdl_was_initialized = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sdl_was_initialized = 0;
|
||||
}
|
||||
|
||||
opl_sdl_paused = 0;
|
||||
pause_offset = 0;
|
||||
|
||||
@ -356,22 +240,9 @@ static int OPL_SDL_Init(unsigned int port_base)
|
||||
|
||||
// Get the mixer frequency, format and number of channels.
|
||||
|
||||
Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels);
|
||||
|
||||
// Only supports AUDIO_S16SYS
|
||||
|
||||
if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"OPL_SDL only supports native signed 16-bit LSB, "
|
||||
"stereo format!\n");
|
||||
|
||||
OPL_SDL_Shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mix buffer: four bytes per sample (16 bits * 2 channels):
|
||||
mix_buffer = malloc(mixing_freq * 4);
|
||||
mixing_channels = 2;
|
||||
mixing_freq = opl_sample_rate;
|
||||
|
||||
// Create the emulator structure:
|
||||
|
||||
@ -381,7 +252,8 @@ static int OPL_SDL_Init(unsigned int port_base)
|
||||
callback_mutex = SDL_CreateMutex();
|
||||
callback_queue_mutex = SDL_CreateMutex();
|
||||
|
||||
Mix_HookMusic(OPL_Mix_Callback, NULL);
|
||||
I_OAL_HookMusic(OPL_Callback);
|
||||
I_OAL_SetGain((float)opl_gain / 100.0f);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -35,8 +35,8 @@ set(WOOF_SOURCES
|
||||
i_flmusic.c
|
||||
i_glob.c i_glob.h
|
||||
i_main.c
|
||||
i_oalmusic.c i_oalmusic.h
|
||||
i_oplmusic.c
|
||||
i_sdlmusic.c
|
||||
i_sndfile.c i_sndfile.h
|
||||
i_sound.c i_sound.h
|
||||
i_system.c i_system.h
|
||||
@ -190,13 +190,13 @@ if (FluidSynth_FOUND)
|
||||
target_compile_definitions(woof PRIVATE HAVE_FLUIDSYNTH)
|
||||
endif()
|
||||
|
||||
if (SndFile_FOUND)
|
||||
list(APPEND WOOF_LIBRARIES SndFile::sndfile)
|
||||
target_compile_definitions(woof PRIVATE HAVE_SNDFILE)
|
||||
endif()
|
||||
|
||||
target_link_libraries(woof PRIVATE ${WOOF_LIBRARIES}
|
||||
SDL2::SDL2 SDL2_mixer::SDL2_mixer SDL2_net::SDL2_net opl textscreen)
|
||||
SDL2::SDL2
|
||||
SDL2_net::SDL2_net
|
||||
OpenAL::OpenAL
|
||||
SndFile::sndfile
|
||||
opl
|
||||
textscreen)
|
||||
|
||||
# Optional features.
|
||||
#
|
||||
@ -237,8 +237,12 @@ add_executable(woof-setup WIN32 ${SETUP_SOURCES})
|
||||
target_woof_settings(woof-setup)
|
||||
|
||||
target_include_directories(woof-setup PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/../")
|
||||
target_link_libraries(woof-setup PRIVATE "$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>"
|
||||
SDL2::SDL2 SDL2_net::SDL2_net textscreen setup)
|
||||
target_link_libraries(woof-setup PRIVATE
|
||||
"$<TARGET_NAME_IF_EXISTS:SDL2::SDL2main>"
|
||||
SDL2::SDL2
|
||||
SDL2_net::SDL2_net
|
||||
textscreen
|
||||
setup)
|
||||
|
||||
if(MSVC)
|
||||
target_link_options(woof-setup PRIVATE "/MANIFEST:NO")
|
||||
|
@ -26,7 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
#include "i_oalmusic.h"
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "i_system.h"
|
||||
@ -52,7 +52,7 @@ static fluid_player_t *player = NULL;
|
||||
static char **soundfonts;
|
||||
static int soundfonts_num;
|
||||
|
||||
static void FL_Mix_Callback(void *udata, Uint8 *stream, int len)
|
||||
static int FL_Callback(byte *stream, int len)
|
||||
{
|
||||
int result;
|
||||
|
||||
@ -62,6 +62,8 @@ static void FL_Mix_Callback(void *udata, Uint8 *stream, int len)
|
||||
{
|
||||
fprintf(stderr, "Error generating FluidSynth audio\n");
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Load SNDFONT lump
|
||||
@ -356,7 +358,7 @@ static void *I_FL_RegisterSong(void *data, int len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Mix_HookMusic(FL_Mix_Callback, NULL);
|
||||
I_OAL_HookMusic(FL_Callback);
|
||||
|
||||
return (void *)1;
|
||||
}
|
||||
@ -368,7 +370,7 @@ static void I_FL_UnRegisterSong(void *handle)
|
||||
fluid_synth_program_reset(synth);
|
||||
fluid_synth_system_reset(synth);
|
||||
|
||||
Mix_HookMusic(NULL, NULL);
|
||||
I_OAL_HookMusic(NULL);
|
||||
|
||||
delete_fluid_player(player);
|
||||
player = NULL;
|
||||
|
320
src/i_oalmusic.c
Normal file
320
src/i_oalmusic.c
Normal file
@ -0,0 +1,320 @@
|
||||
//
|
||||
// Copyright(C) 2023 Roman Fomin
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 for more details.
|
||||
//
|
||||
// DESCRIPTION:
|
||||
//
|
||||
|
||||
#include "i_oalmusic.h"
|
||||
|
||||
#include <AL/al.h>
|
||||
#include <AL/alext.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "i_sndfile.h"
|
||||
#include "i_sound.h"
|
||||
|
||||
// Define the number of buffers and buffer size (in milliseconds) to use. 4
|
||||
// buffers with 8192 samples each gives a nice per-chunk size, and lets the
|
||||
// queue last for almost one second at 44.1khz.
|
||||
#define NUM_BUFFERS 4
|
||||
#define BUFFER_SAMPLES 8192
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// These are the buffers and source to play out through OpenAL with
|
||||
ALuint buffers[NUM_BUFFERS];
|
||||
ALuint source;
|
||||
|
||||
byte *data;
|
||||
|
||||
// The format of the output stream
|
||||
ALenum format;
|
||||
ALsizei freq;
|
||||
} stream_player_t;
|
||||
|
||||
static stream_player_t player;
|
||||
|
||||
static SDL_Thread *player_thread_handle;
|
||||
static int player_thread_running;
|
||||
|
||||
static callback_func_t callback;
|
||||
|
||||
static boolean UpdatePlayer(void)
|
||||
{
|
||||
ALint processed, state;
|
||||
|
||||
// Get relevant source info
|
||||
alGetSourcei(player.source, AL_SOURCE_STATE, &state);
|
||||
alGetSourcei(player.source, AL_BUFFERS_PROCESSED, &processed);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "UpdatePlayer: Error checking source state\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unqueue and handle each processed buffer
|
||||
while (processed > 0)
|
||||
{
|
||||
ALuint bufid;
|
||||
ALsizei size;
|
||||
|
||||
alSourceUnqueueBuffers(player.source, 1, &bufid);
|
||||
processed--;
|
||||
|
||||
// Read the next chunk of data, refill the buffer, and queue it back on
|
||||
// the source.
|
||||
if (callback)
|
||||
{
|
||||
size = callback(player.data, BUFFER_SAMPLES);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = I_SND_FillStream(player.data, BUFFER_SAMPLES);
|
||||
}
|
||||
if (size > 0)
|
||||
{
|
||||
alBufferData(bufid, player.format, player.data, size, player.freq);
|
||||
alSourceQueueBuffers(player.source, 1, &bufid);
|
||||
}
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "UpdatePlayer: Error buffering data\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the source hasn't underrun
|
||||
if (state != AL_PLAYING && state != AL_PAUSED)
|
||||
{
|
||||
ALint queued;
|
||||
|
||||
// If no buffers are queued, playback is finished
|
||||
alGetSourcei(player.source, AL_BUFFERS_QUEUED, &queued);
|
||||
if (queued == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourcePlay(player.source);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "UpdatePlayer: Error restarting playback\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean StartPlayer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
player.data = malloc(BUFFER_SAMPLES);
|
||||
|
||||
// Rewind the source position and clear the buffer queue.
|
||||
alSourceRewind(player.source);
|
||||
alSourcei(player.source, AL_BUFFER, 0);
|
||||
|
||||
// Fill the buffer queue
|
||||
for (i = 0; i < NUM_BUFFERS; i++)
|
||||
{
|
||||
ALsizei size;
|
||||
|
||||
// Get some data to give it to the buffer
|
||||
if (callback)
|
||||
{
|
||||
size = callback(player.data, BUFFER_SAMPLES);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = I_SND_FillStream(player.data, BUFFER_SAMPLES);
|
||||
}
|
||||
|
||||
if (size < 1)
|
||||
break;
|
||||
|
||||
alBufferData(player.buffers[i], player.format, player.data,
|
||||
size, player.freq);
|
||||
}
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "StartPlayer: Error buffering for playback.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(player.source, i, player.buffers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int PlayerThread(void *unused)
|
||||
{
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
while (player_thread_running && UpdatePlayer())
|
||||
{
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static boolean I_OAL_InitMusic(int device)
|
||||
{
|
||||
alGenBuffers(NUM_BUFFERS, player.buffers);
|
||||
alGenSources(1, &player.source);
|
||||
|
||||
// Set parameters so mono sources play out the front-center speaker and
|
||||
// won't distance attenuate.
|
||||
alSource3i(player.source, AL_POSITION, 0, 0, -1);
|
||||
alSourcei(player.source, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
alSourcei(player.source, AL_ROLLOFF_FACTOR, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void I_OAL_SetMusicVolume(int volume)
|
||||
{
|
||||
alSourcef(player.source, AL_GAIN, (ALfloat)volume / 15.0f);
|
||||
}
|
||||
|
||||
static void I_OAL_PauseSong(void *handle)
|
||||
{
|
||||
alSourcePause(player.source);
|
||||
}
|
||||
|
||||
static void I_OAL_ResumeSong(void *handle)
|
||||
{
|
||||
alSourcePlay(player.source);
|
||||
}
|
||||
|
||||
static void I_OAL_PlaySong(void *handle, boolean looping)
|
||||
{
|
||||
if (!callback)
|
||||
{
|
||||
I_SND_SetLooping(looping);
|
||||
}
|
||||
|
||||
alSourcePlay(player.source);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "I_OAL_PlaySong: Error starting playback.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
player_thread_running = true;
|
||||
player_thread_handle = SDL_CreateThread(PlayerThread, NULL, NULL);
|
||||
}
|
||||
|
||||
static void I_OAL_StopSong(void *handle)
|
||||
{
|
||||
alSourceStop(player.source);
|
||||
}
|
||||
|
||||
static void I_OAL_UnRegisterSong(void *handle)
|
||||
{
|
||||
if (player_thread_running)
|
||||
{
|
||||
player_thread_running = false;
|
||||
SDL_WaitThread(player_thread_handle, NULL);
|
||||
}
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
I_SND_CloseStream();
|
||||
}
|
||||
|
||||
if (player.data)
|
||||
{
|
||||
free(player.data);
|
||||
player.data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void I_OAL_ShutdownMusic(void)
|
||||
{
|
||||
I_OAL_StopSong(NULL);
|
||||
I_OAL_UnRegisterSong(NULL);
|
||||
|
||||
alDeleteSources(1, &player.source);
|
||||
alDeleteBuffers(NUM_BUFFERS, player.buffers);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "I_OAL_ShutdownMusic: Failed to delete object IDs.\n");
|
||||
}
|
||||
|
||||
memset(&player, 0, sizeof(stream_player_t));
|
||||
}
|
||||
|
||||
// Prebuffers some audio from the file, and starts playing the source.
|
||||
|
||||
static void *I_OAL_RegisterSong(void *data, int len)
|
||||
{
|
||||
if (I_SND_OpenStream(data, len, &player.format, &player.freq) == false)
|
||||
return NULL;
|
||||
|
||||
StartPlayer();
|
||||
|
||||
return (void *)1;
|
||||
}
|
||||
|
||||
static int I_OAL_DeviceList(const char *devices[], int size, int *current_device)
|
||||
{
|
||||
*current_device = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void I_OAL_SetGain(float gain)
|
||||
{
|
||||
alSourcef(player.source, AL_MAX_GAIN, 10.0f);
|
||||
alSourcef(player.source, AL_GAIN, (ALfloat)gain);
|
||||
}
|
||||
|
||||
void I_OAL_HookMusic(callback_func_t callback_func)
|
||||
{
|
||||
if (callback_func)
|
||||
{
|
||||
callback = callback_func;
|
||||
|
||||
player.format = AL_FORMAT_STEREO16;
|
||||
player.freq = snd_samplerate;
|
||||
|
||||
StartPlayer();
|
||||
I_OAL_PlaySong(NULL, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback = NULL;
|
||||
|
||||
I_OAL_UnRegisterSong(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
music_module_t music_oal_module =
|
||||
{
|
||||
I_OAL_InitMusic,
|
||||
I_OAL_ShutdownMusic,
|
||||
I_OAL_SetMusicVolume,
|
||||
I_OAL_PauseSong,
|
||||
I_OAL_ResumeSong,
|
||||
I_OAL_RegisterSong,
|
||||
I_OAL_PlaySong,
|
||||
I_OAL_StopSong,
|
||||
I_OAL_UnRegisterSong,
|
||||
I_OAL_DeviceList,
|
||||
};
|
27
src/i_oalmusic.h
Normal file
27
src/i_oalmusic.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright(C) 2023 Roman Fomin
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 for more details.
|
||||
//
|
||||
// DESCRIPTION:
|
||||
//
|
||||
|
||||
#ifndef __I_OALMUSIC__
|
||||
#define __I_OALMUSIC__
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
typedef int (*callback_func_t)(byte *data, int size);
|
||||
|
||||
void I_OAL_HookMusic(callback_func_t callback_func);
|
||||
void I_OAL_SetGain(float gain);
|
||||
|
||||
#endif
|
272
src/i_sdlmusic.c
272
src/i_sdlmusic.c
@ -1,272 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 1999 by
|
||||
// id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program 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 for more details.
|
||||
//
|
||||
// DESCRIPTION:
|
||||
// System interface for SDL music.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// haleyjd
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
|
||||
#include "doomstat.h"
|
||||
#include "doomtype.h"
|
||||
#include "i_sound.h"
|
||||
|
||||
///
|
||||
// MUSIC API.
|
||||
//
|
||||
|
||||
// julian (10/25/2005): rewrote (nearly) entirely
|
||||
|
||||
#include "mus2mid.h"
|
||||
#include "memio.h"
|
||||
#include "m_misc2.h"
|
||||
|
||||
// Only one track at a time
|
||||
static Mix_Music *music = NULL;
|
||||
|
||||
// Some tracks are directly streamed from the RWops;
|
||||
// we need to free them in the end
|
||||
static SDL_RWops *rw = NULL;
|
||||
|
||||
// Same goes for buffers that were allocated to convert music;
|
||||
// since this concerns mus, we could do otherwise but this
|
||||
// approach is better for consistency
|
||||
static void *music_block = NULL;
|
||||
|
||||
// Macro to make code more readable
|
||||
#define CHECK_MUSIC(h) ((h) && music != NULL)
|
||||
|
||||
//
|
||||
// I_ShutdownMusic
|
||||
//
|
||||
// atexit handler.
|
||||
//
|
||||
static void I_SDL_UnRegisterSong(void *handle);
|
||||
static void I_SDL_ShutdownMusic(void)
|
||||
{
|
||||
I_SDL_UnRegisterSong((void *)1);
|
||||
}
|
||||
|
||||
//
|
||||
// I_InitMusic
|
||||
//
|
||||
static boolean I_SDL_InitMusic(int device)
|
||||
{
|
||||
// Initialize SDL_Mixer for MIDI music playback
|
||||
// [crispy] initialize some more audio formats
|
||||
Mix_Init(MIX_INIT_MID | MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// jff 1/18/98 changed interface to make mididata destroyable
|
||||
|
||||
static void I_SDL_SetMusicVolume(int volume);
|
||||
static void I_SDL_PlaySong(void *handle, boolean looping)
|
||||
{
|
||||
if(CHECK_MUSIC(handle) && Mix_PlayMusic(music, looping ? -1 : 0) == -1)
|
||||
{
|
||||
doomprintf("I_PlaySong: Mix_PlayMusic failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// haleyjd 10/28/05: make sure volume settings remain consistent
|
||||
I_SDL_SetMusicVolume(snd_MusicVolume);
|
||||
}
|
||||
|
||||
//
|
||||
// I_SetMusicVolume
|
||||
//
|
||||
|
||||
static int current_midi_volume;
|
||||
|
||||
static void I_SDL_SetMusicVolume(int volume)
|
||||
{
|
||||
current_midi_volume = volume;
|
||||
|
||||
// haleyjd 09/04/06: adjust to use scale from 0 to 15
|
||||
Mix_VolumeMusic((current_midi_volume * 128) / 15);
|
||||
}
|
||||
|
||||
//
|
||||
// I_PauseSong
|
||||
//
|
||||
static void I_SDL_PauseSong(void *handle)
|
||||
{
|
||||
if(CHECK_MUSIC(handle))
|
||||
{
|
||||
// Not for mids
|
||||
if(Mix_GetMusicType(music) != MUS_MID)
|
||||
Mix_PauseMusic();
|
||||
else
|
||||
{
|
||||
// haleyjd 03/21/06: set MIDI volume to zero on pause
|
||||
Mix_VolumeMusic(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// I_ResumeSong
|
||||
//
|
||||
static void I_SDL_ResumeSong(void *handle)
|
||||
{
|
||||
if(CHECK_MUSIC(handle))
|
||||
{
|
||||
// Not for mids
|
||||
if(Mix_GetMusicType(music) != MUS_MID)
|
||||
Mix_ResumeMusic();
|
||||
else
|
||||
Mix_VolumeMusic((current_midi_volume * 128) / 15);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// I_StopSong
|
||||
//
|
||||
static void I_SDL_StopSong(void *handle)
|
||||
{
|
||||
if(CHECK_MUSIC(handle))
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
|
||||
//
|
||||
// I_UnRegisterSong
|
||||
//
|
||||
static void I_SDL_UnRegisterSong(void *handle)
|
||||
{
|
||||
if(CHECK_MUSIC(handle))
|
||||
{
|
||||
// Stop and free song
|
||||
I_SDL_StopSong(handle);
|
||||
Mix_FreeMusic(music);
|
||||
|
||||
// Free RWops
|
||||
if(rw != NULL)
|
||||
SDL_FreeRW(rw);
|
||||
|
||||
// Free music block
|
||||
if(music_block != NULL)
|
||||
free(music_block);
|
||||
|
||||
// Reinitialize all this
|
||||
music = NULL;
|
||||
rw = NULL;
|
||||
music_block = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// I_RegisterSong
|
||||
//
|
||||
static void *I_SDL_RegisterSong(void *data, int size)
|
||||
{
|
||||
if(music != NULL)
|
||||
I_SDL_UnRegisterSong((void *)1);
|
||||
|
||||
if (!IsMus(data, size))
|
||||
{
|
||||
rw = SDL_RWFromMem(data, size);
|
||||
music = Mix_LoadMUS_RW(rw, false);
|
||||
|
||||
// Sometimes SDL_Mixer fail to detect MP3, so we try again
|
||||
if (!music)
|
||||
music = Mix_LoadMUSType_RW(rw, MUS_MP3, false);
|
||||
|
||||
if (!music)
|
||||
printf("I_SDL_RegisterSong: %s\n", SDL_GetError());
|
||||
}
|
||||
else // Assume a MUS file and try to convert
|
||||
{
|
||||
MEMFILE *instream;
|
||||
MEMFILE *outstream;
|
||||
byte *mid;
|
||||
size_t midlen;
|
||||
int result;
|
||||
|
||||
instream = mem_fopen_read(data, size);
|
||||
outstream = mem_fopen_write();
|
||||
|
||||
result = mus2mid(instream, outstream);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
void *outbuf;
|
||||
|
||||
mem_get_buf(outstream, &outbuf, &midlen);
|
||||
|
||||
mid = malloc(midlen);
|
||||
memcpy(mid, outbuf, midlen);
|
||||
}
|
||||
|
||||
mem_fclose(instream);
|
||||
mem_fclose(outstream);
|
||||
|
||||
if (result)
|
||||
{
|
||||
doomprintf("Error loading music: %d", result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
rw = SDL_RWFromMem(mid, midlen);
|
||||
music = Mix_LoadMUS_RW(rw, false);
|
||||
|
||||
if(music == NULL)
|
||||
{
|
||||
// Conversion failed, free everything
|
||||
SDL_FreeRW(rw);
|
||||
rw = NULL;
|
||||
free(mid);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conversion succeeded
|
||||
// -> save memory block to free when unregistering
|
||||
music_block = mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the handle is a simple boolean
|
||||
return music;
|
||||
}
|
||||
|
||||
static int I_SDL_DeviceList(const char *devices[], int size, int *current_device)
|
||||
{
|
||||
*current_device = 0;
|
||||
if (size > 0)
|
||||
{
|
||||
devices[0] = "SDL2 Mixer";
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
music_module_t music_sdl_module =
|
||||
{
|
||||
I_SDL_InitMusic,
|
||||
I_SDL_ShutdownMusic,
|
||||
I_SDL_SetMusicVolume,
|
||||
I_SDL_PauseSong,
|
||||
I_SDL_ResumeSong,
|
||||
I_SDL_RegisterSong,
|
||||
I_SDL_PlaySong,
|
||||
I_SDL_StopSong,
|
||||
I_SDL_UnRegisterSong,
|
||||
I_SDL_DeviceList,
|
||||
};
|
273
src/i_sndfile.c
273
src/i_sndfile.c
@ -18,9 +18,10 @@
|
||||
|
||||
#include "i_sndfile.h"
|
||||
|
||||
#if defined(HAVE_SNDFILE)
|
||||
|
||||
#include <AL/alext.h>
|
||||
#include <sndfile.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct sfvio_data_s
|
||||
{
|
||||
@ -98,90 +99,208 @@ static sf_count_t sfvio_tell(void *user_data)
|
||||
return data->offset;
|
||||
}
|
||||
|
||||
void *Load_SNDFile(void *data, SDL_AudioSpec *sample, Uint8 **wavdata, Uint32 *samplelen)
|
||||
static SF_VIRTUAL_IO sfvio =
|
||||
{
|
||||
sfvio_get_filelen,
|
||||
sfvio_seek,
|
||||
sfvio_read,
|
||||
NULL,
|
||||
sfvio_tell
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
Int16,
|
||||
Float,
|
||||
//IMA4,
|
||||
//MSADPCM
|
||||
} sample_format_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SNDFILE *sndfile;
|
||||
SF_INFO sfinfo;
|
||||
SF_VIRTUAL_IO sfvio =
|
||||
{
|
||||
sfvio_get_filelen,
|
||||
sfvio_seek,
|
||||
sfvio_read,
|
||||
NULL,
|
||||
sfvio_tell
|
||||
};
|
||||
sfvio_data_t sfdata;
|
||||
Uint32 wavlen;
|
||||
short *local_wavdata;
|
||||
|
||||
sfdata.data = data;
|
||||
sfdata.length = *samplelen;
|
||||
sfdata.offset = 0;
|
||||
sample_format_t sample_format;
|
||||
ALint byteblockalign;
|
||||
ALenum format;
|
||||
} sndfile_t;
|
||||
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
|
||||
sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata);
|
||||
|
||||
if (!sndfile)
|
||||
{
|
||||
fprintf(stderr, "sf_open_virtual: %s\n", sf_strerror(sndfile));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sfinfo.frames <= 0 || sfinfo.channels <= 0)
|
||||
{
|
||||
sf_close(sndfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wavlen = sfinfo.frames * sfinfo.channels * sizeof(short);
|
||||
local_wavdata = SDL_malloc(wavlen);
|
||||
|
||||
if (!local_wavdata)
|
||||
{
|
||||
sf_close(sndfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sf_readf_short(sndfile, local_wavdata, sfinfo.frames) < sfinfo.frames)
|
||||
{
|
||||
fprintf(stderr, "sf_readf_short: %s\n", sf_strerror(sndfile));
|
||||
sf_close(sndfile);
|
||||
SDL_free(local_wavdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sf_close(sndfile);
|
||||
|
||||
sample->channels = sfinfo.channels;
|
||||
sample->freq = sfinfo.samplerate;
|
||||
sample->format = AUDIO_S16;
|
||||
|
||||
*wavdata = (Uint8 *)local_wavdata;
|
||||
*samplelen = wavlen;
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *Load_SNDFile(void *data, SDL_AudioSpec *sample, Uint8 **wavdata, Uint32 *samplelen)
|
||||
static void CloseFile(sndfile_t *file)
|
||||
{
|
||||
SDL_RWops *RWops;
|
||||
|
||||
if ((RWops = SDL_RWFromMem(data, *samplelen)) == NULL)
|
||||
if (file->sndfile)
|
||||
{
|
||||
fprintf(stderr, "SDL_RWFromMem: %s\n", SDL_GetError());
|
||||
return NULL;
|
||||
sf_close(file->sndfile);
|
||||
file->sndfile = NULL;
|
||||
}
|
||||
|
||||
if (SDL_LoadWAV_RW(RWops, 1, sample, wavdata, samplelen) == NULL) // 1 = will call SDL_RWclose(RWops) for us
|
||||
{
|
||||
fprintf(stderr, "SDL_LoadWAV_RW: %s\n", SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
static boolean OpenFile(sndfile_t *file, void *data, sf_count_t size)
|
||||
{
|
||||
file->sfdata.data = data;
|
||||
file->sfdata.length = size;
|
||||
file->sfdata.offset = 0;
|
||||
memset(&file->sfinfo, 0, sizeof(file->sfinfo));
|
||||
|
||||
file->sndfile = sf_open_virtual(&sfvio, SFM_READ, &file->sfinfo, &file->sfdata);
|
||||
|
||||
if (!file->sndfile)
|
||||
{
|
||||
fprintf(stderr, "sf_open_virtual: %s\n", sf_strerror(file->sndfile));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file->sfinfo.frames <= 0 || file->sfinfo.channels <= 0)
|
||||
{
|
||||
CloseFile(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect a suitable format to load. Formats like Vorbis and Opus use float
|
||||
// natively, so load as float to avoid clipping when possible. Formats
|
||||
// larger than 16-bit can also use float to preserve a bit more precision.
|
||||
|
||||
file->sample_format = Int16;
|
||||
|
||||
switch ((file->sfinfo.format & SF_FORMAT_SUBMASK))
|
||||
{
|
||||
case SF_FORMAT_PCM_24:
|
||||
case SF_FORMAT_PCM_32:
|
||||
case SF_FORMAT_FLOAT:
|
||||
case SF_FORMAT_DOUBLE:
|
||||
case SF_FORMAT_VORBIS:
|
||||
case SF_FORMAT_OPUS:
|
||||
case SF_FORMAT_ALAC_20:
|
||||
case SF_FORMAT_ALAC_24:
|
||||
case SF_FORMAT_ALAC_32:
|
||||
#if HAVE_SNDFILE_MPEG
|
||||
case SF_FORMAT_MPEG_LAYER_I:
|
||||
case SF_FORMAT_MPEG_LAYER_II:
|
||||
case SF_FORMAT_MPEG_LAYER_III:
|
||||
#endif
|
||||
if (alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
file->sample_format = Float;
|
||||
break;
|
||||
}
|
||||
|
||||
file->byteblockalign = 1;
|
||||
|
||||
if (file->sample_format == Int16)
|
||||
{
|
||||
file->byteblockalign = file->sfinfo.channels * 2;
|
||||
}
|
||||
else if (file->sample_format == Float)
|
||||
{
|
||||
file->byteblockalign = file->sfinfo.channels * 4;
|
||||
}
|
||||
|
||||
// Figure out the OpenAL format from the file and desired sample type.
|
||||
|
||||
file->format = AL_NONE;
|
||||
|
||||
if (file->sfinfo.channels == 1)
|
||||
{
|
||||
if (file->sample_format == Int16)
|
||||
file->format = AL_FORMAT_MONO16;
|
||||
else if (file->sample_format == Float)
|
||||
file->format = AL_FORMAT_MONO_FLOAT32;
|
||||
}
|
||||
else if (file->sfinfo.channels == 2)
|
||||
{
|
||||
if (file->sample_format == Int16)
|
||||
file->format = AL_FORMAT_STEREO16;
|
||||
else if (file->sample_format == Float)
|
||||
file->format = AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
|
||||
if (file->format == AL_NONE)
|
||||
{
|
||||
fprintf(stderr, "Unsupported channel count: %d\n", file->sfinfo.channels);
|
||||
CloseFile(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean I_SND_LoadFile(void *data, ALenum *format, byte **wavdata,
|
||||
ALsizei *size, ALsizei *freq)
|
||||
{
|
||||
sndfile_t file;
|
||||
sf_count_t num_frames = 0;
|
||||
void *local_wavdata = NULL;
|
||||
|
||||
if (OpenFile(&file, data, *size) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
local_wavdata = malloc((size_t)(file.sfinfo.frames * file.byteblockalign));
|
||||
|
||||
if (file.sample_format == Int16)
|
||||
num_frames = sf_readf_short(file.sndfile, local_wavdata, file.sfinfo.frames);
|
||||
else if (file.sample_format == Float)
|
||||
num_frames = sf_readf_float(file.sndfile, local_wavdata, file.sfinfo.frames);
|
||||
|
||||
if (num_frames < file.sfinfo.frames)
|
||||
{
|
||||
fprintf(stderr, "sf_readf: %s\n", sf_strerror(file.sndfile));
|
||||
CloseFile(&file);
|
||||
free(local_wavdata);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseFile(&file);
|
||||
|
||||
*wavdata = local_wavdata;
|
||||
*format = file.format;
|
||||
*size = (ALsizei)(num_frames * file.byteblockalign);
|
||||
*freq = file.sfinfo.samplerate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static sndfile_t stream;
|
||||
static boolean looping;
|
||||
|
||||
boolean I_SND_OpenStream(void *data, ALsizei size, ALenum *format, ALsizei *freq)
|
||||
{
|
||||
if (OpenFile(&stream, data, size) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*format = stream.format;
|
||||
*freq = stream.sfinfo.samplerate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void I_SND_SetLooping(boolean on)
|
||||
{
|
||||
looping = on;
|
||||
}
|
||||
|
||||
int I_SND_FillStream(byte *data, ALsizei size)
|
||||
{
|
||||
sf_count_t num_frames = 0;
|
||||
sf_count_t frames = size / stream.byteblockalign;
|
||||
|
||||
if (stream.sample_format == Int16)
|
||||
num_frames = sf_readf_short(stream.sndfile, (short *)data, frames);
|
||||
else if (stream.sample_format == Float)
|
||||
num_frames = sf_readf_float(stream.sndfile, (float *)data, frames);
|
||||
|
||||
if (num_frames < frames && looping)
|
||||
{
|
||||
sf_seek(stream.sndfile, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
return (num_frames * stream.byteblockalign);
|
||||
}
|
||||
|
||||
void I_SND_CloseStream(void)
|
||||
{
|
||||
CloseFile(&stream);
|
||||
}
|
||||
|
@ -19,8 +19,15 @@
|
||||
#ifndef __I_SNDFILE__
|
||||
#define __I_SNDFILE__
|
||||
|
||||
#include <SDL_audio.h>
|
||||
#include <AL/al.h>
|
||||
#include "doomtype.h"
|
||||
|
||||
void *Load_SNDFile(void *data, SDL_AudioSpec *sample, Uint8 **wavdata, Uint32 *samplelen);
|
||||
boolean I_SND_LoadFile(void *data, ALenum *format, byte **wavdata,
|
||||
ALsizei *size, ALsizei *freq);
|
||||
|
||||
boolean I_SND_OpenStream(void *data, ALsizei size, ALenum *format, ALsizei *freq);
|
||||
void I_SND_SetLooping(boolean on);
|
||||
int I_SND_FillStream(byte *data, ALsizei size);
|
||||
void I_SND_CloseStream(void);
|
||||
|
||||
#endif
|
||||
|
360
src/i_sound.c
360
src/i_sound.c
@ -20,7 +20,8 @@
|
||||
|
||||
// haleyjd
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "doomstat.h"
|
||||
@ -31,7 +32,7 @@
|
||||
// Music modules
|
||||
extern music_module_t music_win_module;
|
||||
extern music_module_t music_fl_module;
|
||||
extern music_module_t music_sdl_module;
|
||||
extern music_module_t music_oal_module;
|
||||
extern music_module_t music_opl_module;
|
||||
|
||||
typedef struct
|
||||
@ -44,8 +45,6 @@ static music_modules_t music_modules[] =
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
{ &music_win_module, 1 },
|
||||
#else
|
||||
{ &music_sdl_module, 1 },
|
||||
#endif
|
||||
#if defined(HAVE_FLUIDSYNTH)
|
||||
{ &music_fl_module, 1 },
|
||||
@ -63,24 +62,22 @@ static boolean snd_init = false;
|
||||
// haleyjd 10/28/05: updated for Julian's music code, need full quality now
|
||||
int snd_samplerate;
|
||||
char *snd_resampling_mode;
|
||||
static Uint16 mix_format;
|
||||
static int mix_channels;
|
||||
|
||||
static ALuint *openal_sources;
|
||||
|
||||
typedef struct {
|
||||
// SFX id of the playing sound effect.
|
||||
// Used to catch duplicates (like chainsaw).
|
||||
sfxinfo_t *sfx;
|
||||
// The channel data pointer.
|
||||
unsigned char* data;
|
||||
// [FG] let SDL_Mixer do the actual sound mixing
|
||||
Mix_Chunk chunk;
|
||||
ALuint *data;
|
||||
// haleyjd 06/16/08: unique id number
|
||||
int idnum;
|
||||
} channel_info_t;
|
||||
|
||||
channel_info_t channelinfo[MAX_CHANNELS];
|
||||
|
||||
// Pitch to stepping lookup, unused.
|
||||
// Pitch to stepping lookup.
|
||||
float steptable[256];
|
||||
|
||||
//
|
||||
@ -101,7 +98,7 @@ static void StopChannel(int channel)
|
||||
|
||||
if (channelinfo[channel].data)
|
||||
{
|
||||
Mix_HaltChannel(channel);
|
||||
alSourceStop(openal_sources[channel]);
|
||||
|
||||
// [FG] immediately free samples not connected to a sound SFX
|
||||
if (channelinfo[channel].sfx == NULL)
|
||||
@ -132,85 +129,16 @@ static void StopChannel(int channel)
|
||||
|
||||
#define SOUNDHDRSIZE 8
|
||||
|
||||
// [FG] support multi-channel samples by converting them to mono first
|
||||
static Uint8 *ConvertToMono(Uint8 **data, SDL_AudioSpec *sample, Uint32 *len)
|
||||
{
|
||||
SDL_AudioCVT cvt;
|
||||
|
||||
if (sample->channels < 1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (SDL_BuildAudioCVT(&cvt,
|
||||
sample->format, sample->channels, sample->freq,
|
||||
sample->format, 1, sample->freq) < 0)
|
||||
{
|
||||
fprintf(stderr, "SDL_BuildAudioCVT: %s\n", SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cvt.len = *len;
|
||||
cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult); // [FG] will call SDL_FreeWAV() on this later
|
||||
memset(cvt.buf, 0, cvt.len * cvt.len_mult);
|
||||
memcpy(cvt.buf, *data, cvt.len);
|
||||
|
||||
if (SDL_ConvertAudio(&cvt) < 0)
|
||||
{
|
||||
SDL_free(cvt.buf);
|
||||
fprintf(stderr, "SDL_ConvertAudio: %s\n", SDL_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_FreeWAV(*data);
|
||||
|
||||
sample->channels = 1;
|
||||
*data = cvt.buf;
|
||||
*len = cvt.len_cvt;
|
||||
|
||||
return *data;
|
||||
}
|
||||
|
||||
// Allocate a new sound chunk and pitch-shift an existing sound up-or-down
|
||||
// into it, based on chocolate-doom/src/i_sdlsound.c:PitchShift().
|
||||
static void PitchShift(sfxinfo_t *sfx, int pitch, Mix_Chunk *chunk)
|
||||
{
|
||||
Sint16 *inp, *outp;
|
||||
Sint16 *srcbuf, *dstbuf;
|
||||
Uint32 srclen, dstlen;
|
||||
|
||||
srcbuf = (Sint16 *)sfx->data;
|
||||
srclen = sfx->alen;
|
||||
|
||||
dstlen = (int)(srclen * steptable[pitch]);
|
||||
|
||||
// ensure that the new buffer length is a multiple of sample size
|
||||
dstlen = (dstlen + 3) & (Uint32)~3;
|
||||
|
||||
dstbuf = (Sint16 *)malloc(dstlen);
|
||||
|
||||
// loop over output buffer. find corresponding input cell, copy over
|
||||
for (outp = dstbuf; outp < dstbuf + dstlen/2; outp++)
|
||||
{
|
||||
inp = srcbuf + (int)((float)(outp - dstbuf) * srclen / dstlen);
|
||||
*outp = *inp;
|
||||
}
|
||||
|
||||
chunk->abuf = (Uint8 *)dstbuf;
|
||||
chunk->alen = dstlen;
|
||||
}
|
||||
|
||||
//
|
||||
// CacheSound
|
||||
//
|
||||
// haleyjd: needs to take a sfxinfo_t ptr, not a sound id num
|
||||
// haleyjd 06/03/06: changed to return boolean for failure or success
|
||||
//
|
||||
static boolean CacheSound(sfxinfo_t *sfx, int channel, int pitch)
|
||||
static boolean CacheSound(sfxinfo_t *sfx, int channel)
|
||||
{
|
||||
int lumpnum, lumplen;
|
||||
Uint8 *lumpdata = NULL, *wavdata = NULL;
|
||||
Mix_Chunk *const chunk = &channelinfo[channel].chunk;
|
||||
byte *lumpdata = NULL, *wavdata = NULL;
|
||||
|
||||
#ifdef RANGECHECK
|
||||
if (channel < 0 || channel >= MAX_CHANNELS)
|
||||
@ -245,24 +173,24 @@ static boolean CacheSound(sfxinfo_t *sfx, int channel, int pitch)
|
||||
// haleyjd 06/03/06: rewrote again to make sound data properly freeable
|
||||
while (sfx->data == NULL)
|
||||
{
|
||||
SDL_AudioSpec sample;
|
||||
SDL_AudioCVT cvt;
|
||||
Uint8 *sampledata;
|
||||
Uint32 samplelen;
|
||||
byte *sampledata;
|
||||
ALsizei size, freq;
|
||||
ALenum format;
|
||||
ALuint *buffer;
|
||||
|
||||
// haleyjd: this should always be called (if lump is already loaded,
|
||||
// W_CacheLumpNum handles that for us).
|
||||
lumpdata = (Uint8 *)W_CacheLumpNum(lumpnum, PU_STATIC);
|
||||
lumpdata = (byte *)W_CacheLumpNum(lumpnum, PU_STATIC);
|
||||
|
||||
// Check the header, and ensure this is a valid sound
|
||||
if (lumpdata[0] == 0x03 && lumpdata[1] == 0x00)
|
||||
{
|
||||
sample.freq = (lumpdata[3] << 8) | lumpdata[2];
|
||||
samplelen = (lumpdata[7] << 24) | (lumpdata[6] << 16) |
|
||||
(lumpdata[5] << 8) | lumpdata[4];
|
||||
freq = (lumpdata[3] << 8) | lumpdata[2];
|
||||
size = (lumpdata[7] << 24) | (lumpdata[6] << 16) |
|
||||
(lumpdata[5] << 8) | lumpdata[4];
|
||||
|
||||
// don't play sounds that think they're longer than they really are
|
||||
if (samplelen > lumplen - SOUNDHDRSIZE)
|
||||
if (size > lumplen - SOUNDHDRSIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -270,53 +198,29 @@ static boolean CacheSound(sfxinfo_t *sfx, int channel, int pitch)
|
||||
sampledata = lumpdata + SOUNDHDRSIZE;
|
||||
|
||||
// All Doom sounds are 8-bit
|
||||
sample.format = AUDIO_U8;
|
||||
sample.channels = 1;
|
||||
format = AL_FORMAT_MONO8;
|
||||
}
|
||||
else
|
||||
{
|
||||
samplelen = lumplen;
|
||||
size = lumplen;
|
||||
|
||||
if (Load_SNDFile(lumpdata, &sample, &wavdata, &samplelen) == NULL)
|
||||
if (I_SND_LoadFile(lumpdata, &format, &wavdata, &size, &freq) == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (sample.channels != 1)
|
||||
{
|
||||
if (ConvertToMono(&wavdata, &sample, &samplelen) == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sampledata = wavdata;
|
||||
}
|
||||
|
||||
// Convert sound to target samplerate
|
||||
if (SDL_BuildAudioCVT(&cvt,
|
||||
sample.format, sample.channels, sample.freq,
|
||||
mix_format, mix_channels, snd_samplerate) < 0)
|
||||
buffer = malloc(sizeof(*buffer));
|
||||
alGenBuffers(1, buffer);
|
||||
alBufferData(*buffer, format, sampledata, size, freq);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "SDL_BuildAudioCVT: %s\n", SDL_GetError());
|
||||
break;
|
||||
fprintf(stderr, "CacheSound: Error buffering data.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
cvt.len = samplelen;
|
||||
cvt.buf = (Uint8 *)malloc(cvt.len * cvt.len_mult);
|
||||
// [FG] clear buffer (cvt.len * cvt.len_mult >= cvt.len_cvt)
|
||||
memset(cvt.buf, 0, cvt.len * cvt.len_mult);
|
||||
memcpy(cvt.buf, sampledata, cvt.len);
|
||||
|
||||
if (SDL_ConvertAudio(&cvt) < 0)
|
||||
{
|
||||
free(cvt.buf);
|
||||
fprintf(stderr, "SDL_ConvertAudio: %s\n", SDL_GetError());
|
||||
break;
|
||||
}
|
||||
|
||||
sfx->data = cvt.buf;
|
||||
sfx->alen = cvt.len_cvt;
|
||||
sfx->data = buffer;
|
||||
}
|
||||
|
||||
// don't need original lump data any more
|
||||
@ -326,7 +230,7 @@ static boolean CacheSound(sfxinfo_t *sfx, int channel, int pitch)
|
||||
}
|
||||
if (wavdata)
|
||||
{
|
||||
SDL_FreeWAV(wavdata);
|
||||
free(wavdata);
|
||||
}
|
||||
|
||||
if (sfx->data == NULL)
|
||||
@ -335,27 +239,10 @@ static boolean CacheSound(sfxinfo_t *sfx, int channel, int pitch)
|
||||
return false;
|
||||
}
|
||||
|
||||
// [FG] let SDL_Mixer do the actual sound mixing
|
||||
chunk->allocated = 1;
|
||||
chunk->volume = MIX_MAX_VOLUME;
|
||||
// Preserve sound SFX id
|
||||
channelinfo[channel].sfx = sfx;
|
||||
|
||||
if (pitch != NORM_PITCH)
|
||||
{
|
||||
PitchShift(sfx, pitch, chunk);
|
||||
|
||||
// [FG] do not connect pitch-shifted samples to a sound SFX
|
||||
channelinfo[channel].sfx = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk->abuf = sfx->data;
|
||||
chunk->alen = sfx->alen;
|
||||
|
||||
// Preserve sound SFX id
|
||||
channelinfo[channel].sfx = sfx;
|
||||
}
|
||||
|
||||
channelinfo[channel].data = chunk->abuf;
|
||||
channelinfo[channel].data = sfx->data;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -370,8 +257,8 @@ int forceFlipPan;
|
||||
//
|
||||
void I_UpdateSoundParams(int channel, int volume, int separation)
|
||||
{
|
||||
int rightvol;
|
||||
int leftvol;
|
||||
ALfloat pan;
|
||||
ALuint source;
|
||||
|
||||
if (!snd_init)
|
||||
return;
|
||||
@ -385,20 +272,16 @@ void I_UpdateSoundParams(int channel, int volume, int separation)
|
||||
if (forceFlipPan)
|
||||
separation = 254 - separation;
|
||||
|
||||
// [FG] linear stereo volume separation
|
||||
leftvol = ((254 - separation) * volume) / 127;
|
||||
rightvol = ((separation) * volume) / 127;
|
||||
source = openal_sources[channel];
|
||||
|
||||
if (leftvol < 0)
|
||||
leftvol = 0;
|
||||
else if (leftvol > 255)
|
||||
leftvol = 255;
|
||||
if (rightvol < 0)
|
||||
rightvol = 0;
|
||||
else if (rightvol > 255)
|
||||
rightvol = 255;
|
||||
alSourcef(source, AL_GAIN, (ALfloat)volume / 127.0f);
|
||||
|
||||
Mix_SetPanning(channel, leftvol, rightvol);
|
||||
// Create a panning effect by moving the source in an arc around the listener.
|
||||
// https://github.com/kcat/openal-soft/issues/194
|
||||
pan = (ALfloat)separation / 255.0f - 0.5f;
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f);
|
||||
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
alSource3f(source, AL_POSITION, pan, 0.0f, -sqrtf(1.0f - pan * pan));
|
||||
}
|
||||
|
||||
// [FG] variable pitch bend range
|
||||
@ -499,11 +382,26 @@ int I_StartSound(sfxinfo_t *sound, int vol, int sep, int pitch, boolean loop)
|
||||
if (channel == MAX_CHANNELS)
|
||||
return -1;
|
||||
|
||||
if (CacheSound(sound, channel, pitch))
|
||||
if (CacheSound(sound, channel))
|
||||
{
|
||||
ALuint source = openal_sources[channel];
|
||||
ALuint buffer = *channelinfo[channel].data;
|
||||
|
||||
channelinfo[channel].idnum = id++; // give the sound a unique id
|
||||
Mix_PlayChannelTimed(channel, &channelinfo[channel].chunk, loop ? -1 : 0, -1);
|
||||
I_UpdateSoundParams(channel, vol, sep);
|
||||
|
||||
alSourcei(source, AL_BUFFER, buffer);
|
||||
alSourcei(source, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
|
||||
if (pitch != NORM_PITCH)
|
||||
{
|
||||
alSourcef(source, AL_PITCH, steptable[pitch]);
|
||||
}
|
||||
|
||||
alSourcePlay(source);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "I_StartSound: Error playing sfx.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
channel = -1;
|
||||
@ -537,6 +435,8 @@ void I_StopSound(int channel)
|
||||
//
|
||||
int I_SoundIsPlaying(int channel)
|
||||
{
|
||||
ALint value;
|
||||
|
||||
if (!snd_init)
|
||||
return false;
|
||||
|
||||
@ -545,7 +445,8 @@ int I_SoundIsPlaying(int channel)
|
||||
I_Error("I_SoundIsPlaying: channel out of range");
|
||||
#endif
|
||||
|
||||
return Mix_Playing(channel);
|
||||
alGetSourcei(openal_sources[channel], AL_SOURCE_STATE, &value);
|
||||
return (value == AL_PLAYING);
|
||||
}
|
||||
|
||||
//
|
||||
@ -606,37 +507,43 @@ void I_UpdateSound(void)
|
||||
//
|
||||
void I_ShutdownSound(void)
|
||||
{
|
||||
if (snd_init)
|
||||
{
|
||||
Mix_CloseAudio();
|
||||
snd_init = 0;
|
||||
}
|
||||
}
|
||||
int i;
|
||||
ALCcontext *context;
|
||||
ALCdevice *device;
|
||||
|
||||
// Calculate slice size, the result must be a power of two.
|
||||
|
||||
static int GetSliceSize(void)
|
||||
{
|
||||
int limit;
|
||||
int n;
|
||||
|
||||
limit = snd_samplerate / TICRATE;
|
||||
|
||||
// Try all powers of two, not exceeding the limit.
|
||||
|
||||
for (n=0;; ++n)
|
||||
if (!snd_init)
|
||||
{
|
||||
// 2^n <= limit < 2^n+1 ?
|
||||
|
||||
if ((1 << (n + 1)) > limit)
|
||||
{
|
||||
return (1 << n);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Should never happen?
|
||||
context = alcGetCurrentContext();
|
||||
|
||||
return 1024;
|
||||
if (!context)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
device = alcGetContextsDevice(context);
|
||||
|
||||
alDeleteSources(MAX_CHANNELS, openal_sources);
|
||||
for (i = 0; i < num_sfx; ++i)
|
||||
{
|
||||
if (S_sfx[i].data)
|
||||
{
|
||||
alDeleteBuffers(1, S_sfx[i].data);
|
||||
free(S_sfx[i].data);
|
||||
}
|
||||
}
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fprintf(stderr, "I_ShutdownSound: Failed to delete object IDs.\n");
|
||||
}
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(context);
|
||||
alcCloseDevice(device);
|
||||
|
||||
snd_init = false;
|
||||
}
|
||||
|
||||
// [FG] add links for likely missing sounds
|
||||
@ -663,43 +570,45 @@ struct {
|
||||
|
||||
void I_InitSound(void)
|
||||
{
|
||||
if (!nosfxparm || !nomusicparm)
|
||||
{
|
||||
const ALCchar *name;
|
||||
ALCdevice *device;
|
||||
ALCcontext *context;
|
||||
|
||||
if (nosfxparm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
printf("I_InitSound: ");
|
||||
|
||||
SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, snd_resampling_mode);
|
||||
name = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
device = alcOpenDevice(name);
|
||||
if (device)
|
||||
{
|
||||
printf("Couldn't initialize SDL audio: %s\n", SDL_GetError());
|
||||
return;
|
||||
printf("Using '%s'.\n", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Could not open a device.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Mix_OpenAudioDevice(snd_samplerate, AUDIO_S16SYS, 2, GetSliceSize(), NULL,
|
||||
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) < 0)
|
||||
context = alcCreateContext(device, NULL);
|
||||
if (!context || alcMakeContextCurrent(context) == ALC_FALSE)
|
||||
{
|
||||
printf("Couldn't open audio with desired format.\n");
|
||||
return;
|
||||
fprintf(stderr, "I_InitSound: Error making context.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// [FG] feed actual sample frequency back into config variable
|
||||
Mix_QuerySpec(&snd_samplerate, &mix_format, &mix_channels);
|
||||
printf("Configured audio device with %.1f kHz (%s%d%s), %d channels.\n",
|
||||
(float)snd_samplerate / 1000,
|
||||
SDL_AUDIO_ISFLOAT(mix_format) ? "F" : SDL_AUDIO_ISSIGNED(mix_format) ? "S" : "U",
|
||||
(int)SDL_AUDIO_BITSIZE(mix_format),
|
||||
SDL_AUDIO_BITSIZE(mix_format) > 8 ? (SDL_AUDIO_ISBIGENDIAN(mix_format) ? "MSB" : "LSB") : "",
|
||||
mix_channels);
|
||||
|
||||
// [FG] let SDL_Mixer do the actual sound mixing
|
||||
Mix_AllocateChannels(MAX_CHANNELS);
|
||||
openal_sources = malloc(MAX_CHANNELS * sizeof(*openal_sources));
|
||||
alGenSources(MAX_CHANNELS, openal_sources);
|
||||
|
||||
I_AtExit(I_ShutdownSound, true);
|
||||
|
||||
snd_init = true;
|
||||
|
||||
// [FG] precache all sound effects
|
||||
if (!nosfxparm)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -710,7 +619,7 @@ void I_InitSound(void)
|
||||
if (!S_sfx[i].name)
|
||||
continue;
|
||||
|
||||
CacheSound(&S_sfx[i], 0, NORM_PITCH);
|
||||
CacheSound(&S_sfx[i], 0);
|
||||
}
|
||||
StopChannel(0);
|
||||
printf("done.\n");
|
||||
@ -729,7 +638,6 @@ void I_InitSound(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int midi_player; // current music module
|
||||
@ -759,28 +667,18 @@ void I_SetMidiPlayer(int device)
|
||||
accum += num_devices;
|
||||
}
|
||||
|
||||
if (!midi_player_module->I_InitMusic(device))
|
||||
{
|
||||
midi_player_module = music_modules[0].module;
|
||||
if (midi_player_module != &music_sdl_module)
|
||||
{
|
||||
midi_player_module->I_InitMusic(0);
|
||||
}
|
||||
}
|
||||
midi_player_module->I_InitMusic(device);
|
||||
active_module = midi_player_module;
|
||||
}
|
||||
|
||||
boolean I_InitMusic(void)
|
||||
{
|
||||
// haleyjd 04/11/03: don't use music if sfx aren't init'd
|
||||
// (may be dependent, docs are unclear)
|
||||
if (nomusicparm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// always initilize SDL music
|
||||
music_sdl_module.I_InitMusic(0);
|
||||
music_oal_module.I_InitMusic(0);
|
||||
|
||||
I_AtExit(I_ShutdownMusic, true);
|
||||
|
||||
@ -797,10 +695,8 @@ boolean I_InitMusic(void)
|
||||
// Fall back to module 0 device 0.
|
||||
midi_player = 0;
|
||||
midi_player_module = music_modules[0].module;
|
||||
if (midi_player_module != &music_sdl_module)
|
||||
{
|
||||
midi_player_module->I_InitMusic(0);
|
||||
}
|
||||
midi_player_module->I_InitMusic(0);
|
||||
|
||||
active_module = midi_player_module;
|
||||
|
||||
return true;
|
||||
@ -808,9 +704,9 @@ boolean I_InitMusic(void)
|
||||
|
||||
void I_ShutdownMusic(void)
|
||||
{
|
||||
music_sdl_module.I_ShutdownMusic();
|
||||
music_oal_module.I_ShutdownMusic();
|
||||
|
||||
if (midi_player_module && midi_player_module != &music_sdl_module)
|
||||
if (midi_player_module)
|
||||
{
|
||||
midi_player_module->I_ShutdownMusic();
|
||||
}
|
||||
@ -860,7 +756,7 @@ void *I_RegisterSong(void *data, int size)
|
||||
midi_player_module->I_ShutdownMusic();
|
||||
}
|
||||
|
||||
active_module = &music_sdl_module;
|
||||
active_module = &music_oal_module;
|
||||
return active_module->I_RegisterSong(data, size);
|
||||
}
|
||||
|
||||
|
@ -125,8 +125,7 @@ musicinfo_t S_music[] = {
|
||||
.volume = -1, \
|
||||
.data = NULL, \
|
||||
.usefulness = -1, \
|
||||
.lumpnum = -1, \
|
||||
.alen = 0}
|
||||
.lumpnum = -1}
|
||||
|
||||
#define SOUND(n, s, p) \
|
||||
SOUND_LINK(n, s, p, 0, -1)
|
||||
|
@ -67,8 +67,6 @@ typedef struct sfxinfo_struct {
|
||||
// lump number of sfx
|
||||
int lumpnum;
|
||||
|
||||
// haleyjd 04/23/08: additional caching data
|
||||
unsigned int alen; // length of converted sound pointed to by data
|
||||
} sfxinfo_t;
|
||||
|
||||
extern int parallel_sfx_limit;
|
||||
|
18
vcpkg.json
18
vcpkg.json
@ -1,19 +1,9 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "sdl2",
|
||||
"features": [ "samplerate" ]
|
||||
},
|
||||
{
|
||||
"name": "sdl2-mixer",
|
||||
"features": [ "libmodplug", "opusfile" ]
|
||||
},
|
||||
"sdl2",
|
||||
"sdl2-net",
|
||||
{
|
||||
"name": "libsndfile",
|
||||
"default-features": false,
|
||||
"features": [ "external-libs" ]
|
||||
},
|
||||
"fluidsynth"
|
||||
"libsndfile",
|
||||
"fluidsynth",
|
||||
"openal-soft"
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user