diff --git a/CMakeLists.txt b/CMakeLists.txt index 74762470ce..c73f950a7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,11 +24,14 @@ set (ORCT2_RESOURCE_DIR ${CMAKE_INSTALL_PREFIX}/share/${PROJECT}/) project(${PROJECT}) add_definitions(-DORCT2_RESOURCE_DIR="${ORCT2_RESOURCE_DIR}") +add_definitions(-DHAVE_CONFIG_H) # include lodepng header include_directories("lodepng/") +# include speex header +include_directories("libspeex/") # add source files -file(GLOB_RECURSE ORCT2_SOURCES "src/*.c" "lodepng/*.c") +file(GLOB_RECURSE ORCT2_SOURCES "src/*.c" "src/*.cpp" "libspeex/*.c" "lodepng/*.c") if (UNIX) # force 32bit build for now and set necessary flags to compile code as is diff --git a/libspeex/arch.h b/libspeex/arch.h new file mode 100644 index 0000000000..d38c36ce7c --- /dev/null +++ b/libspeex/arch.h @@ -0,0 +1,239 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file arch.h + @brief Various architecture definitions Speex +*/ +/* + 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 Xiph.org Foundation 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 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. +*/ + +#ifndef ARCH_H +#define ARCH_H + +#ifndef SPEEX_VERSION +#define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */ +#define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */ +#define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */ +#define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */ +#define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */ +#endif + +/* A couple test to catch stupid option combinations */ +#ifdef FIXED_POINT + +#ifdef FLOATING_POINT +#error You cannot compile as floating point and fixed point at the same time +#endif +#ifdef _USE_SSE +#error SSE is only for floating-point +#endif +#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM)) +#error Make up your mind. What CPU do you have? +#endif +#ifdef VORBIS_PSYCHO +#error Vorbis-psy model currently not implemented in fixed-point +#endif + +#else + +#ifndef FLOATING_POINT +#error You now need to define either FIXED_POINT or FLOATING_POINT +#endif +#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM) +#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions? +#endif +#ifdef FIXED_POINT_DEBUG +#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?" +#endif + + +#endif + +#ifndef OUTSIDE_SPEEX +#include "speex/speex_types.h" +#endif + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */ +#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */ +#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */ +#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */ +#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ + +#ifdef FIXED_POINT + +typedef spx_int16_t spx_word16_t; +typedef spx_int32_t spx_word32_t; +typedef spx_word32_t spx_mem_t; +typedef spx_word16_t spx_coef_t; +typedef spx_word16_t spx_lsp_t; +typedef spx_word32_t spx_sig_t; + +#define Q15ONE 32767 + +#define LPC_SCALING 8192 +#define SIG_SCALING 16384 +#define LSP_SCALING 8192. +#define GAMMA_SCALING 32768. +#define GAIN_SCALING 64 +#define GAIN_SCALING_1 0.015625 + +#define LPC_SHIFT 13 +#define LSP_SHIFT 13 +#define SIG_SHIFT 14 +#define GAIN_SHIFT 6 + +#define VERY_SMALL 0 +#define VERY_LARGE32 ((spx_word32_t)2147483647) +#define VERY_LARGE16 ((spx_word16_t)32767) +#define Q15_ONE ((spx_word16_t)32767) + + +#ifdef FIXED_DEBUG +#include "fixed_debug.h" +#else + +#include "fixed_generic.h" + +#ifdef ARM5E_ASM +#include "fixed_arm5e.h" +#elif defined (ARM4_ASM) +#include "fixed_arm4.h" +#elif defined (BFIN_ASM) +#include "fixed_bfin.h" +#endif + +#endif + + +#else + +typedef float spx_mem_t; +typedef float spx_coef_t; +typedef float spx_lsp_t; +typedef float spx_sig_t; +typedef float spx_word16_t; +typedef float spx_word32_t; + +#define Q15ONE 1.0f +#define LPC_SCALING 1.f +#define SIG_SCALING 1.f +#define LSP_SCALING 1.f +#define GAMMA_SCALING 1.f +#define GAIN_SCALING 1.f +#define GAIN_SCALING_1 1.f + + +#define VERY_SMALL 1e-15f +#define VERY_LARGE32 1e15f +#define VERY_LARGE16 1e15f +#define Q15_ONE ((spx_word16_t)1.f) + +#define QCONST16(x,bits) (x) +#define QCONST32(x,bits) (x) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) (x) +#define EXTEND32(x) (x) +#define SHR16(a,shift) (a) +#define SHL16(a,shift) (a) +#define SHR32(a,shift) (a) +#define SHL32(a,shift) (a) +#define PSHR16(a,shift) (a) +#define PSHR32(a,shift) (a) +#define VSHR32(a,shift) (a) +#define SATURATE16(x,a) (x) +#define SATURATE32(x,a) (x) + +#define PSHR(a,shift) (a) +#define SHR(a,shift) (a) +#define SHL(a,shift) (a) +#define SATURATE(x,a) (x) + +#define ADD16(a,b) ((a)+(b)) +#define SUB16(a,b) ((a)-(b)) +#define ADD32(a,b) ((a)+(b)) +#define SUB32(a,b) ((a)-(b)) +#define MULT16_16_16(a,b) ((a)*(b)) +#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b)) +#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b)) + +#define MULT16_32_Q11(a,b) ((a)*(b)) +#define MULT16_32_Q13(a,b) ((a)*(b)) +#define MULT16_32_Q14(a,b) ((a)*(b)) +#define MULT16_32_Q15(a,b) ((a)*(b)) +#define MULT16_32_P15(a,b) ((a)*(b)) + +#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) + +#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_P13(c,a,b) ((c)+(a)*(b)) +#define MULT16_16_Q11_32(a,b) ((a)*(b)) +#define MULT16_16_Q13(a,b) ((a)*(b)) +#define MULT16_16_Q14(a,b) ((a)*(b)) +#define MULT16_16_Q15(a,b) ((a)*(b)) +#define MULT16_16_P15(a,b) ((a)*(b)) +#define MULT16_16_P13(a,b) ((a)*(b)) +#define MULT16_16_P14(a,b) ((a)*(b)) + +#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) +#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) + + +#endif + + +#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + +/* 2 on TI C5x DSP */ +#define BYTES_PER_CHAR 2 +#define BITS_PER_CHAR 16 +#define LOG2_BITS_PER_CHAR 4 + +#else + +#define BYTES_PER_CHAR 1 +#define BITS_PER_CHAR 8 +#define LOG2_BITS_PER_CHAR 3 + +#endif + + + +#ifdef FIXED_DEBUG +extern long long spx_mips; +#endif + + +#endif diff --git a/libspeex/config.h b/libspeex/config.h new file mode 100644 index 0000000000..abd35f0914 --- /dev/null +++ b/libspeex/config.h @@ -0,0 +1,20 @@ +// Microsoft version of 'inline' +#define inline __inline + +// Visual Studio support alloca(), but it always align variables to 16-bit +// boundary, while SSE need 128-bit alignment. So we disable alloca() when +// SSE is enabled. +#ifndef _USE_SSE +# define USE_ALLOCA +#endif + +/* Default to floating point */ +#ifndef FIXED_POINT +# define FLOATING_POINT +# define USE_SMALLFT +#else +# define USE_KISS_FFT +#endif + +/* We don't support visibility on Win32 */ +#define EXPORT diff --git a/libspeex/os_support.h b/libspeex/os_support.h new file mode 100644 index 0000000000..6b74b0c22f --- /dev/null +++ b/libspeex/os_support.h @@ -0,0 +1,169 @@ +/* Copyright (C) 2007 Jean-Marc Valin + + File: os_support.h + This is the (tiny) OS abstraction layer. Aside from math.h, this is the + only place where system headers are allowed. + + 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. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ + +#ifndef OS_SUPPORT_H +#define OS_SUPPORT_H + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef OS_SUPPORT_CUSTOM +#include "os_support_custom.h" +#endif + +/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_free + NOTE: speex_alloc needs to CLEAR THE MEMORY */ +#ifndef OVERRIDE_SPEEX_ALLOC +static inline void *speex_alloc (int size) +{ + /* WARNING: this is not equivalent to malloc(). If you want to use malloc() + or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise + you will experience strange bugs */ + return calloc(size,1); +} +#endif + +/** Same as speex_alloc, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ +#ifndef OVERRIDE_SPEEX_ALLOC_SCRATCH +static inline void *speex_alloc_scratch (int size) +{ + /* Scratch space doesn't need to be cleared */ + return calloc(size,1); +} +#endif + +/** Speex wrapper for realloc. To do your own dynamic allocation, all you need to do is replace this function, speex_alloc and speex_free */ +#ifndef OVERRIDE_SPEEX_REALLOC +static inline void *speex_realloc (void *ptr, int size) +{ + return realloc(ptr, size); +} +#endif + +/** Speex wrapper for calloc. To do your own dynamic allocation, all you need to do is replace this function, speex_realloc and speex_alloc */ +#ifndef OVERRIDE_SPEEX_FREE +static inline void speex_free (void *ptr) +{ + free(ptr); +} +#endif + +/** Same as speex_free, except that the area is only needed inside a Speex call (might cause problem with wideband though) */ +#ifndef OVERRIDE_SPEEX_FREE_SCRATCH +static inline void speex_free_scratch (void *ptr) +{ + free(ptr); +} +#endif + +/** Copy n bytes of memory from src to dst. The 0* term provides compile-time type checking */ +#ifndef OVERRIDE_SPEEX_COPY +#define SPEEX_COPY(dst, src, n) (memcpy((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) +#endif + +/** Copy n bytes of memory from src to dst, allowing overlapping regions. The 0* term + provides compile-time type checking */ +#ifndef OVERRIDE_SPEEX_MOVE +#define SPEEX_MOVE(dst, src, n) (memmove((dst), (src), (n)*sizeof(*(dst)) + 0*((dst)-(src)) )) +#endif + +/** Set n bytes of memory to value of c, starting at address s */ +#ifndef OVERRIDE_SPEEX_MEMSET +#define SPEEX_MEMSET(dst, c, n) (memset((dst), (c), (n)*sizeof(*(dst)))) +#endif + + +#ifndef OVERRIDE_SPEEX_FATAL +static inline void _speex_fatal(const char *str, const char *file, int line) +{ + fprintf (stderr, "Fatal (internal) error in %s, line %d: %s\n", file, line, str); + exit(1); +} +#endif + +#ifndef OVERRIDE_SPEEX_WARNING +static inline void speex_warning(const char *str) +{ +#ifndef DISABLE_WARNINGS + fprintf (stderr, "warning: %s\n", str); +#endif +} +#endif + +#ifndef OVERRIDE_SPEEX_WARNING_INT +static inline void speex_warning_int(const char *str, int val) +{ +#ifndef DISABLE_WARNINGS + fprintf (stderr, "warning: %s %d\n", str, val); +#endif +} +#endif + +#ifndef OVERRIDE_SPEEX_NOTIFY +static inline void speex_notify(const char *str) +{ +#ifndef DISABLE_NOTIFICATIONS + fprintf (stderr, "notification: %s\n", str); +#endif +} +#endif + +#ifndef OVERRIDE_SPEEX_PUTC +/** Speex wrapper for putc */ +static inline void _speex_putc(int ch, void *file) +{ + FILE *f = (FILE *)file; + fprintf(f, "%c", ch); +} +#endif + +#define speex_fatal(str) _speex_fatal(str, __FILE__, __LINE__); +#define speex_assert(cond) {if (!(cond)) {speex_fatal("assertion failed: " #cond);}} + +#ifndef RELEASE +static inline void print_vec(float *vec, int len, char *name) +{ + int i; + printf ("%s ", name); + for (i=0;i +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} +#include "speex_resampler.h" +#include "arch.h" +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_resampler.h" +#include "arch.h" +#include "os_support.h" +#endif /* OUTSIDE_SPEEX */ + +#include "stack_alloc.h" +#include + +#ifndef M_PI +#define M_PI 3.14159263 +#endif + +#ifdef FIXED_POINT +#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x))) +#else +#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x)))) +#endif + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef _USE_SSE +#include "resample_sse.h" +#endif + +/* Numer of elements to allocate on the stack */ +#ifdef VAR_ARRAYS +#define FIXED_STACK_ALLOC 8192 +#else +#define FIXED_STACK_ALLOC 1024 +#endif + +typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); + +struct SpeexResamplerState_ { + spx_uint32_t in_rate; + spx_uint32_t out_rate; + spx_uint32_t num_rate; + spx_uint32_t den_rate; + + int quality; + spx_uint32_t nb_channels; + spx_uint32_t filt_len; + spx_uint32_t mem_alloc_size; + spx_uint32_t buffer_size; + int int_advance; + int frac_advance; + float cutoff; + spx_uint32_t oversample; + int initialised; + int started; + + /* These are per-channel */ + spx_int32_t *last_sample; + spx_uint32_t *samp_frac_num; + spx_uint32_t *magic_samples; + + spx_word16_t *mem; + spx_word16_t *sinc_table; + spx_uint32_t sinc_table_length; + resampler_basic_func resampler_ptr; + + int in_stride; + int out_stride; +} ; + +static double kaiser12_table[68] = { + 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, + 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, + 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, + 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, + 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, + 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, + 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, + 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, + 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, + 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, + 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, + 0.00001000, 0.00000000}; +/* +static double kaiser12_table[36] = { + 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, + 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, + 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, + 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, + 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, + 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +*/ +static double kaiser10_table[36] = { + 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, + 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, + 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, + 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, + 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, + 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + +static double kaiser8_table[36] = { + 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, + 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, + 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, + 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, + 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, + 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + +static double kaiser6_table[36] = { + 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, + 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, + 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, + 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, + 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, + 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + +struct FuncDef { + double *table; + int oversample; +}; + +static struct FuncDef _KAISER12 = {kaiser12_table, 64}; +#define KAISER12 (&_KAISER12) +/*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; +#define KAISER12 (&_KAISER12)*/ +static struct FuncDef _KAISER10 = {kaiser10_table, 32}; +#define KAISER10 (&_KAISER10) +static struct FuncDef _KAISER8 = {kaiser8_table, 32}; +#define KAISER8 (&_KAISER8) +static struct FuncDef _KAISER6 = {kaiser6_table, 32}; +#define KAISER6 (&_KAISER6) + +struct QualityMapping { + int base_length; + int oversample; + float downsample_bandwidth; + float upsample_bandwidth; + struct FuncDef *window_func; +}; + + +/* This table maps conversion quality to internal parameters. There are two + reasons that explain why the up-sampling bandwidth is larger than the + down-sampling bandwidth: + 1) When up-sampling, we can assume that the spectrum is already attenuated + close to the Nyquist rate (from an A/D or a previous resampling filter) + 2) Any aliasing that occurs very close to the Nyquist rate will be masked + by the sinusoids/noise just below the Nyquist rate (guaranteed only for + up-sampling). +*/ +static const struct QualityMapping quality_map[11] = { + { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ + { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ + { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ + { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ + { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ + { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ + {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ + {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +}; +/*8,24,40,56,80,104,128,160,200,256,320*/ +static double compute_func(float x, struct FuncDef *func) +{ + float y, frac; + double interp[4]; + int ind; + y = x*func->oversample; + ind = (int)floor(y); + frac = (y-ind); + /* CSE with handle the repeated powers */ + interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac); + interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); + /* Just to make sure we don't have rounding problems */ + interp[1] = 1.f-interp[3]-interp[2]-interp[0]; + + /*sum = frac*accum[1] + (1-frac)*accum[2];*/ + return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; +} + +#if 0 +#include +int main(int argc, char **argv) +{ + int i; + for (i=0;i<256;i++) + { + printf ("%f\n", compute_func(i/256., KAISER12)); + } + return 0; +} +#endif + +#ifdef FIXED_POINT +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6f) + return WORD2INT(32768.*cutoff); + else if (fabs(x) > .5f*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); +} +#else +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6) + return cutoff; + else if (fabs(x) > .5*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); +} +#endif + +#ifdef FIXED_POINT +static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + spx_word16_t x2, x3; + x2 = MULT16_16_P15(x, x); + x3 = MULT16_16_P15(x, x2); + interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); + interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); + interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); + /* Just to make sure we don't have rounding problems */ + interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; + if (interp[2]<32767) + interp[2]+=1; +} +#else +static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac; + interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; + /* Just to make sure we don't have rounding problems */ + interp[2] = 1.-interp[0]-interp[1]-interp[3]; +} +#endif + +static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + int j; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_SINGLE + float accum[4] = {0,0,0,0}; + + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + double sum; + int j; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE + double accum[4] = {0,0,0,0}; + + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + int j; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE + spx_word32_t accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + int j; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE + double accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static void update_filter(SpeexResamplerState *st) +{ + spx_uint32_t old_length; + + old_length = st->filt_len; + st->oversample = quality_map[st->quality].oversample; + st->filt_len = quality_map[st->quality].base_length; + + if (st->num_rate > st->den_rate) + { + /* down-sampling */ + st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; + /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ + st->filt_len = st->filt_len*st->num_rate / st->den_rate; + /* Round down to make sure we have a multiple of 4 */ + st->filt_len &= (~0x3); + if (2*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (4*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (8*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (16*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (st->oversample < 1) + st->oversample = 1; + } else { + /* up-sampling */ + st->cutoff = quality_map[st->quality].upsample_bandwidth; + } + + /* Choose the resampling type that requires the least amount of memory */ + if (st->den_rate <= st->oversample) + { + spx_uint32_t i; + if (!st->sinc_table) + st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t)); + else if (st->sinc_table_length < st->filt_len*st->den_rate) + { + st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t)); + st->sinc_table_length = st->filt_len*st->den_rate; + } + for (i=0;iden_rate;i++) + { + spx_int32_t j; + for (j=0;jfilt_len;j++) + { + st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); + } + } +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_direct_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_direct_double; + else + st->resampler_ptr = resampler_basic_direct_single; +#endif + /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ + } else { + spx_int32_t i; + if (!st->sinc_table) + st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); + else if (st->sinc_table_length < st->filt_len*st->oversample+8) + { + st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); + st->sinc_table_length = st->filt_len*st->oversample+8; + } + for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) + st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_interpolate_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_interpolate_double; + else + st->resampler_ptr = resampler_basic_interpolate_single; +#endif + /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ + } + st->int_advance = st->num_rate/st->den_rate; + st->frac_advance = st->num_rate%st->den_rate; + + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + if (!st->mem) + { + spx_uint32_t i; + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + for (i=0;inb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("init filter");*/ + } else if (!st->started) + { + spx_uint32_t i; + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + for (i=0;inb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("reinit filter");*/ + } else if (st->filt_len > old_length) + { + spx_int32_t i; + /* Increase the filter length */ + /*speex_warning("increase filter size");*/ + int old_alloc_size = st->mem_alloc_size; + if ((st->filt_len-1 + st->buffer_size) > st->mem_alloc_size) + { + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + } + for (i=st->nb_channels-1;i>=0;i--) + { + spx_int32_t j; + spx_uint32_t olen = old_length; + /*if (st->magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2*st->magic_samples[i]; + for (j=old_length-2+st->magic_samples[i];j>=0;j--) + st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; + for (j=0;jmagic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = 0; + st->magic_samples[i] = 0; + } + if (st->filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; + /* Then put zeros for lack of anything better */ + for (;jfilt_len-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; + /* Adjust last_sample */ + st->last_sample[i] += (st->filt_len - olen)/2; + } else { + /* Put back some of the magic! */ + st->magic_samples[i] = (olen - st->filt_len)/2; + for (j=0;jfilt_len-1+st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + } + } + } else if (st->filt_len < old_length) + { + spx_uint32_t i; + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ + for (i=0;inb_channels;i++) + { + spx_uint32_t j; + spx_uint32_t old_magic = st->magic_samples[i]; + st->magic_samples[i] = (old_length - st->filt_len)/2; + /* We must copy some of the memory that's no longer used */ + /* Copy data going backward */ + for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + st->magic_samples[i] += old_magic; + } + } + +} + +EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); +} + +EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + spx_uint32_t i; + SpeexResamplerState *st; + if (quality > 10 || quality < 0) + { + if (err) + *err = RESAMPLER_ERR_INVALID_ARG; + return NULL; + } + st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); + st->initialised = 0; + st->started = 0; + st->in_rate = 0; + st->out_rate = 0; + st->num_rate = 0; + st->den_rate = 0; + st->quality = -1; + st->sinc_table_length = 0; + st->mem_alloc_size = 0; + st->filt_len = 0; + st->mem = 0; + st->resampler_ptr = 0; + + st->cutoff = 1.f; + st->nb_channels = nb_channels; + st->in_stride = 1; + st->out_stride = 1; + +#ifdef FIXED_POINT + st->buffer_size = 160; +#else + st->buffer_size = 160; +#endif + + /* Per channel data */ + st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int)); + st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); + st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); + for (i=0;ilast_sample[i] = 0; + st->magic_samples[i] = 0; + st->samp_frac_num[i] = 0; + } + + speex_resampler_set_quality(st, quality); + speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); + + + update_filter(st); + + st->initialised = 1; + if (err) + *err = RESAMPLER_ERR_SUCCESS; + + return st; +} + +EXPORT void speex_resampler_destroy(SpeexResamplerState *st) +{ + speex_free(st->mem); + speex_free(st->sinc_table); + speex_free(st->last_sample); + speex_free(st->magic_samples); + speex_free(st->samp_frac_num); + speex_free(st); +} + +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int j=0; + const int N = st->filt_len; + int out_sample = 0; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + spx_uint32_t ilen; + + st->started = 1; + + /* Call the right resampler through the function ptr */ + out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); + + if (st->last_sample[channel_index] < (spx_int32_t)*in_len) + *in_len = st->last_sample[channel_index]; + *out_len = out_sample; + st->last_sample[channel_index] -= *in_len; + + ilen = *in_len; + + for(j=0;jmagic_samples[channel_index]; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + const int N = st->filt_len; + + speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); + + st->magic_samples[channel_index] -= tmp_in_len; + + /* If we couldn't process all "magic" input samples, save the rest for next time */ + if (st->magic_samples[channel_index]) + { + spx_uint32_t i; + for (i=0;imagic_samples[channel_index];i++) + mem[N-1+i]=mem[N-1+i+tmp_in_len]; + } + *out += out_len*st->out_stride; + return out_len; +} + +#ifdef FIXED_POINT +EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#else +EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#endif +{ + int j; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const int filt_offs = st->filt_len - 1; + const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; + const int istride = st->in_stride; + + if (st->magic_samples[channel_index]) + olen -= speex_resampler_magic(st, channel_index, &out, olen); + if (! st->magic_samples[channel_index]) { + while (ilen && olen) { + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = olen; + + if (in) { + for(j=0;jout_stride; + if (in) + in += ichunk * istride; + } + } + *in_len -= ilen; + *out_len -= olen; + return RESAMPLER_ERR_SUCCESS; +} + +#ifdef FIXED_POINT +EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#else +EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#endif +{ + int j; + const int istride_save = st->in_stride; + const int ostride_save = st->out_stride; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1); +#ifdef VAR_ARRAYS + const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; + VARDECL(spx_word16_t *ystack); + ALLOC(ystack, ylen, spx_word16_t); +#else + const unsigned int ylen = FIXED_STACK_ALLOC; + spx_word16_t ystack[FIXED_STACK_ALLOC]; +#endif + + st->out_stride = 1; + + while (ilen && olen) { + spx_word16_t *y = ystack; + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = (olen > ylen) ? ylen : olen; + spx_uint32_t omagic = 0; + + if (st->magic_samples[channel_index]) { + omagic = speex_resampler_magic(st, channel_index, &y, ochunk); + ochunk -= omagic; + olen -= omagic; + } + if (! st->magic_samples[channel_index]) { + if (in) { + for(j=0;jfilt_len-1]=WORD2INT(in[j*istride_save]); +#else + x[j+st->filt_len-1]=in[j*istride_save]; +#endif + } else { + for(j=0;jfilt_len-1]=0; + } + + speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk); + } else { + ichunk = 0; + ochunk = 0; + } + + for (j=0;jout_stride = ostride_save; + *in_len -= ilen; + *out_len -= olen; + + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_len; + if (in != NULL) + speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_len; + if (in != NULL) + speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); +} + +EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) +{ + *in_rate = st->in_rate; + *out_rate = st->out_rate; +} + +EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + spx_uint32_t fact; + spx_uint32_t old_den; + spx_uint32_t i; + if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) + return RESAMPLER_ERR_SUCCESS; + + old_den = st->den_rate; + st->in_rate = in_rate; + st->out_rate = out_rate; + st->num_rate = ratio_num; + st->den_rate = ratio_den; + /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ + for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) + { + while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) + { + st->num_rate /= fact; + st->den_rate /= fact; + } + } + + if (old_den > 0) + { + for (i=0;inb_channels;i++) + { + st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; + /* Safety net */ + if (st->samp_frac_num[i] >= st->den_rate) + st->samp_frac_num[i] = st->den_rate-1; + } + } + + if (st->initialised) + update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) +{ + *ratio_num = st->num_rate; + *ratio_den = st->den_rate; +} + +EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality) +{ + if (quality > 10 || quality < 0) + return RESAMPLER_ERR_INVALID_ARG; + if (st->quality == quality) + return RESAMPLER_ERR_SUCCESS; + st->quality = quality; + if (st->initialised) + update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) +{ + *quality = st->quality; +} + +EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->in_stride = stride; +} + +EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->in_stride; +} + +EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->out_stride = stride; +} + +EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->out_stride; +} + +EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st) +{ + return st->filt_len / 2; +} + +EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st) +{ + return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; +} + +EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels;i++) + st->last_sample[i] = st->filt_len/2; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels*(st->filt_len-1);i++) + st->mem[i] = 0; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT const char *speex_resampler_strerror(int err) +{ + switch (err) + { + case RESAMPLER_ERR_SUCCESS: + return "Success."; + case RESAMPLER_ERR_ALLOC_FAILED: + return "Memory allocation failed."; + case RESAMPLER_ERR_BAD_STATE: + return "Bad resampler state."; + case RESAMPLER_ERR_INVALID_ARG: + return "Invalid argument."; + case RESAMPLER_ERR_PTR_OVERLAP: + return "Input and output buffers overlap."; + default: + return "Unknown error. Bad error code or strange version mismatch."; + } +} diff --git a/libspeex/speex/speex_resampler.h b/libspeex/speex/speex_resampler.h new file mode 100644 index 0000000000..54eef8d7b8 --- /dev/null +++ b/libspeex/speex/speex_resampler.h @@ -0,0 +1,340 @@ +/* Copyright (C) 2007 Jean-Marc Valin + + File: speex_resampler.h + Resampling code + + The design goals of this code are: + - Very fast algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + 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. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ + + +#ifndef SPEEX_RESAMPLER_H +#define SPEEX_RESAMPLER_H + +#ifdef OUTSIDE_SPEEX + +/********* WARNING: MENTAL SANITY ENDS HERE *************/ + +/* If the resampler is defined outside of Speex, we change the symbol names so that + there won't be any clash if linking with Speex later on. */ + +/* #define RANDOM_PREFIX your software name here */ +#ifndef RANDOM_PREFIX +#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes" +#endif + +#define CAT_PREFIX2(a,b) a ## b +#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b) + +#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init) +#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac) +#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy) +#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float) +#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int) +#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float) +#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int) +#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate) +#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate) +#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac) +#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio) +#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality) +#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality) +#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride) +#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride) +#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride) +#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) +#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) +#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) +#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) +#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) +#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) + +#define spx_int16_t short +#define spx_int32_t int +#define spx_uint16_t unsigned short +#define spx_uint32_t unsigned int + +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_types.h" + +#endif /* OUTSIDE_SPEEX */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPEEX_RESAMPLER_QUALITY_MAX 10 +#define SPEEX_RESAMPLER_QUALITY_MIN 0 +#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4 +#define SPEEX_RESAMPLER_QUALITY_VOIP 3 +#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5 + +enum { + RESAMPLER_ERR_SUCCESS = 0, + RESAMPLER_ERR_ALLOC_FAILED = 1, + RESAMPLER_ERR_BAD_STATE = 2, + RESAMPLER_ERR_INVALID_ARG = 3, + RESAMPLER_ERR_PTR_OVERLAP = 4, + + RESAMPLER_ERR_MAX_ERROR +}; + +struct SpeexResamplerState_; +typedef struct SpeexResamplerState_ SpeexResamplerState; + +/** Create a new resampler with integer input and output rates. + * @param nb_channels Number of channels to be processed + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, + spx_uint32_t in_rate, + spx_uint32_t out_rate, + int quality, + int *err); + +/** Create a new resampler with fractional input/output rates. The sampling + * rate ratio is an arbitrary rational number with both the numerator and + * denominator being 32-bit integers. + * @param nb_channels Number of channels to be processed + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, + spx_uint32_t ratio_num, + spx_uint32_t ratio_den, + spx_uint32_t in_rate, + spx_uint32_t out_rate, + int quality, + int *err); + +/** Destroy a resampler state. + * @param st Resampler state + */ +void speex_resampler_destroy(SpeexResamplerState *st); + +/** Resample a float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the + * number of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_float(SpeexResamplerState *st, + spx_uint32_t channel_index, + const float *in, + spx_uint32_t *in_len, + float *out, + spx_uint32_t *out_len); + +/** Resample an int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_int(SpeexResamplerState *st, + spx_uint32_t channel_index, + const spx_int16_t *in, + spx_uint32_t *in_len, + spx_int16_t *out, + spx_uint32_t *out_len); + +/** Resample an interleaved float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_float(SpeexResamplerState *st, + const float *in, + spx_uint32_t *in_len, + float *out, + spx_uint32_t *out_len); + +/** Resample an interleaved int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_int(SpeexResamplerState *st, + const spx_int16_t *in, + spx_uint32_t *in_len, + spx_int16_t *out, + spx_uint32_t *out_len); + +/** Set (change) the input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + */ +int speex_resampler_set_rate(SpeexResamplerState *st, + spx_uint32_t in_rate, + spx_uint32_t out_rate); + +/** Get the current input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz) copied. + * @param out_rate Output sampling rate (integer number of Hz) copied. + */ +void speex_resampler_get_rate(SpeexResamplerState *st, + spx_uint32_t *in_rate, + spx_uint32_t *out_rate); + +/** Set (change) the input/output sampling rates and resampling ratio + * (fractional values in Hz supported). + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + */ +int speex_resampler_set_rate_frac(SpeexResamplerState *st, + spx_uint32_t ratio_num, + spx_uint32_t ratio_den, + spx_uint32_t in_rate, + spx_uint32_t out_rate); + +/** Get the current resampling ratio. This will be reduced to the least + * common denominator. + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio copied + * @param ratio_den Denominator of the sampling rate ratio copied + */ +void speex_resampler_get_ratio(SpeexResamplerState *st, + spx_uint32_t *ratio_num, + spx_uint32_t *ratio_den); + +/** Set (change) the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +int speex_resampler_set_quality(SpeexResamplerState *st, + int quality); + +/** Get the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +void speex_resampler_get_quality(SpeexResamplerState *st, + int *quality); + +/** Set (change) the input stride. + * @param st Resampler state + * @param stride Input stride + */ +void speex_resampler_set_input_stride(SpeexResamplerState *st, + spx_uint32_t stride); + +/** Get the input stride. + * @param st Resampler state + * @param stride Input stride copied + */ +void speex_resampler_get_input_stride(SpeexResamplerState *st, + spx_uint32_t *stride); + +/** Set (change) the output stride. + * @param st Resampler state + * @param stride Output stride + */ +void speex_resampler_set_output_stride(SpeexResamplerState *st, + spx_uint32_t stride); + +/** Get the output stride. + * @param st Resampler state copied + * @param stride Output stride + */ +void speex_resampler_get_output_stride(SpeexResamplerState *st, + spx_uint32_t *stride); + +/** Get the latency in input samples introduced by the resampler. + * @param st Resampler state + */ +int speex_resampler_get_input_latency(SpeexResamplerState *st); + +/** Get the latency in output samples introduced by the resampler. + * @param st Resampler state + */ +int speex_resampler_get_output_latency(SpeexResamplerState *st); + +/** Make sure that the first samples to go out of the resamplers don't have + * leading zeros. This is only useful before starting to use a newly created + * resampler. It is recommended to use that when resampling an audio file, as + * it will generate a file with the same length. For real-time processing, + * it is probably easier not to use this call (so that the output duration + * is the same for the first frame). + * @param st Resampler state + */ +int speex_resampler_skip_zeros(SpeexResamplerState *st); + +/** Reset a resampler so a new (unrelated) stream can be processed. + * @param st Resampler state + */ +int speex_resampler_reset_mem(SpeexResamplerState *st); + +/** Returns the English meaning for an error code + * @param err Error code + * @return English string + */ +const char *speex_resampler_strerror(int err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libspeex/speex/speex_types.h b/libspeex/speex/speex_types.h new file mode 100644 index 0000000000..852fed801d --- /dev/null +++ b/libspeex/speex/speex_types.h @@ -0,0 +1,126 @@ +/* speex_types.h taken from libogg */ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h 7524 2004-08-11 04:20:36Z conrad $ + + ********************************************************************/ +/** + @file speex_types.h + @brief Speex types +*/ +#ifndef _SPEEX_TYPES_H +#define _SPEEX_TYPES_H + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int32_t spx_int32_t; + typedef _G_uint32_t spx_uint32_t; + typedef _G_int16_t spx_int16_t; + typedef _G_uint16_t spx_uint16_t; +# elif defined(__MINGW32__) + typedef short spx_int16_t; + typedef unsigned short spx_uint16_t; + typedef int spx_int32_t; + typedef unsigned int spx_uint32_t; +# elif defined(__MWERKS__) + typedef int spx_int32_t; + typedef unsigned int spx_uint32_t; + typedef short spx_int16_t; + typedef unsigned short spx_uint16_t; +# else + /* MSVC/Borland */ + typedef __int32 spx_int32_t; + typedef unsigned __int32 spx_uint32_t; + typedef __int16 spx_int16_t; + typedef unsigned __int16 spx_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 spx_int16_t; + typedef UInt16 spx_uint16_t; + typedef SInt32 spx_int32_t; + typedef UInt32 spx_uint32_t; + +#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ + +# include + typedef int16_t spx_int16_t; + typedef u_int16_t spx_uint16_t; + typedef int32_t spx_int32_t; + typedef u_int32_t spx_uint32_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t spx_int16_t; + typedef u_int16_t spx_uint16_t; + typedef int32_t spx_int32_t; + typedef u_int32_t spx_uint32_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short spx_int16_t; + typedef unsigned short spx_uint16_t; + typedef int spx_int32_t; + typedef unsigned int spx_uint32_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short spx_int16_t; + typedef int spx_int32_t; + typedef unsigned int spx_uint32_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef int spx_int32_t; + typedef unsigned spx_uint32_t; + typedef short spx_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short spx_int16_t; + typedef unsigned short spx_uint16_t; + typedef signed int spx_int32_t; + typedef unsigned int spx_uint32_t; + +#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + + typedef short spx_int16_t; + typedef unsigned short spx_uint16_t; + typedef long spx_int32_t; + typedef unsigned long spx_uint32_t; + +#elif defined(CONFIG_TI_C6X) + + typedef short spx_int16_t; + typedef unsigned short spx_uint16_t; + typedef int spx_int32_t; + typedef unsigned int spx_uint32_t; + +#else + +# include + +#endif + +#endif /* _SPEEX_TYPES_H */ diff --git a/libspeex/stack_alloc.h b/libspeex/stack_alloc.h new file mode 100644 index 0000000000..f2a10921c5 --- /dev/null +++ b/libspeex/stack_alloc.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2002 Jean-Marc Valin */ +/** + @file stack_alloc.h + @brief Temporary memory allocation on stack +*/ +/* + 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 Xiph.org Foundation 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 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. +*/ + +#ifndef STACK_ALLOC_H +#define STACK_ALLOC_H + +#ifdef USE_ALLOCA +# ifdef WIN32 +# include +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# endif +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + +#ifdef ENABLE_VALGRIND + +#include + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#endif + +#if defined(VAR_ARRAYS) +#define VARDECL(var) +#define ALLOC(var, size, type) type var[size] +#elif defined(USE_ALLOCA) +#define VARDECL(var) var +#define ALLOC(var, size, type) var = _alloca(sizeof(type)*(size)) +#else +#define VARDECL(var) var +#define ALLOC(var, size, type) var = PUSH(stack, size, type) +#endif + + +#endif diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index 91a3aaa564..d956ad6eed 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -28,11 +28,13 @@ + + @@ -65,6 +67,7 @@ + @@ -77,11 +80,13 @@ + + @@ -191,7 +196,7 @@ $(SolutionDir)..\obj\$(Configuration)\ - $(SolutionDir)..\lodepng;$(SolutionDir)..\sdl\include;$(IncludePath) + $(SolutionDir)..\lodepng;$(SolutionDir)..\sdl\include;$(SolutionDir)..\libspeex;$(IncludePath) $(SolutionDir)..\sdl\lib\x86;$(LibraryPath) $(SolutionDir)..\build\$(Configuration)\ $(SolutionDir)..\obj\$(Configuration)\ @@ -202,7 +207,7 @@ Disabled true 1Byte - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;%(PreprocessorDefinitions) true @@ -223,7 +228,7 @@ false - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;%(PreprocessorDefinitions) true diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index 9063f53269..ea66dda408 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -162,12 +162,18 @@ Header Files - + Header Files Header Files + + Header Files + + + Header Files + @@ -386,9 +392,6 @@ Source Files - - Windows - Source Files @@ -398,6 +401,18 @@ Windows + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/src/addresses.h b/src/addresses.h index 665747655f..22f3f1d3f6 100644 --- a/src/addresses.h +++ b/src/addresses.h @@ -41,6 +41,8 @@ #define RCT2_CALLPROC_4(address, a1, a2, a3, a4, v1, v2, v3, v4) RCT2_CALLFUNC_4(address, void, a1, a2, a3, a4, v1, v2, v3, v4) #define RCT2_CALLPROC_5(address, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) RCT2_CALLFUNC_5(address, void, a1, a2, a3, a4, a5, v1, v2, v3, v4, v5) +#define RCT2_ADDRESS_EASTEREGG_NAMES 0x00988C20 + #define RCT2_ADDRESS_RIDE_PROPERTIES 0x00997C9D #define RCT2_ADDRESS_LAND_TOOL_SIZE 0x009A9800 #define RCT2_ADDRESS_SAVE_PROMPT_MODE 0x009A9802 diff --git a/src/audio.c b/src/audio.c index f250d956ea..9125c36b84 100644 --- a/src/audio.c +++ b/src/audio.c @@ -22,6 +22,8 @@ #include "audio.h" #include "addresses.h" #include "config.h" +#include "map.h" +#include "osinterface.h" #include "rct2.h" #include "sprite.h" #include "viewport.h" @@ -215,14 +217,14 @@ int dsound_create_primary_buffer(int a, int device, int channels, int samples, i } dsdevice = &RCT2_GLOBAL(RCT2_ADDRESS_DSOUND_DEVICES, rct_dsdevice*)[device]; } - memset(&RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info), 0, sizeof(rct_audio_info)); - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_0 = 1; - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).channels = channels; - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).samples = samples; - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_8 = samples * RCT2_GLOBAL(0x01425B4C, uint16); - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).bytes = bits * channels / 8; - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).bits = bits; - RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, rct_audio_info).var_E = 0; + memset(&RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX), 0, sizeof(WAVEFORMATEX)); + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).wFormatTag = 1; + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nChannels = channels; + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nSamplesPerSec = samples; + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nAvgBytesPerSec = samples * RCT2_GLOBAL(0x01425B4C, uint16); + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).nBlockAlign = bits * channels / 8; + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).wBitsPerSample = bits; + RCT2_GLOBAL(RCT2_ADDRESS_AUDIO_INFO, WAVEFORMATEX).cbSize = 0; DSBUFFERDESC bufferdesc; memset(&bufferdesc, 0, sizeof(bufferdesc)); bufferdesc.dwSize = sizeof(bufferdesc); @@ -466,8 +468,8 @@ int sub_4015E7(int channel) } else { sound_channel->var_168 = 1; sound_channel->var_15C = read; - rct_audio_info* audio_info = sound_channel->hmem; - uint16 v = ((audio_info->var_E != 8) - 1) & 0x80; + LPWAVEFORMATEX waveformat = sound_channel->hmem; + uint16 v = ((waveformat->nBlockAlign != 8) - 1) & 0x80; memset(&buf1[read], v, buf1size - r); } } @@ -479,11 +481,26 @@ int sub_4015E7(int channel) return result; } +/** +* +* rct2: 0x00401AF3 +*/ +void sub_401AF3(int channel, const char* filename, int a3, int a4) +{ + rct_sound_channel* sound_channel = &RCT2_ADDRESS(RCT2_ADDRESS_SOUND_CHANNEL_LIST, rct_sound_channel)[channel]; + sound_channel->var_4 = 1; + memcpy(sound_channel->filename, filename, sizeof(sound_channel->filename)); + sound_channel->var_10C = 0; + sound_channel->var_110 = a4; + sound_channel->var_114 = a3; + sound_channel->var_164 = 1; +} + /** * * rct2: 0x004016FF */ -int sound_channel_load_file(int channel, char* filename, int offset) +int sound_channel_load_file(int channel, const char* filename, int offset) { rct_sound_channel* sound_channel = &RCT2_ADDRESS(0x014262E0, rct_sound_channel)[channel]; sound_channel->hmem; @@ -524,18 +541,18 @@ int sound_channel_load_file(int channel, char* filename, int offset) * * rct2: 0x00405222 */ -MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo) +MMRESULT mmio_open(const char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo) { HGLOBAL* hmemold; HGLOBAL hmemold2; HMMIO hmmio1; MMRESULT result; MMCKINFO mmckinfo1; - rct_audio_info audio_info; + WAVEFORMATEX waveformat; hmemold = hmem; *hmem = 0; - hmmio1 = mmioOpenA(filename, 0, MMIO_ALLOCBUF); + hmmio1 = mmioOpenA((char*)filename, 0, MMIO_ALLOCBUF); if (hmmio1) { result = mmioDescend(hmmio1, mmckinfo, 0, 0); if (result != MMSYSERR_NOERROR) { @@ -555,20 +572,20 @@ MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmcki result = 57601; goto label20; } - if (mmioRead(hmmio1, (HPSTR)&audio_info, 16) == 16) { - if (audio_info.var_0 == 1) { + if (mmioRead(hmmio1, (HPSTR)&waveformat, 16) == 16) { + if (waveformat.wFormatTag == 1) { //strcpy(audio_info.var_0, "\x01"); hmem = 0; label11: - hmemold2 = GlobalAlloc(0, (uint16)hmem + 18); + hmemold2 = GlobalAlloc(0, hmem + 18); *hmemold = hmemold2; if (!hmemold2) { result = 57344; goto label20; } - memcpy(hmemold2, &audio_info, 16); - *((uint16*)*hmemold + 8) = (uint16)hmem; - if (!(uint16)hmem || mmioRead(hmmio1, (char*)*hmemold + 18, (uint16)hmem) == (uint16)hmem) { + memcpy(hmemold2, &waveformat, 16); + *((uint16*)*hmemold + 8) = hmem; + if (!hmem || mmioRead(hmmio1, (char*)*hmemold + 18, hmem) == hmem) { result = mmioAscend(hmmio1, &mmckinfo1, 0); if (!result) { goto label24; @@ -1134,17 +1151,68 @@ int sound_prepare(int sound_id, rct_sound *sound, int channels, int software) /** * * rct2: 0x006BB76E +* +* @param sound_id (eax) +* @param ebx (ebx) +* @param x (cx) +* @param y (dx) +* @param z (bp) */ -int sound_play_panned(int sound_id, int x) +int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z) { - //RCT2_CALLPROC_X(0x006BB76E, sound_id, x, 0, 0, 0, 0, 0); - // this function is not complete, need to add in volume adjust int result = 0; if (RCT2_GLOBAL(0x009AF59D, uint8) & 1) { RCT2_GLOBAL(0x00F438AD, uint8) = 0; int volume = 0; - if (x == 0x8001) { - // stuff to adjust volume + if (ebx == 0x8001) { + sint16 x2 = x & 0xFFE0; // round by 32 + sint16 y2 = y & 0xFFE0; + if (x2 < 0x1FFF && y2 < 0x1FFF) { + rct_map_element* mapelement = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[((y2 * 256 + x2) & 0xFFFF) / 8]; + while (mapelement->type & MAP_ELEMENT_TYPE_MASK) { + mapelement++; + } + if ((mapelement->base_height * 8) - 5 > z) { + RCT2_GLOBAL(0x00F438AD, uint8) = 10; + } + } + sint16 v11; + sint16 v12; + switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) { + case MAP_ELEMENT_DIRECTION_WEST: + v11 = y - x; + v12 = ((y + x) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_NORTH: + v11 = -x - y; + v12 = ((y - x) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_EAST: + v11 = x - y; + v12 = ((-y - x) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_SOUTH: + v11 = y + x; + v12 = ((x - y) / 2) - z; + break; + } + rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); + while (1) { + window--; + if (window < RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) { + break; + } + rct_viewport* viewport = window->viewport; + if (viewport && viewport->flags & VIEWPORT_FLAG_SOUND_ON) { + sint16 v15 = v12 - viewport->view_y; + sint16 v16 = v11 - viewport->view_x; + ebx = viewport->x + (v16 >> viewport->zoom); + volume = RCT2_ADDRESS(0x0099282C, int)[sound_id] + ((-1024 * viewport->zoom - 1) << RCT2_GLOBAL(0x00F438AD, uint8)) + 1; + if (v15 < 0 || v15 >= viewport->view_height || v16 < 0 || v16 >= viewport->view_width || volume < -10000) { + return sound_id; + } + } + } } int i = 0; rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; @@ -1157,10 +1225,10 @@ int sound_play_panned(int sound_id, int x) } other_sound->id = sound_id; int pan; - if (x == 0x8000) { + if (ebx == 0x8000) { pan = 0; } else { - int x2 = x << 16; + int x2 = ebx << 16; uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); if (screenwidth < 64) { screenwidth = 64; @@ -1198,7 +1266,7 @@ int sub_401B63(int channel) * * rct2: 0x0040194E */ -int sound_channel_load_file2(int channel, char* filename, int offset) +int sound_channel_load_file2(int channel, const char* filename, int offset) { if (!RCT2_GLOBAL(0x009E1AA4, int)) { return 0; @@ -1539,12 +1607,12 @@ void stop_vehicle_sounds() for (int i = 0; i < 7; i++) { rct_vehicle_sound* vehicle_sound = &RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound)[i]; if (vehicle_sound->id != 0xFFFF) { - if (vehicle_sound->var_18 != 0xFFFF) { + if (vehicle_sound->sound1_id != 0xFFFF) { RCT2_GLOBAL(0x014241BC, uint32) = 1; sound_stop(&vehicle_sound->sound1); RCT2_GLOBAL(0x014241BC, uint32) = 0; } - if (vehicle_sound->var_34 != 0xFFFF) { + if (vehicle_sound->sound2_id != 0xFFFF) { RCT2_GLOBAL(0x014241BC, uint32) = 1; sound_stop(&vehicle_sound->sound2); RCT2_GLOBAL(0x014241BC, uint32) = 0; @@ -1640,4 +1708,171 @@ void unpause_sounds() { RCT2_GLOBAL(0x009AF59C, uint8)--; g_sounds_disabled = 0; -} \ No newline at end of file +} + +/** +* Play/update ride music based on structs updated somewhere else +* rct2: 0x006BC6D8 +*/ +void sub_6BC6D8() +{ + int edi; + int ebx; + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2)) { + if ((RCT2_GLOBAL(0x009AF284, uint32) & (1 << 0))) { + if (!RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x009AF59D, uint8) & 1 && RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) && !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1)) { + while (1) { + int v8 = 0; + int v9 = 1; + rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info); + while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) { + if (music_info->id != (uint8)-1) { + rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1] + 8; + if (RCT2_ADDRESS(0x009AA0B1, uint8*)[music_info3->var_0]) { + v8++; + if (v9 >= music_info->volume) { + v9 = music_info->volume; + edi = (int)music_info; + } + } + } + music_info++; + } + if (v8 <= 1) { + break; + } + edi = -1; + } + + while (1) { + int v8 = 0; + int v9 = 1; + rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info); + while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) { + if (music_info->id != (uint8)-1) { + v8++; + if (v9 >= music_info->volume) { + v9 = music_info->volume; + edi = (int)music_info; + } + } + music_info++; + } + if (v8 <= 2) { + break; + } + edi = -1; + } + + rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2); + int channel = 0; + do { + if (music_info2->id != (uint8)-1) { + rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info); + while (music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*)) { + if (music_info->id == music_info2->id && music_info->var_1 == music_info2->var_1) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + int v16 = sub_401B63(channel); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + if (v16) { + goto label32; + } + break; + } + music_info++; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_channel_stop(channel); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + music_info2->id = -1; + } + label32: + music_info2++; + channel++; + } while(channel < 2); + + for (rct_music_info* music_info = &RCT2_GLOBAL(0x009AF430, rct_music_info); music_info < RCT2_GLOBAL(0x009AF42C, rct_music_info*); music_info++) { + if (music_info->id != (uint8)-1) { + rct_music_info2* music_info2 = &RCT2_GLOBAL(0x009AF46C, rct_music_info2); + int channel = 0; + while (music_info->id != music_info2->id || music_info->var_1 != music_info2->var_1) { + if (music_info2->id == (uint8)-1) { + ebx = channel; + } + music_info2++; + channel++; + if (channel >= 2) { + rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1]; + const char* filename = get_file_path(music_info3->pathid); + RCT2_GLOBAL(0x014241BC, uint32) = 3; + HANDLE hfile = osinterface_file_open(filename); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + if (hfile != INVALID_HANDLE_VALUE) { + RCT2_GLOBAL(0x014241BC, uint32) = 3; + osinterface_file_read(hfile, &RCT2_GLOBAL(0x009AF47E, uint32), 4); + RCT2_GLOBAL(0x014241BC, uint32) = 3; + osinterface_file_close(hfile); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (hfile == INVALID_HANDLE_VALUE || RCT2_GLOBAL(0x009AF47E, uint32) != 0x78787878) { + int offset = music_info->offset - 10000; + if (offset < 0) { + offset = 0; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + int musicloaded = sound_channel_load_file2(ebx, filename, offset & 0xFFFFFFF0); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + if (musicloaded) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + int musicplayed = sound_channel_play(ebx, 0, music_info->volume, music_info->pan, music_info->freq); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + if (musicplayed) { + rct_music_info3* music_info3 = &RCT2_GLOBAL(0x009AF1C8, rct_music_info3*)[music_info->var_1]; + if (music_info3->var_9) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sub_401AF3(ebx, get_file_path(music_info3->pathid), 1, 0); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + rct_music_info2* music_info2 = &RCT2_ADDRESS(0x009AF46C, rct_music_info2)[ebx]; + music_info2->volume = music_info->volume; + music_info2->pan = music_info->pan; + music_info2->freq = music_info->freq; + music_info2->id = music_info->id; + music_info2->var_1 = music_info->var_1; + } + } else { + RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_MUSIC, uint8) = 0; + } + } + + return; + + } + } + + if (music_info->volume != music_info2->volume) { + music_info2->volume = music_info->volume; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_channel_set_volume(channel, music_info->volume); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (music_info->pan != music_info2->pan) { + music_info2->pan = music_info->pan; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_channel_set_pan(channel, music_info->pan); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (music_info->freq != music_info2->freq) { + music_info2->freq = music_info->freq; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_channel_set_frequency(channel, music_info->freq); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + + } + } + + } + } + } +} diff --git a/src/audio.h b/src/audio.h index cb9ba496f5..278f6bd317 100644 --- a/src/audio.h +++ b/src/audio.h @@ -59,20 +59,11 @@ typedef struct rct_sound { struct rct_sound* next; } rct_sound; -typedef struct { - uint16 var_0; - uint16 channels; - uint32 samples; - uint32 var_8; - uint16 bytes; - uint16 bits; - uint16 var_E; -} rct_audio_info; - typedef struct { uint32 var_0; uint32 var_4; - char filename[0x108]; // 0x8 + char filename[MAX_PATH]; // 0x8 + uint32 var_10C; uint32 var_110; uint32 var_114; uint32 var_118; @@ -100,15 +91,15 @@ typedef struct { uint16 id; uint16 var_2; rct_sound sound1; // 0x04 - uint16 var_18; - uint16 var_1A; - uint16 var_1C; - uint16 var_1D; + uint16 sound1_id; // 0x18 + sint16 sound1_volume; // 0x1A + sint16 sound1_pan; // 0x1C + uint16 sound1_freq; rct_sound sound2; // 0x20 - uint16 var_34; - uint16 pad_36; - uint16 var_38; - uint16 var_3A; + uint16 sound2_id; // 0x34 + sint16 sound2_volume; // 0x36 + sint16 sound2_pan; // 0x38 + uint16 sound2_freq; // 0x3A } rct_vehicle_sound; typedef struct { @@ -118,30 +109,55 @@ typedef struct { typedef struct { uint16 id; - uint8 var_2; - uint8 var_3; - uint8 var_4; - uint16 var_5; - uint8 var_7; - uint16 var_8; - uint16 next; // 0xA + sint16 pan; // 0x2 + sint16 var_4; + uint16 frequency; // 0x6 + sint16 var_8; + uint16 next; // 0xA } rct_sound_unknown; +typedef struct { + uint8 id; + uint8 var_1; + sint32 offset; //0x2 + sint16 volume; //0x6 + sint16 pan; //0x8 + uint16 freq; //0xA +} rct_music_info; + +typedef struct { + uint8 id; + uint8 var_1; + uint16 volume; //0x2 + uint16 pan; //0x4 + uint16 freq; //0x6 +} rct_music_info2; + +typedef struct { + uint8 var_0; + uint8 pad_1[0x7]; + uint8 pathid; //0x8 + uint8 var_9; +} rct_music_info3; + int get_dsound_devices(); int dsound_create_primary_buffer(int a, int device, int channels, int samples, int bits); void audio_timefunc(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2, int channel); int audio_release(); MMRESULT mmio_read(HMMIO hmmio, uint32 size, char* buffer, LPMMCKINFO mmckinfo, int* read); MMRESULT mmio_seek(HMMIO* hmmio, LPMMCKINFO mmckinfo1, LPMMCKINFO mmckinfo2, int offset); -MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo); +MMRESULT mmio_open(const char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmckinfo); int sub_40153B(int channel); int sub_4015E7(int channel); +void sub_401AF3(int channel, const char* filename, int a3, int a4); +int sub_401B63(int channel); +void sub_6BC6D8(); int audio_remove_timer(); void audio_close(); LPVOID map_file(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwNumberOfBytesToMap); int unmap_sound_info(); int sound_prepare(int sound_id, rct_sound *sound, int channels, int software); -int sound_play_panned(int sound_id, int x); +int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z); int sound_play(rct_sound* sound, int looping, int volume, int pan, int frequency); int sound_is_playing(rct_sound* sound); int sound_set_frequency(rct_sound* sound, int frequency); @@ -151,8 +167,8 @@ int sound_channel_play(int channel, int a2, int volume, int pan, int frequency); int sound_channel_set_frequency(int channel, int frequency); int sound_channel_set_pan(int channel, int pan); int sound_channel_set_volume(int channel, int volume); -int sound_channel_load_file2(int channel, char* filename, int offset); -int sound_channel_load_file(int channel, char* filename, int offset); +int sound_channel_load_file2(int channel, const char* filename, int offset); +int sound_channel_load_file(int channel, const char* filename, int offset); void sound_channel_free(HMMIO* hmmio, HGLOBAL* hmem); int sound_stop(rct_sound *sound); int sound_stop_all(); @@ -239,7 +255,8 @@ typedef enum { SOUND_TRAM = 59, SOUND_DOOR_OPEN = 60, SOUND_DOOR_CLOSE = 61, - SOUND_62 = 62 + SOUND_62 = 62, + SOUND_MAXID } RCT2_SOUND; #endif diff --git a/src/config.h b/src/config.h index 43c9b25585..1c200ec09c 100644 --- a/src/config.h +++ b/src/config.h @@ -133,7 +133,7 @@ typedef struct general_configuration { uint16 language; } general_configuration_t; -static const struct { char *key; int value; } _currencyLookupTable[] = { +static const struct { const char *key; int value; } _currencyLookupTable[] = { { "GBP", CURRENCY_POUNDS }, { "USD", CURRENCY_DOLLARS }, { "FRF", CURRENCY_FRANC }, diff --git a/src/game.c b/src/game.c index 00ec7cf776..39556fc8e9 100644 --- a/src/game.c +++ b/src/game.c @@ -383,7 +383,7 @@ void game_logic_update() RCT2_CALLPROC_EBPSAFE(0x006B5A2A); RCT2_CALLPROC_EBPSAFE(0x006B6456); // update ride measurements RCT2_CALLPROC_EBPSAFE(0x0068AFAD); - RCT2_CALLPROC_EBPSAFE(0x006BBC6B); // vehicle and scream sounds + vehicle_sounds_update();//RCT2_CALLPROC_EBPSAFE(0x006BBC6B); // vehicle and scream sounds peep_update_crowd_noise(); climate_update_sound(); news_item_update_current(); diff --git a/src/hook.c b/src/hook.c new file mode 100644 index 0000000000..6c8dd9395a --- /dev/null +++ b/src/hook.c @@ -0,0 +1,201 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 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 3 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. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#include +#include "hook.h" + +void* g_hooktableaddress = 0; +int g_hooktableoffset = 0; +int g_maxhooks = 1000; + +void hookfunc(int address, int newaddress, int stacksize, int registerargs[], int registersreturned) +{ + int i = 0; + char data[100]; + + int registerssaved = 7; + int n = registersreturned; + for (; n; registerssaved--) { + n &= n - 1; + } + int numrargs = 0; + for (int j = 0; ; j++) { + if (registerargs[j] != END) { + numrargs++; + } else { + break; + } + } + + int rargssize = numrargs * 4; + + data[i++] = 0x50; // push eax + + // move stack down for possible existing arguments + for (int j = 0; j < stacksize; j++) { + data[i++] = 0x8B; // mov eax, [esp+x] + data[i++] = 0x44; + data[i++] = 0xE4; + data[i++] = (signed char)((4 * (stacksize - j)) + 4); + + data[i++] = 0x89; // mov [esp+x], eax + data[i++] = 0x44; + data[i++] = 0xE4; + data[i++] = (signed char)((4 * (stacksize - j)) - ((registerssaved + stacksize) * 4)); + } + + if (numrargs > 0) { + // push the registers to be on the stack to access as arguments + data[i++] = 0x83; // add esp, x + data[i++] = 0xC4; + data[i++] = -((registerssaved + stacksize) * 4) + 4; + + for (signed int j = numrargs - 1; j >= 0; j--) { + switch (registerargs[j]) { + case EAX: data[i++] = 0x50; break; + case EBX: data[i++] = 0x53; break; + case ECX: data[i++] = 0x51; break; + case EDX: data[i++] = 0x52; break; + case ESI: data[i++] = 0x56; break; + case EDI: data[i++] = 0x57; break; + case EBP: data[i++] = 0x55; break; + } + } + + data[i++] = 0x83; // add esp, x + data[i++] = 0xC4; + data[i++] = rargssize + ((registerssaved + stacksize) * 4) - 4; + } + + + data[i++] = 0xE8; // call + data[i++] = 0x00; + data[i++] = 0x00; + data[i++] = 0x00; + data[i++] = 0x00; + + int sizec = i; + + data[i++] = 0x8B; // push eax, [esp] - puts eip in eax + data[i++] = 0x04; + data[i++] = 0xE4; + + data[i++] = 0x83; // add eax, x + data[i++] = 0xC0; + int sizeoffset = i; + data[i++] = 0; // set to returnlocation offset later + + data[i++] = 0x89; // mov [esp-20h], eax - put return address on stack + data[i++] = 0x44; + data[i++] = 0xE4; + data[i++] = (signed char)(-(registerssaved * 4) - rargssize - (stacksize * 4)) + 4; + + data[i++] = 0x83; // add esp, x + data[i++] = 0xC4; + data[i++] = 4; + + data[i++] = 0x58; // pop eax + + if (!(registersreturned & EAX)) { + data[i++] = 0x50; // push eax + } + if (!(registersreturned & EBX)) { + data[i++] = 0x53; // push ebx + } + if (!(registersreturned & ECX)) { + data[i++] = 0x51; // push ecx + } + if (!(registersreturned & EDX)) { + data[i++] = 0x52; // push edx + } + if (!(registersreturned & EBP)) { + data[i++] = 0x55; // push ebp + } + if (!(registersreturned & ESI)) { + data[i++] = 0x56; // push esi + } + if (!(registersreturned & EDI)) { + data[i++] = 0x57; // push edi + } + + data[i++] = 0x83; // sub esp, x + data[i++] = 0xEC; + data[i++] = 4 + (stacksize * 4) + rargssize; + + data[i++] = 0xEA; // jmp + *((int *)&data[i]) = newaddress; i += 4; + data[i++] = 0x23; + data[i++] = 0x00; + + data[sizeoffset] = i - sizec; + + // returnlocation: + + data[i++] = 0x83; // sub esp, x + data[i++] = 0xEC; + data[i++] = (signed char)(stacksize * -4) - rargssize; + + if (!(registersreturned & EDI)) { + data[i++] = 0x5F; // pop edi + } + if (!(registersreturned & ESI)) { + data[i++] = 0x5E; // pop esi + } + if (!(registersreturned & EBP)) { + data[i++] = 0x5D; // pop ebp + } + if (!(registersreturned & EDX)) { + data[i++] = 0x5A; // pop edx + } + if (!(registersreturned & ECX)) { + data[i++] = 0x59; // pop ecx + } + if (!(registersreturned & EBX)) { + data[i++] = 0x5B; // pop ebx + } + if (!(registersreturned & EAX)) { + data[i++] = 0x58; // pop eax + } + + data[i++] = 0xC3; // retn + + WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0); +} + +void addhook(int address, int newaddress, int stacksize, int registerargs[], int registersreturned) +{ + if (!g_hooktableaddress) { + g_hooktableaddress = VirtualAllocEx(GetCurrentProcess(), NULL, g_maxhooks * 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + } + if (g_hooktableoffset > g_maxhooks) { + return; + } + unsigned int hookaddress = (unsigned int)g_hooktableaddress + (g_hooktableoffset * 100); + char data[9]; + int i = 0; + data[i++] = 0xEA; // jmp + *((int *)&data[i]) = hookaddress; i += 4; + data[i++] = 0x23; + data[i++] = 0x00; + data[i++] = 0xC3; // retn + WriteProcessMemory(GetCurrentProcess(), (LPVOID)address, data, i, 0); + hookfunc(hookaddress, newaddress, stacksize, registerargs, registersreturned); + g_hooktableoffset++; +} \ No newline at end of file diff --git a/src/hook.h b/src/hook.h new file mode 100644 index 0000000000..fed10792f3 --- /dev/null +++ b/src/hook.h @@ -0,0 +1,37 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 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 3 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. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#ifndef _HOOK_H_ +#define _HOOK_H_ + +enum REGISTER_ARGS { + EAX = 1 << 0, + EBX = 1 << 1, + ECX = 1 << 2, + EDX = 1 << 3, + ESI = 1 << 4, + EDI = 1 << 5, + EBP = 1 << 6, + END = 0 +}; + +void addhook(int address, int newaddress, int stacksize, int registerargs[], int registersreturned); + +#endif \ No newline at end of file diff --git a/src/input.c b/src/input.c index 429c5c3d2e..74020608c3 100644 --- a/src/input.c +++ b/src/input.c @@ -536,7 +536,7 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex) if (widget_is_disabled(w, widgetIndex)) break; - sound_play_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2); + sound_play_panned(SOUND_CLICK_1, w->x + (widget->left + widget->right) / 2, 0, 0, 0); // Set new cursor down widget RCT2_GLOBAL(RCT2_ADDRESS_CURSOR_DOWN_WINDOWCLASS, rct_windowclass) = windowClass; @@ -649,7 +649,7 @@ void input_state_widget_pressed( int x, int y, int state, int widgetIndex, rct_w break; int mid_point_x = (widget->left + widget->right) / 2 + w->x; - sound_play_panned(5, mid_point_x); + sound_play_panned(5, mid_point_x, 0, 0, 0); if (cursor_w_class != w->classification || cursor_w_number != w->number || widgetIndex != cursor_widgetIndex) break; diff --git a/src/mixer.cpp b/src/mixer.cpp new file mode 100644 index 0000000000..baabda9f85 --- /dev/null +++ b/src/mixer.cpp @@ -0,0 +1,440 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 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 3 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. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#include +#include +#include + +extern "C" { +#include "audio.h" +#include "config.h" +} +#include "mixer.h" + +Mixer gMixer; + +Sample::Sample() +{ + data = 0; + length = 0; + issdlwav = false; +} + +Sample::~Sample() +{ + Unload(); +} + +bool Sample::Load(const char* filename) +{ + Unload(); + SDL_RWops* rw = SDL_RWFromFile(filename, "rb"); + if (!rw) { + SDL_RWclose(rw); + return false; + } + SDL_AudioSpec audiospec; + memset(&audiospec, 0, sizeof(audiospec)); + SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &data, (Uint32*)&length); + if (spec != NULL) { + format.freq = spec->freq; + format.format = spec->format; + format.channels = spec->channels; + issdlwav = true; + } else { + return false; + } + return true; +} + +bool Sample::LoadCSS1(const char* filename, unsigned int offset) +{ + Unload(); + SDL_RWops* rw = SDL_RWFromFile(filename, "rb"); + if (!rw) { + return false; + } + Uint32 numsounds; + SDL_RWread(rw, &numsounds, sizeof(numsounds), 1); + if (offset > numsounds) { + SDL_RWclose(rw); + return false; + } + SDL_RWseek(rw, offset * 4, RW_SEEK_CUR); + Uint32 soundoffset; + SDL_RWread(rw, &soundoffset, sizeof(soundoffset), 1); + SDL_RWseek(rw, soundoffset, RW_SEEK_SET); + Uint32 soundsize; + SDL_RWread(rw, &soundsize, sizeof(soundsize), 1); + length = soundsize; + WAVEFORMATEX waveformat; + SDL_RWread(rw, &waveformat, sizeof(waveformat), 1); + format.freq = waveformat.nSamplesPerSec; + format.format = AUDIO_S16LSB; + format.channels = waveformat.nChannels; + data = new uint8[length]; + SDL_RWread(rw, data, length, 1); + SDL_RWclose(rw); + return true; +} + +void Sample::Unload() +{ + if (data) { + if (issdlwav) { + SDL_FreeWAV(data); + } else { + delete[] data; + } + data = 0; + } + issdlwav = false; + length = 0; +} + +bool Sample::Convert(AudioFormat format) +{ + if(Sample::format.format != format.format || Sample::format.channels != format.channels || Sample::format.freq != format.freq){ + SDL_AudioCVT cvt; + if (SDL_BuildAudioCVT(&cvt, Sample::format.format, Sample::format.channels, Sample::format.freq, format.format, format.channels, format.freq) < 0) { + return false; + } + cvt.len = length; + cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult]; + memcpy(cvt.buf, data, length); + if (SDL_ConvertAudio(&cvt) < 0) { + delete[] cvt.buf; + return false; + } + Unload(); + data = cvt.buf; + length = cvt.len_cvt; + Sample::format = format; + } + return true; +} + +const uint8* Sample::Data() +{ + return data; +} + +unsigned long Sample::Length() +{ + return length; +} + +Stream::Stream() +{ + sourcetype = SOURCE_NONE; +} + +unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length) +{ + unsigned long size = length; + switch(sourcetype) { + case SOURCE_SAMPLE: + if (offset >= sample->Length()) { + return 0; + } + if (offset + length > sample->Length()) { + size = sample->Length() - offset; + } + *data = &sample->Data()[offset]; + return size; + break; + } + return 0; +} + +unsigned long Stream::Length() +{ + switch(sourcetype) { + case SOURCE_SAMPLE: + return sample->Length(); + break; + } + return 0; +} + +void Stream::SetSource_Sample(Sample& sample) +{ + sourcetype = SOURCE_SAMPLE; + Stream::sample = &sample; +} + +const AudioFormat* Stream::Format() +{ + switch(sourcetype) { + case SOURCE_SAMPLE: + return &sample->format; + break; + } + return 0; +} + +Channel::Channel() +{ + rate = 1; + resampler = 0; + SetVolume(SDL_MIX_MAXVOLUME); +} + +Channel::~Channel() +{ + if (resampler) { + speex_resampler_destroy(resampler); + resampler = 0; + } +} + +void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE) +{ + Channel::stream = &stream; + Channel::loop = loop; + offset = 0; +} + +void Channel::SetRate(double rate) +{ + Channel::rate = rate; + if (Channel::rate < 0.001) { + Channel::rate = 0.001; + } +} + +void Channel::SetVolume(int volume) +{ + Channel::volume = volume; + if (volume > SDL_MIX_MAXVOLUME) { + Channel::volume = SDL_MIX_MAXVOLUME; + } + if (volume < 0) { + Channel::volume = 0; + } +} + +void Channel::SetPan(float pan) +{ + Channel::pan = pan; + if (pan > 1) { + Channel::pan = 1; + } + if (pan < 0) { + Channel::pan = 0; + } + volume_l = (float)sin((1.0 - Channel::pan) * M_PI / 2.0); + volume_r = (float)sin(Channel::pan * M_PI / 2.0); +} + +void Mixer::Init(const char* device) +{ + Close(); + SDL_AudioSpec want, have; + SDL_zero(want); + want.freq = 44100; + want.format = AUDIO_S16SYS; + want.channels = 2; + want.samples = 1024; + want.callback = Callback; + want.userdata = this; + deviceid = SDL_OpenAudioDevice(device, 0, &want, &have, 0); + format.format = have.format; + format.channels = have.channels; + format.freq = have.freq; + const char* filename = get_file_path(PATH_ID_CSS1); + for (int i = 0; i < SOUND_MAXID; i++) { + css1samples[i].LoadCSS1(filename, i); + css1samples[i].Convert(format); // convert to audio output format, saves some cpu usage but requires a bit more memory, optional + css1streams[i].SetSource_Sample(css1samples[i]); + } + effectbuffer = new uint8[(have.samples * format.BytesPerSample() * format.channels) + 200]; + SDL_PauseAudioDevice(deviceid, 0); +} + +void Mixer::Close() +{ + SDL_CloseAudioDevice(deviceid); + delete[] effectbuffer; +} + +void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length) +{ + Mixer* mixer = (Mixer*)arg; + memset(stream, 0, length); + for (int i = 0; i < 10; i++) { + mixer->MixChannel(mixer->channels[i], stream, length); + } +} + +void Mixer::MixChannel(Channel& channel, uint8* data, int length) +{ + if (channel.stream) { + if (!channel.resampler) { + channel.resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0); + } + AudioFormat channelformat = *channel.stream->Format(); + int loaded = 0; + SDL_AudioCVT cvt; + cvt.len_ratio = 1; + do { + int samplesize = format.channels * format.BytesPerSample(); + int samples = length / samplesize; + int samplesloaded = loaded / samplesize; + int samplestoread = (int)ceil((samples - samplesloaded) * channel.rate); + int lengthloaded = 0; + if (channel.offset < channel.stream->Length()) { + bool mustconvert = false; + if (MustConvert(*channel.stream)) { + if (SDL_BuildAudioCVT(&cvt, channelformat.format, channelformat.channels, channelformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) { + break; + } + mustconvert = true; + } + + const uint8* datastream = 0; + int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, (int)(((samplestoread) * samplesize) / cvt.len_ratio)) / channelformat.BytesPerSample()) * channelformat.BytesPerSample(); + if (readfromstream == 0) { + break; + } + + int volume = channel.volume; + uint8* dataconverted = 0; + const uint8* tomix = 0; + + if (mustconvert) { + if (Convert(cvt, datastream, readfromstream, &dataconverted)) { + tomix = dataconverted; + lengthloaded = (cvt.len_cvt / samplesize) * samplesize; + } else { + break; + } + } else { + tomix = datastream; + lengthloaded = readfromstream; + } + + bool effectbufferloaded = false; + + if (channel.rate != 1 && format.format == AUDIO_S16SYS) { + int in_len = (int)(ceil((double)lengthloaded / samplesize)); + int out_len = samples + 20; // needs some extra, otherwise resampler sometimes doesn't process all the input samples + speex_resampler_set_rate(channel.resampler, format.freq, (int)(format.freq * (1 / channel.rate))); + speex_resampler_process_interleaved_int(channel.resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len); + effectbufferloaded = true; + tomix = effectbuffer; + lengthloaded = (out_len * samplesize); + } + + if (channel.pan != 0.5f && format.channels == 2) { + if (!effectbufferloaded) { + memcpy(effectbuffer, tomix, lengthloaded); + effectbufferloaded = true; + tomix = effectbuffer; + } + switch (format.format) { + case AUDIO_S16SYS: + EffectPanS16(channel, (sint16*)effectbuffer, lengthloaded / samplesize); + break; + case AUDIO_U8: + EffectPanU8(channel, (uint8*)effectbuffer, lengthloaded / samplesize); + break; + } + } + + int mixlength = lengthloaded; + if (loaded + mixlength > length) { + mixlength = length - loaded; + } + + SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, volume); + + if (dataconverted) { + delete[] dataconverted; + } + + channel.offset += readfromstream; + + } + + loaded += lengthloaded; + + if (channel.loop != 0 && channel.offset >= channel.stream->Length()) { + if (channel.loop != -1) { + channel.loop--; + } + channel.offset = 0; + } + } while(loaded < length && channel.loop != 0); + } +} + +void Mixer::EffectPanS16(Channel& channel, sint16* data, int length) +{ + float left = channel.volume_l; + float right = channel.volume_r; + for (int i = 0; i < length * 2; i += 2) { + data[i] = (sint16)(data[i] * left); + data[i + 1] = (sint16)(data[i + 1] * right); + } +} + +void Mixer::EffectPanU8(Channel& channel, uint8* data, int length) +{ + float left = channel.volume_l; + float right = channel.volume_r; + for (int i = 0; i < length * 2; i += 2) { + data[i] = (uint8)(data[i] * left); + data[i + 1] = (uint8)(data[i + 1] * right); + } +} + +bool Mixer::MustConvert(Stream& stream) +{ + const AudioFormat* streamformat = stream.Format(); + if (!streamformat) { + return false; + } + if (streamformat->format != format.format || streamformat->channels != format.channels || streamformat->freq != format.freq) { + return true; + } + return false; +} + +bool Mixer::Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout) +{ + if (length == 0 || cvt.len_mult == 0) { + return false; + } + cvt.len = length; + cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult]; + memcpy(cvt.buf, data, length); + if (SDL_ConvertAudio(&cvt) < 0) { + delete[] cvt.buf; + return false; + } + *dataout = cvt.buf; + return true; +} + +void Mixer_Init(const char* device) +{ + gMixer.Init(device); +} \ No newline at end of file diff --git a/src/mixer.h b/src/mixer.h new file mode 100644 index 0000000000..b2b453034b --- /dev/null +++ b/src/mixer.h @@ -0,0 +1,136 @@ +/***************************************************************************** + * Copyright (c) 2014 Ted John + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * This file is part of OpenRCT2. + * + * OpenRCT2 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 3 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. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ + +#ifndef _MIXER_H_ +#define _MIXER_H_ + +#include "rct2.h" + +#define MIXER_LOOP_NONE 0 +#define MIXER_LOOP_INFINITE -1 + +#ifdef __cplusplus + +extern "C" { +#include +} + +struct AudioFormat { + int BytesPerSample() const { return (SDL_AUDIO_BITSIZE(format)) / 8; }; + int freq; + SDL_AudioFormat format; + int channels; +}; + +class Sample +{ +public: + Sample(); + ~Sample(); + bool Load(const char* filename); + bool LoadCSS1(const char* filename, unsigned int offset); + void Unload(); + bool Convert(AudioFormat format); + const uint8* Data(); + unsigned long Length(); + + friend class Stream; + +private: + AudioFormat format; + uint8* data; + unsigned long length; + bool issdlwav; +}; + +class Stream +{ +public: + Stream(); + unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length); + unsigned long Length(); + void SetSource_Sample(Sample& sample); + const AudioFormat* Format(); + + friend class Mixer; + +private: + enum { + SOURCE_NONE = 0, + SOURCE_SAMPLE + } sourcetype; + Sample* sample; +}; + +class Channel +{ +public: + Channel(); + ~Channel(); + void Play(Stream& stream, int loop); + void SetRate(double rate); + void SetVolume(int volume); + void SetPan(float pan); + + friend class Mixer; + +private: + int loop; + unsigned long offset; + double rate; + int volume; + float volume_l, volume_r; + float pan; + SpeexResamplerState* resampler; + Stream* stream; +}; + +class Mixer +{ +public: + void Init(const char* device); + void Close(); + +private: + static void SDLCALL Callback(void* arg, uint8* data, int length); + void MixChannel(Channel& channel, uint8* buffer, int length); + void EffectPanS16(Channel& channel, sint16* data, int length); + void EffectPanU8(Channel& channel, uint8* data, int length); + bool MustConvert(Stream& stream); + bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout); + SDL_AudioDeviceID deviceid; + AudioFormat format; + uint8* effectbuffer; + Sample css1samples[SOUND_MAXID]; + Stream css1streams[SOUND_MAXID]; + Channel channels[10]; +}; + +extern "C" +{ +#endif + +void Mixer_Init(const char* device); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/news_item.c b/src/news_item.c index 0d2986aba3..b4c7ec3f29 100644 --- a/src/news_item.c +++ b/src/news_item.c @@ -97,7 +97,7 @@ void news_item_update_current() newsItems[0].ticks++; if (newsItems[0].ticks == 1 && !(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 1)) { // Play sound - sound_play_panned(SOUND_NEWS_ITEM, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2); + sound_play_panned(SOUND_NEWS_ITEM, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, sint16) / 2, 0, 0, 0); } // Removal of current news item diff --git a/src/osinterface.c b/src/osinterface.c index 017fa8c551..4f95acd7f3 100644 --- a/src/osinterface.c +++ b/src/osinterface.c @@ -654,7 +654,7 @@ int osinterface_file_close(HANDLE handle) * * rct2: 0x00408060 */ -HANDLE osinterface_file_open(char* filename) +HANDLE osinterface_file_open(const char* filename) { return CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, 0); } @@ -663,7 +663,7 @@ HANDLE osinterface_file_open(char* filename) * * rct2: 0x0040807D */ -HANDLE osinterface_file_create(char* filename) +HANDLE osinterface_file_create(const char* filename) { return CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); } @@ -672,7 +672,7 @@ HANDLE osinterface_file_create(char* filename) * * rct2: 0x00408099 */ -int osinterface_file_move(char* srcfilename, char* dstfilename) +int osinterface_file_move(const char* srcfilename, const char* dstfilename) { return (MoveFileA(srcfilename, dstfilename) != 0) - 1; } @@ -681,7 +681,7 @@ int osinterface_file_move(char* srcfilename, char* dstfilename) * * rct2: 0x004080AF */ -int osinterface_file_delete(char* filename) +int osinterface_file_delete(const char* filename) { return (DeleteFileA(filename) != 0) - 1; } diff --git a/src/osinterface.h b/src/osinterface.h index a9480bb0db..7c08580483 100644 --- a/src/osinterface.h +++ b/src/osinterface.h @@ -21,6 +21,7 @@ #ifndef _SDL_INTERFACE_H_ #define _SDL_INTERFACE_H_ +#include #include "rct2.h" enum { @@ -74,7 +75,7 @@ typedef struct { char path[260]; uint32 var_20C; uint8 pad_210[0x100]; - char addon[15][0x80]; + char addon[16][0x80]; uint32 addons; //0xB10 } rct2_install_info; @@ -96,6 +97,10 @@ void osinterface_progressbar_setpos(int pos); void osinterface_set_cursor(char cursor); +HANDLE osinterface_file_open(const char* filename); +int osinterface_file_read(HANDLE handle, void* data, int size); +int osinterface_file_close(HANDLE handle); + int osinterface_open_common_file_dialog(int type, char *title, char *filename, char *filterPattern, char *filterName); void osinterface_show_messagebox(char* message); char* osinterface_open_directory_browser(char *title); diff --git a/src/peep.c b/src/peep.c index 24841b5f25..f86c26b1c2 100644 --- a/src/peep.c +++ b/src/peep.c @@ -394,7 +394,7 @@ void peep_applause() } // Play applause noise - sound_play_panned(SOUND_APPLAUSE, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2); + sound_play_panned(SOUND_APPLAUSE, RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2, 0, 0, 0); } /** @@ -682,3 +682,37 @@ int get_peep_face_sprite_small(rct_peep *peep){ int get_peep_face_sprite_large(rct_peep *peep){ return face_sprite_large[get_face_sprite_offset(peep)]; } + +/** +* +* rct2: 0x0069A5A0 +* tests if a peep's name matches a cheat code, normally returns using a register flag +* @param index (eax) +* @param ride (esi) +*/ +int peep_check_cheatcode(int index, rct_peep *peep) +{ + char* str = RCT2_ADDRESS(RCT2_ADDRESS_EASTEREGG_NAMES, char*)[index]; + char* dst = (char*)RCT2_ADDRESS_COMMON_STRING_FORMAT_BUFFER; + format_string(dst, peep->name_string_idx, &peep->id); + + // strtoupper: + int i = 0; + while(dst[i]) { + if (dst[i] >= 0x61 && dst[i] <= 0x7A) { + dst[i] -= 0x20; + } + i++; + } + + // check for match, characters are -1 to obfuscate the cheat codes + i = 0; + while(str[i] + 1) { + if (str[i] + 1 != dst[i]) { + return 0; + } + i++; + } + + return 1; +} diff --git a/src/peep.h b/src/peep.h index c090f67001..05cc200039 100644 --- a/src/peep.h +++ b/src/peep.h @@ -456,6 +456,7 @@ void get_arguments_from_action(rct_peep* peep, uint32 *argument_1, uint32* argum void get_arguments_from_thought(rct_peep_thought thought, uint32* argument_1, uint32* argument_2); int get_peep_face_sprite_small(rct_peep *peep); int get_peep_face_sprite_large(rct_peep *peep); +int peep_check_cheatcode(int index, rct_peep *peep); #endif diff --git a/src/rct2.c b/src/rct2.c index 29572477a3..0af4c93dea 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -39,6 +39,7 @@ #include "intro.h" #include "language.h" #include "map.h" +#include "mixer.h" #include "news_item.h" #include "object.h" #include "osinterface.h" @@ -88,6 +89,7 @@ __declspec(dllexport) int StartOpenRCT(HINSTANCE hInstance, HINSTANCE hPrevInsta config_init(); language_open(gGeneral_config.language); rct2_init(); + Mixer_Init(NULL); rct2_loop(); osinterface_free(); exit(0); diff --git a/src/ride.c b/src/ride.c index 7478d4cccc..1c2e31db13 100644 --- a/src/ride.c +++ b/src/ride.c @@ -25,6 +25,7 @@ #include "news_item.h" #include "sprite.h" #include "ride.h" +#include "scenario.h" #include "sprite.h" #include "peep.h" #include "window.h" @@ -474,3 +475,51 @@ int ride_try_construct(rct_map_element *trackMapElement) RCT2_CALLPROC_X(0x006CC056, 0, 0, 0, (int)trackMapElement, 0, 0, 0); return 1; } + +/** +* +* rct2: 0x006AC988 +* set the speed of the gokart type vehicle at the start to a random value or alter if peep name is a cheat code +* @param ride (esi) +*/ +void ride_init_vehicle_speed(rct_ride *ride) +{ + int ecx = -1; + while (1) { + ecx++; + if (ecx >= ride->var_0C8) { + break; + } + rct_vehicle *vehicle = &g_sprite_list[ride->train_car_map[ecx]].vehicle; + vehicle->var_48 &= (1 << 6); + uint8 r = scenario_rand(); + r = 0xC; + r &= 0xF; + r -= 8; + + int testaddr = (vehicle->var_31 * 0x65); + testaddr += (int)RCT2_ADDRESS(0x009ACFA4, rct_ride_type*)[vehicle->var_D6]; + uint8 test = ((uint8*)testaddr)[0x76]; + r += test; + + vehicle->speed = r; + if (vehicle->var_B3) { + rct_peep *peep = &g_sprite_list[vehicle->peep].peep; + if (peep_check_cheatcode(0, peep)) { // MICHAEL SCHUMACHER + vehicle->speed += 35; + } + if (peep_check_cheatcode(1, peep)) { // JACQUES VILLENEUVE + vehicle->speed += 25; + } + if (peep_check_cheatcode(2, peep)) { // DAMON HILL + vehicle->speed += 55; + } + if (peep_check_cheatcode(4, peep)) { // CHRIS SAWYER + vehicle->speed += 14; + } + if (peep_check_cheatcode(3, peep)) { // MR BEAN + vehicle->speed = 9; + } + } + } +} \ No newline at end of file diff --git a/src/ride.h b/src/ride.h index d454020c92..13ee2c4090 100644 --- a/src/ride.h +++ b/src/ride.h @@ -81,7 +81,9 @@ typedef struct { uint16 overall_view; // 0x050 00XX = X, XX00 = Y (* 32 + 16) uint16 station_starts[4]; // 0x052 uint8 station_heights[4]; // 0x05A - uint8 pad_05E[0xC]; + uint8 pad_05E[4]; + uint8 var_62[4]; + uint8 pad_66[4]; uint16 entrances[4]; // 0x06A uint16 exits[4]; // 0x072 uint8 pad_07A[0x0C]; @@ -90,11 +92,12 @@ typedef struct { // Not sure if these should be uint or sint. uint8 var_0C7; - uint8 var_0C8; + uint8 var_0C8; // Number of train cars? uint8 var_0C9; - uint8 pad_0CA[0x1A]; - + uint8 pad_0CA[0x06]; + uint8 var_0D0; + uint8 pad_0D1[0x13]; sint32 var_0E4; sint32 var_0E8; sint32 var_0EC; @@ -147,7 +150,9 @@ typedef struct { sint32 profit; // 0x1B4 uint8 queue_time[4]; // 0x1B8 uint8 var_1BC; - uint8 pad_1BD[0x10]; + uint8 pad_1BD[0xD]; + uint16 var_1CA; + uint8 var_1CC; uint8 var_1CD; uint16 guests_favourite; // 0x1CE uint32 lifecycle_flags; // 0x1D0 @@ -389,5 +394,6 @@ rct_map_element *sub_6CAF80(int rideIndex, int *outX, int *outY); rct_map_element *ride_find_track_gap(rct_map_element *startTrackElement, int *outX, int *outY); void ride_construct_new(int list_item); int ride_try_construct(rct_map_element *trackMapElement); +void ride_init_vehicle_speed(rct_ride *ride); #endif diff --git a/src/vehicle.c b/src/vehicle.c index 2bf9f92029..6cd47e648a 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -19,11 +19,445 @@ *****************************************************************************/ #include "addresses.h" +#include "audio.h" +#include "ride.h" #include "sprite.h" #include "vehicle.h" +#include "viewport.h" static void vehicle_update(rct_vehicle *vehicle); +/** +* +* rct2: 0x006BB9FF +*/ +void sub_6BB9FF(rct_vehicle* vehicle) +{ + if (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 2) && (!(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & 4) || RCT2_GLOBAL(0x0141F570, uint8) == 6)) { + if (vehicle->var_BB != (uint8)-1 || vehicle->var_BD != (uint8)-1) { + if (vehicle->var_16.width != 0x8000) { + RCT2_GLOBAL(0x009AF5A0, rct_widthheight) = vehicle->var_16; + RCT2_GLOBAL(0x009AF5A4, rct_widthheight) = vehicle->view; + sint16 v4 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_x; + sint16 v5 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_y; + sint16 v6 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_width / 4; + sint16 v7 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_height / 4; + if (!RCT2_GLOBAL(0x00F438A8, rct_window*)->classification) { + v4 -= v6; + v5 -= v7; + } + if (v4 < RCT2_GLOBAL(0x009AF5A4, rct_widthheight).width && v5 < RCT2_GLOBAL(0x009AF5A4, rct_widthheight).height) { + sint16 t8 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_width + v4; + sint16 t9 = RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_height + v5; + if (!RCT2_GLOBAL(0x00F438A8, rct_window*)->classification) { + t8 += v6 + v6; + t9 += v7 + v7; + } + if (t8 >= RCT2_GLOBAL(0x009AF5A0, rct_widthheight).width && t9 >= RCT2_GLOBAL(0x009AF5A0, rct_widthheight).height) { + uint16 v9 = sub_6BC2F3(vehicle); + rct_sound_unknown* i; + for (i = RCT2_ADDRESS(0x00F438B4, rct_sound_unknown); i < RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*) && v9 <= i->next; i++); + if (i < RCT2_ADDRESS(0x00F43908, rct_sound_unknown)) { // 0x00F43908 is end of rct_sound_unknown list, which has 7 elements, not to be confused with variable at 0x00F43908 + if (RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*) < RCT2_ADDRESS(0x00F43908, rct_sound_unknown)) { + RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*)++; + } + rct_sound_unknown* j = RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*) - 1; + while (j >= i) { + j--; + *(j + 1) = *j; + } + i->next = v9; + rct_widthheight v12; + v12.height = vehicle->var_16.height; + v12.width = ((uint16)RCT2_GLOBAL(0x009AF5A0, rct_widthheight).width / 2) + ((uint16)RCT2_GLOBAL(0x009AF5A4, rct_widthheight).width / 2) - RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_x; + v12.width >>= RCT2_GLOBAL(0x00F438A4, rct_viewport*)->zoom; + v12.width += RCT2_GLOBAL(0x00F438A4, rct_viewport*)->x; + + uint16 v14 = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); + if (v14 < 64) { + v14 = 64; + } + rct_widthheight v15; + i->pan = (((((uint32)v12.width << 16) / v14) - 0x8000) >> 4); + v15.width = 0; + v15.height = (RCT2_GLOBAL(0x009AF5A0, rct_widthheight).height / 2) + (RCT2_GLOBAL(0x009AF5A4, rct_widthheight).height / 2) - RCT2_GLOBAL(0x00F438A4, rct_viewport*)->view_y; + v15.height >>= RCT2_GLOBAL(0x00F438A4, rct_viewport*)->zoom; + v15.height += RCT2_GLOBAL(0x00F438A4, rct_viewport*)->y; + + uint16 v18 = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16); + if (v18 < 64) { + v18 = 64; + } + i->var_4 = (sint16)(((v15.both / v18) - 0x8000) >> 4); + sint32 v19 = vehicle->var_28; + + int testaddr = (vehicle->var_31 * 0x65); + testaddr += (int)RCT2_ADDRESS(0x009ACFA4, rct_ride_type*)[vehicle->var_D6]; + uint8 test = ((uint8*)testaddr)[0x74]; + + if (test & 1) { + v19 *= 2; + } + if (v19 < 0) { + v19 = -v19; + } + v19 >>= 5; + v19 *= 0x1588; + v19 >>= 14; + v19 += 0x2B11; + v19 += 16 * vehicle->var_BF; + i->frequency = (uint16)v19; + i->id = vehicle->sprite_index; + i->var_8 = 0; + if (vehicle->x != 0x8000) { + uint16 v22 = (vehicle->y & 0xFFE0) << 8; + v22 |= (vehicle->x & 0xFFE0 | v22) & 0xFFFF; + rct_map_element* map_element; + for (map_element = RCT2_ADDRESS(RCT2_ADDRESS_TILE_MAP_ELEMENT_POINTERS, rct_map_element*)[v22 >> 3]; map_element->type & MAP_ELEMENT_TYPE_MASK; map_element++); + if (map_element->base_height * 8 > vehicle->z) { + i->var_8 = 0x30; + } + } + } + } + } + } + } + } +} + + +/** +* +* rct2: 0x006BC2F3 +*/ +int sub_6BC2F3(rct_vehicle* vehicle) +{ + int result = 0; + rct_vehicle* vehicle_temp = vehicle; + do { + result += vehicle_temp->var_46; + } while (vehicle_temp->next_vehicle_on_train != (uint16)-1 && (vehicle_temp = GET_VEHICLE(vehicle_temp->next_vehicle_on_train))); + sint32 v4 = vehicle->var_28; + if (v4 < 0) { + v4 = -v4; + } + result += ((uint16)v4) >> 13; + rct_vehicle_sound* vehicle_sound = RCT2_ADDRESS(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound); + while (vehicle_sound->id != vehicle->sprite_index) { + vehicle_sound++; + if (vehicle_sound >= RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound*)) { + return result; + } + } + return result + 300; +} + +/** +* +* rct2: 0x006BBC6B +*/ +void vehicle_sounds_update() +{ + uint16 result; + if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_SOUND_DEVICE, uint32) != -1 && !RCT2_GLOBAL(0x009AF59C, uint8) && RCT2_GLOBAL(0x009AF59D, uint8) & 1) { + RCT2_GLOBAL(0x00F438A4, rct_viewport*) = (rct_viewport*)-1; + rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); + rct_viewport* viewport = (rct_viewport*)-1; + for (window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); window >= RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window); window--) { + viewport = window->viewport; + if (viewport && viewport->flags & VIEWPORT_FLAG_SOUND_ON) { + break; + } + } + RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport; + if (viewport != (rct_viewport*)-1) { + if (window) { + RCT2_GLOBAL(0x00F438A8, rct_window*) = window; + RCT2_GLOBAL(0x00F438AC, uint8) = 0; + if (viewport->zoom) { + RCT2_GLOBAL(0x00F438AC, uint8) = 35; + if (viewport->zoom != 1) { + RCT2_GLOBAL(0x00F438AC, uint8) = 70; + } + } + } + //label12: + RCT2_GLOBAL(0x00F438B0, rct_sound_unknown**) = &RCT2_GLOBAL(0x00F438B4, rct_sound_unknown*); + for (uint16 i = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_VEHICLE, uint16); i != SPRITE_INDEX_NULL; i = g_sprite_list[i].vehicle.next) { + sub_6BB9FF(&g_sprite_list[i].vehicle); + } + RCT2_ADDRESS_VEHICLE_SOUND_LIST; + for (rct_vehicle_sound* vehicle_sound = &RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound); vehicle_sound != &RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound); vehicle_sound++) { + if (vehicle_sound->id != (uint16)-1) { + for (rct_sound_unknown* sound_unknown = &RCT2_GLOBAL(0x00F438B4, rct_sound_unknown); sound_unknown != RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*); sound_unknown++) { + if (vehicle_sound->id == sound_unknown->id) { + goto label26; + } + } + if (vehicle_sound->sound1_id != (uint16)-1) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound1); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (vehicle_sound->sound2_id != (uint16)-1) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound2); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + vehicle_sound->id = (uint16)-1; + } + label26: + 1; + } + + for (rct_sound_unknown* sound_unknown = &RCT2_GLOBAL(0x00F438B4, rct_sound_unknown); sound_unknown != RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*); sound_unknown++) { + label28: + result = (uint16)-1; + sint16 v = sound_unknown->var_4; + if (v < 0) { + v = -v; + } + if (v > 0xFFF) { + v = 0xFFF; + } + v -= 0x800; + if (v > 0) { + v -= 0x400; + v = -v; + v = (uint16)v / 4; + result = MAKEWORD(LOBYTE(v), HIBYTE(result)); + if (HIBYTE(v) != 0) { + result = MAKEWORD(0xFF, HIBYTE(result)); + if (HIBYTE(v) < 0) { + result = MAKEWORD(0, HIBYTE(result)); + } + } + } + + sint16 w = sound_unknown->pan; + if (w < 0) { + w = -w; + } + if (w > 0xFFF) { + w = 0xFFF; + } + w -= 0x800; + if (w > 0) { + w -= 0x400; + w = -w; + w = (uint16)w / 4; + result = MAKEWORD(LOBYTE(result), LOBYTE(w)); + if (HIBYTE(w) != 0) { + result = MAKEWORD(LOBYTE(result), 0xFF); + if (HIBYTE(w) < 0) { + result = MAKEWORD(LOBYTE(result), 0); + } + } + } + + if (LOBYTE(result) >= HIBYTE(result)) { + result = MAKEWORD(HIBYTE(result), HIBYTE(result)); + } + if (LOBYTE(result) < RCT2_GLOBAL(0x00F438AC, uint8)) { + result = MAKEWORD(0, HIBYTE(result)); + } else { + result = MAKEWORD(LOBYTE(result) - RCT2_GLOBAL(0x00F438AC, uint8), HIBYTE(result)); + } + + rct_vehicle_sound* vehicle_sound = &RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound); + while (sound_unknown->id != vehicle_sound->id) { + vehicle_sound++; + if (vehicle_sound >= &RCT2_GLOBAL(0x009AF42C, rct_vehicle_sound)) { + vehicle_sound = &RCT2_GLOBAL(RCT2_ADDRESS_VEHICLE_SOUND_LIST, rct_vehicle_sound); + int i = 0; + while (vehicle_sound->id != (uint16)-1) { + vehicle_sound++; + i++; + if (i >= RCT2_GLOBAL(0x009AAC75, sint8)) { + sound_unknown = (rct_sound_unknown*)((int)sound_unknown + sound_unknown->next); + goto label28; + } + } + vehicle_sound->id = sound_unknown->id; + vehicle_sound->sound1_id = (uint16)-1; + vehicle_sound->sound2_id = (uint16)-1; + vehicle_sound->var_2 = 0x30; + break; + } + } + + uint8 v21 = LOBYTE(sound_unknown->var_8); + uint8 v22 = LOBYTE(vehicle_sound->var_2); + if (v22 != v21) { + if (v22 < v21) { + v22 += 4; + } else { + v22 -= 4; + } + } + vehicle_sound->var_2 = v22; + if (LOBYTE(result) < v22) { + result = MAKEWORD(0, HIBYTE(result)); + } else { + result = MAKEWORD(LOBYTE(result) - v22, HIBYTE(result)); + } + // do sound1 stuff, track noise + RCT2_ADDRESS_SPRITE_LIST; + rct_sprite* sprite = &g_sprite_list[sound_unknown->id]; + sint16 volume = sprite->vehicle.var_BC; + volume *= LOBYTE(result); + volume = (uint16)volume / 8; + volume -= 0x1FFF; + if (volume < -10000) { + volume = -10000; + } + if (sprite->vehicle.var_BB == (uint8)-1) { + if (vehicle_sound->sound1_id != (uint16)-1) { + vehicle_sound->sound1_id = -1; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound1); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } else { + if (vehicle_sound->sound1_id == (uint16)-1) { + goto label69; + } + if (sprite->vehicle.var_BB != vehicle_sound->sound1_id) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound1); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + label69: + vehicle_sound->sound1_id = sprite->vehicle.var_BB; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_prepare(sprite->vehicle.var_BB, &vehicle_sound->sound1, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + vehicle_sound->sound1_pan = sound_unknown->pan; + vehicle_sound->sound1_volume = volume; + vehicle_sound->sound1_freq = sound_unknown->frequency; + uint16 frequency = sound_unknown->frequency; + if (RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.var_BB] & 2) { + frequency = (frequency / 2) + 4000; + } + uint8 looping = RCT2_ADDRESS(0x009AF51E, uint8)[2 * sprite->vehicle.var_BB]; + int pan = sound_unknown->pan; + if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { + pan = 0; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_play(&vehicle_sound->sound1, looping, volume, pan, frequency); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + goto label87; + } + if (volume != vehicle_sound->sound1_volume) { + vehicle_sound->sound1_volume = volume; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_volume(&vehicle_sound->sound1, volume); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (sound_unknown->pan != vehicle_sound->sound1_pan) { + vehicle_sound->sound1_pan = sound_unknown->pan; + if (RCT2_GLOBAL(0x009AAC6D, uint8)) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_pan(&vehicle_sound->sound1, sound_unknown->pan); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && sound_unknown->frequency != vehicle_sound->sound1_freq) { + vehicle_sound->sound1_freq = sound_unknown->frequency; + uint16 frequency = sound_unknown->frequency; + if (RCT2_GLOBAL(0x009AF51F, uint8*)[2 * sprite->vehicle.var_BB] & 2) { + frequency = (frequency / 2) + 4000; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_frequency(&vehicle_sound->sound1, frequency); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } + label87: // do sound2 stuff, screams + sprite = &g_sprite_list[sound_unknown->id]; + volume = sprite->vehicle.var_BE; + volume *= LOBYTE(result); + volume = (uint16)volume / 8; + volume -= 0x1FFF; + if (volume < -10000) { + volume = -10000; + } + if (sprite->vehicle.var_BD == (uint8)-1) { + if (vehicle_sound->sound2_id != (uint16)-1) { + vehicle_sound->sound2_id = -1; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound2); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } else { + if (vehicle_sound->sound2_id == (uint16)-1) { + goto label93; + } + if (sprite->vehicle.var_BD != vehicle_sound->sound2_id) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_stop(&vehicle_sound->sound2); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + label93: + vehicle_sound->sound2_id = sprite->vehicle.var_BD; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_prepare(sprite->vehicle.var_BD, &vehicle_sound->sound2, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + vehicle_sound->sound2_pan = sound_unknown->pan; + vehicle_sound->sound2_volume = volume; + vehicle_sound->sound2_freq = sound_unknown->frequency; + uint16 frequency = sound_unknown->frequency; + if (RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.var_BD] & 1) { + frequency = 12649; + } + frequency = (frequency * 2) - 3248; + if (frequency > 25700) { + frequency = 25700; + } + uint8 looping = RCT2_ADDRESS(0x009AF51E, uint8)[2 * sprite->vehicle.var_BD]; + int pan = sound_unknown->pan; + if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { + pan = 0; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_play(&vehicle_sound->sound2, looping, volume, pan, frequency); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + goto label114; + } + if (volume != vehicle_sound->sound2_volume) { + vehicle_sound->sound2_volume = volume; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_volume(&vehicle_sound->sound2, volume); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + if (sound_unknown->pan != vehicle_sound->sound2_pan) { + vehicle_sound->sound2_pan = sound_unknown->pan; + if (RCT2_GLOBAL(0x009AAC6D, uint8)) { + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_pan(&vehicle_sound->sound2, sound_unknown->pan); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } + if (!(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3) && sound_unknown->frequency != vehicle_sound->sound2_freq) { + vehicle_sound->sound2_freq = sound_unknown->frequency; + if (!(RCT2_ADDRESS(0x009AF51F, uint8)[2 * sprite->vehicle.var_BD] & 1)) { + uint16 frequency = (sound_unknown->frequency * 2) - 3248; + if (frequency > 25700) { + frequency = 25700; + } + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_set_frequency(&vehicle_sound->sound2, frequency); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } + } + } + label114: + 1; + } + } + } +} + /** * * rct2: 0x006D4204 diff --git a/src/vehicle.h b/src/vehicle.h index c80aec3ddf..fd290675a9 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -25,10 +25,10 @@ typedef union { struct { - uint16 width; - uint16 height; + sint16 width; + sint16 height; }; - uint32 both; + sint32 both; } rct_widthheight; typedef struct { @@ -48,28 +48,46 @@ typedef struct { rct_widthheight view; // 0x1A uint16 var_1E; uint8 pad_20[0x08]; - uint32 var_28; + sint32 var_28; uint8 pad_2C[0x04]; uint8 ride; // 0x30 uint8 var_31; - uint8 pad_32[0x0C]; + uint8 pad_32[0x02]; + uint16 var_34; + uint8 pad_36[0x08]; uint16 next_vehicle_on_train; // 0x3E - uint8 pad_40[0x08]; + uint32 var_40; + uint16 var_44; + uint16 var_46; uint16 var_48; uint8 pad_4A[0x06]; uint8 var_50; uint8 var_51; - uint8 pad_52[0x2E]; + uint16 peep; // 0x52 + uint8 pad_54[0x2C]; + uint16 var_80; + uint8 pad_82[0x31]; + uint8 var_B3; + uint8 pad_B4[0x07]; uint8 var_BB; uint8 var_BC; uint8 var_BD; - uint8 pad_BE[0x0E]; + uint8 var_BE; + sint8 var_BF; + uint8 pad_C0[0x02]; + uint8 speed; // 0xC2 + uint8 pad_C3[0x09]; uint8 var_CC; - uint8 pad_CD[0x09]; + uint8 var_CD; + uint8 var_CE; + uint8 pad_CF[0x07]; uint8 var_D6; } rct_vehicle; void vehicle_update_all(); +int sub_6BC2F3(rct_vehicle* vehicle); +void sub_6BB9FF(rct_vehicle* vehicle); +void vehicle_sounds_update(); /** Helper macro until rides are stored in this module. */ #define GET_VEHICLE(sprite_index) &(g_sprite_list[sprite_index].vehicle) diff --git a/src/window.c b/src/window.c index 99368a0aff..27961ed470 100644 --- a/src/window.c +++ b/src/window.c @@ -383,7 +383,7 @@ rct_window *window_create(int x, int y, int width, int height, uint32 *event_han // Play sounds and flash the window if (!(flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT))){ w->flags |= WF_WHITE_BORDER_MASK; - sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2)); + sound_play_panned(SOUND_WINDOW_OPEN, x + (width / 2), 0, 0, 0); } w->number = 0; diff --git a/src/window_footpath.c b/src/window_footpath.c index b2f06656d9..582f9198fe 100644 --- a/src/window_footpath.c +++ b/src/window_footpath.c @@ -730,7 +730,7 @@ static void window_footpath_place_path_at_point(int x, int y) // bp = 0x009DEA62 // dx = 0x009DEA60 // cx = 0x009DEA5E - sound_play_panned(SOUND_PLACE_ITEM, 0x8001); + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, RCT2_GLOBAL(0x009DEA5E, uint16), RCT2_GLOBAL(0x009DEA60, uint16), RCT2_GLOBAL(0x009DEA62, uint16)); } } diff --git a/src/window_new_ride.c b/src/window_new_ride.c index b643b3c6a9..2cadf1970f 100644 --- a/src/window_new_ride.c +++ b/src/window_new_ride.c @@ -658,7 +658,7 @@ static void window_new_ride_scrollmousedown() RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_RIDE_LIST_HIGHLIGHTED_ITEM, ride_list_item)[_window_new_ride_current_tab] = item; w->new_ride.selected_ride_id = *((sint16*)&item); - sound_play_panned(SOUND_CLICK_1, w->x + (w->width / 2)); + sound_play_panned(SOUND_CLICK_1, w->x + (w->width / 2), 0, 0, 0); w->new_ride.selected_ride_countdown = 8; window_invalidate(w); } diff --git a/src/window_news.c b/src/window_news.c index 9c431422e4..256ecb0493 100644 --- a/src/window_news.c +++ b/src/window_news.c @@ -150,7 +150,7 @@ static void window_news_update(rct_window *w) return; window_invalidate(w); - sound_play_panned(SOUND_CLICK_2, w->x + (w->width / 2)); + sound_play_panned(SOUND_CLICK_2, w->x + (w->width / 2), 0, 0, 0); newsItems = RCT2_ADDRESS(RCT2_ADDRESS_NEWS_ITEM_LIST, rct_news_item); j = w->news.var_480; @@ -255,7 +255,7 @@ static void window_news_scrollmousedown() w->news.var_482 = buttonIndex; w->news.var_484 = 4; window_invalidate(w); - sound_play_panned(SOUND_CLICK_1, w->x + (w->width / 2)); + sound_play_panned(SOUND_CLICK_1, w->x + (w->width / 2), 0, 0, 0); } } diff --git a/src/window_options.c b/src/window_options.c index e1bfc20c90..4df489aa0f 100644 --- a/src/window_options.c +++ b/src/window_options.c @@ -33,6 +33,7 @@ #include "config.h" #include "gfx.h" #include "language.h" +#include "mixer.h" #include "osinterface.h" #include "sprites.h" #include "string_ids.h" @@ -503,6 +504,9 @@ static void window_options_dropdown() switch (widgetIndex) { case WIDX_SOUND_DROPDOWN: audio_init2(dropdownIndex); + if (dropdownIndex < gAudioDeviceCount) { + Mixer_Init(gAudioDevices[dropdownIndex].name); + } /*#ifdef _MSC_VER __asm movzx ax, dropdownIndex #else diff --git a/src/window_scenery.c b/src/window_scenery.c index 3ed8744b9e..4ac4070765 100644 --- a/src/window_scenery.c +++ b/src/window_scenery.c @@ -774,7 +774,7 @@ void window_scenery_scrollmousedown() { window_scenery_selected_scenery_by_tab[tabIndex] = sceneryId; window_scenery_is_repaint_scenery_tool_on &= 0xFE; - sound_play_panned(4, (w->width >> 1) + w->x); + sound_play_panned(4, (w->width >> 1) + w->x, 0, 0, 0); w->scenery.hover_counter = -16; RCT2_GLOBAL(0x00F64EB4, uint32) = 0x80000000; window_invalidate(w); diff --git a/src/window_title_scenarioselect.c b/src/window_title_scenarioselect.c index 870a17f795..c55aeedce9 100644 --- a/src/window_title_scenarioselect.c +++ b/src/window_title_scenarioselect.c @@ -244,7 +244,7 @@ static void window_scenarioselect_scrollmousedown() if (y >= 0) continue; - sound_play_panned(SOUND_CLICK_1, w->width / 2 + w->x); + sound_play_panned(SOUND_CLICK_1, w->width / 2 + w->x, 0, 0, 0); scenario_load_and_play(scenario); break; }