From bf805057f2cd4b1cd8c29cb2cbe04d20803fc7ac Mon Sep 17 00:00:00 2001 From: zsilencer Date: Fri, 5 Sep 2014 19:29:22 -0600 Subject: [PATCH 1/9] A new custom software mixer, supports playing streams at any rate, which is needed to emulate the directsound SetFrequency effects (when a ride malfunctions and the music slows down and stops). The speex resampler is added for this. Currently samples can be played and their volumes and looping set on an arbitrary number of channels. Support for FLAC and other streaming formats can be added. --- libspeex/arch.h | 239 ++++++ libspeex/config.h | 20 + libspeex/os_support.h | 169 +++++ libspeex/resample.c | 1131 +++++++++++++++++++++++++++++ libspeex/speex/speex_resampler.h | 340 +++++++++ libspeex/speex/speex_types.h | 126 ++++ libspeex/stack_alloc.h | 115 +++ projects/openrct2.vcxproj | 9 +- projects/openrct2.vcxproj.filters | 9 + src/audio.c | 28 +- src/audio.h | 10 - src/mixer.cpp | 401 ++++++++++ src/mixer.h | 127 ++++ src/osinterface.h | 2 +- src/rct2.c | 2 + src/window_options.c | 4 + 16 files changed, 2704 insertions(+), 28 deletions(-) create mode 100644 libspeex/arch.h create mode 100644 libspeex/config.h create mode 100644 libspeex/os_support.h create mode 100644 libspeex/resample.c create mode 100644 libspeex/speex/speex_resampler.h create mode 100644 libspeex/speex/speex_types.h create mode 100644 libspeex/stack_alloc.h create mode 100644 src/mixer.cpp create mode 100644 src/mixer.h 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 3077811de3..9af8c93c1f 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -33,6 +33,7 @@ + @@ -63,6 +64,7 @@ + @@ -80,6 +82,7 @@ + @@ -186,7 +189,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)\ @@ -197,7 +200,7 @@ Disabled true 1Byte - _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;HAVE_CONFIG_H;_USE_MATH_DEFINES;%(PreprocessorDefinitions) true @@ -218,7 +221,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 59424e14b6..8f175ca65e 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -162,6 +162,9 @@ Header Files + + Header Files + @@ -386,6 +389,12 @@ Windows + + Source Files + + + Source Files + diff --git a/src/audio.c b/src/audio.c index fe46f41668..b366b5f9f9 100644 --- a/src/audio.c +++ b/src/audio.c @@ -215,14 +215,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 +466,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); } } @@ -531,7 +531,7 @@ MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmcki HMMIO hmmio1; MMRESULT result; MMCKINFO mmckinfo1; - rct_audio_info audio_info; + WAVEFORMATEX waveformat; hmemold = hmem; *hmem = 0; @@ -555,8 +555,8 @@ 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: @@ -566,7 +566,7 @@ MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmcki result = 57344; goto label20; } - memcpy(hmemold2, &audio_info, 16); + memcpy(hmemold2, &waveformat, 16); *((uint16*)*hmemold + 8) = (uint16)hmem; if (!(uint16)hmem || mmioRead(hmmio1, (char*)*hmemold + 18, (uint16)hmem) == (uint16)hmem) { result = mmioAscend(hmmio1, &mmckinfo1, 0); diff --git a/src/audio.h b/src/audio.h index c68b059e96..af0b1a9776 100644 --- a/src/audio.h +++ b/src/audio.h @@ -59,16 +59,6 @@ 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; diff --git a/src/mixer.cpp b/src/mixer.cpp new file mode 100644 index 0000000000..a7959e34cf --- /dev/null +++ b/src/mixer.cpp @@ -0,0 +1,401 @@ +/***************************************************************************** + * 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" + +Sample::Sample() +{ + data = 0; + length = 0; + issdlwav = false; +} + +Sample::~Sample() +{ + Unload(); +} + +bool Sample::Load(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, &length); + if (spec != NULL) { + format.freq = spec->freq; + format.format = spec->format; + format.channels = spec->channels; + } else { + issdlwav = true; + } + return true; +} + +bool Sample::LoadCSS1(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_S16; + format.channels = (Uint8)waveformat.nChannels; + data = new Uint8[length]; + SDL_RWread(rw, data, length, 1); + SDL_RWclose(rw); + return true; +} + +void Sample::Unload() +{ + SDL_LockAudio(); + if (data) { + if (issdlwav) { + SDL_FreeWAV(data); + } else { + delete[] data; + } + data = 0; + } + issdlwav = false; + length = 0; + SDL_UnlockAudio(); +} + +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 = 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; +} + +int Sample::Length() +{ + return length; +} + +Stream::Stream() +{ + sourcetype = SOURCE_NONE; +} + +int Stream::GetSome(int offset, const Uint8** data, int length) +{ + int 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; +} + +int 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 = 0) +{ + 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 (Channel::volume > SDL_MIX_MAXVOLUME) { + Channel::volume = SDL_MIX_MAXVOLUME; + } + if (Channel::volume < 0) { + Channel::volume = 0; + } +} + +void Mixer::Init(char* device) +{ + Close(); + SDL_AudioSpec want, have; + SDL_zero(want); + want.freq = 22050; + want.format = AUDIO_S16; + 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; + char css1filename[260]; + strcpy(css1filename, gGeneral_config.game_path); + strcat(css1filename, "\\Data\\css1.dat"); + for (int i = 0; i < 63; i++) { + css1samples[i].LoadCSS1(css1filename, 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]); + } + SDL_PauseAudioDevice(deviceid, 0); +} + +void Mixer::Close() +{ + SDL_CloseAudioDevice(deviceid); +} + +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; + 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; + if (mustconvert) { + Uint8* dataconverted; + if (Convert(cvt, datastream, readfromstream, &dataconverted)) { + if (channel.rate != 1 && format.format == AUDIO_S16) { + spx_uint32_t in_len = (spx_uint32_t)(ceil((double)cvt.len_cvt / samplesize)); + Uint8* out = new Uint8[length + 200]; // needs some extra, otherwise resampler sometimes doesn't process all the input samples + spx_uint32_t out_len = samples + 20; + 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*)dataconverted, &in_len, (spx_int16_t*)out, &out_len); + int mixlength = (out_len * samplesize); + if (loaded + mixlength > length) { // check for overflow + mixlength = length - loaded; + } + lengthloaded = (out_len * samplesize); + SDL_MixAudioFormat(&data[loaded], out, format.format, mixlength, volume); + delete[] out; + } else { + lengthloaded = (cvt.len_cvt / samplesize) * samplesize; + int mixlength = lengthloaded; + if (loaded + cvt.len_cvt > length) { + mixlength = length - loaded; + } + SDL_MixAudioFormat(&data[loaded], dataconverted, format.format, mixlength, volume); + } + delete[] dataconverted; + } + } else { + if (channel.rate != 1 && format.format == AUDIO_S16) { + spx_uint32_t in_len = (spx_uint32_t)(ceil((double)readfromstream / samplesize)); + Uint8* out = new Uint8[length + 200]; + spx_uint32_t out_len = samples + 20; + 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*)datastream, &in_len, (spx_int16_t*)out, &out_len); + int mixlength = (out_len * samplesize); + if (loaded + mixlength > length) { + mixlength = length - loaded; + } + lengthloaded = (out_len * samplesize); + SDL_MixAudioFormat(&data[loaded], out, format.format, mixlength, volume); + delete[] out; + } else { + lengthloaded = readfromstream; + int mixlength = lengthloaded; + if (loaded + readfromstream > length) { + mixlength = length - loaded; + } + SDL_MixAudioFormat(&data[loaded], datastream, format.format, mixlength, volume); + } + } + + 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 || (loaded < length && channel.loop != 0 && channel.offset == 0)); + } +} + +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, int length, Uint8** dataout) +{ + if (length == 0 || cvt.len_mult == 0) { + return false; + } + cvt.len = length; + cvt.buf = (Uint8*)new char[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(char* device) +{ + static Mixer mixer; + mixer.Init(device); +} \ No newline at end of file diff --git a/src/mixer.h b/src/mixer.h new file mode 100644 index 0000000000..9b38dce756 --- /dev/null +++ b/src/mixer.h @@ -0,0 +1,127 @@ +/***************************************************************************** + * 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" + +#ifdef __cplusplus + +extern "C" { +#include +} + +struct AudioFormat { + int BytesPerSample() const { return (SDL_AUDIO_BITSIZE(format)) / 8; }; + int freq; + SDL_AudioFormat format; + Uint8 channels; +}; + +class Sample +{ +public: + Sample(); + ~Sample(); + bool Load(char* filename); + bool LoadCSS1(char* filename, unsigned int offset); + void Unload(); + bool Convert(AudioFormat format); + const Uint8* Data(); + int Length(); + + friend class Stream; + +private: + AudioFormat format; + Uint8* data; + Uint32 length; + bool issdlwav; +}; + +class Stream +{ +public: + Stream(); + int GetSome(int offset, const Uint8** data, int length); + int 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); + + friend class Mixer; + +private: + int loop; + int offset; + double rate; + int volume; + SpeexResamplerState* resampler; + Stream* stream; +}; + +class Mixer +{ +public: + void Init(char* device); + void Close(); + +private: + static void SDLCALL Callback(void* arg, Uint8* data, int length); + void MixChannel(Channel& channel, Uint8* buffer, int length); + bool MustConvert(Stream& stream); + bool Convert(SDL_AudioCVT& cvt, const Uint8* data, int length, Uint8** dataout); + SDL_AudioDeviceID deviceid; + AudioFormat format; + Sample css1samples[63]; + Stream css1streams[63]; + Channel channels[10]; +}; + +extern "C" +{ +#endif + +void Mixer_Init(char* device); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/osinterface.h b/src/osinterface.h index a9480bb0db..ba2f91a7b8 100644 --- a/src/osinterface.h +++ b/src/osinterface.h @@ -74,7 +74,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; 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/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 From 3a4c4938a918c9a85d59acc3f4b5fa385e4c1860 Mon Sep 17 00:00:00 2001 From: zsilencer Date: Fri, 5 Sep 2014 19:56:42 -0600 Subject: [PATCH 2/9] cmake build --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 From 02cacd45c1864e6d076ecc1fc2fabe04df0432a1 Mon Sep 17 00:00:00 2001 From: zsilencer Date: Fri, 5 Sep 2014 19:56:42 -0600 Subject: [PATCH 3/9] cmake build --- CMakeLists.txt | 5 ++++- src/config.h | 2 +- src/mixer.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) 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/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/mixer.cpp b/src/mixer.cpp index a7959e34cf..f7541c12ad 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -362,7 +362,7 @@ void Mixer::MixChannel(Channel& channel, Uint8* data, int length) } channel.offset = 0; } - } while(loaded < length || (loaded < length && channel.loop != 0 && channel.offset == 0)); + } while(loaded < length && channel.loop != 0); } } From 2049a805408c0acc9755e667c5ca94abbc02ecbb Mon Sep 17 00:00:00 2001 From: zsilencer Date: Sat, 13 Sep 2014 11:51:58 -0600 Subject: [PATCH 4/9] 0x006BB9FF, 0x006BC2F3, mixer edit, completed 0x6BB76E --- src/audio.c | 113 ++++++++++++------ src/audio.h | 15 ++- src/input.c | 2 +- src/mixer.cpp | 191 ++++++++++++++++++------------ src/mixer.h | 36 +++--- src/news_item.c | 2 +- src/peep.c | 2 +- src/vehicle.c | 129 ++++++++++++++++++++ src/vehicle.h | 19 ++- src/window.c | 2 +- src/window_footpath.c | 2 +- src/window_new_ride.c | 2 +- src/window_news.c | 4 +- src/window_scenery.c | 2 +- src/window_title_scenarioselect.c | 2 +- 15 files changed, 375 insertions(+), 148 deletions(-) diff --git a/src/audio.c b/src/audio.c index b366b5f9f9..452600ca05 100644 --- a/src/audio.c +++ b/src/audio.c @@ -22,6 +22,7 @@ #include "audio.h" #include "addresses.h" #include "config.h" +#include "map.h" #include "rct2.h" #include "sprite.h" #include "viewport.h" @@ -1135,48 +1136,94 @@ int sound_prepare(int sound_id, rct_sound *sound, int channels, int software) * * rct2: 0x006BB76E */ -int sound_play_panned(int sound_id, int x) +int sound_play_panned(int sound_id, int ebx, uint16 x, uint16 y, uint16 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 - } - int i = 0; - rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; - while (other_sound->id != 0xFFFF) { - i++; - other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; - if (i > RCT2_GLOBAL(0x009AAC76, uint8)) { // too many sounds playing - return sound_id; + if (ebx == 0x8001) { + uint16 x2 = x & 0xFFE0; // round by 32 + uint16 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; + } + } + uint16 v11; + uint16 v12; + switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) { + case MAP_ELEMENT_DIRECTION_WEST: + v11 = x - y; + v12 = ((y + x) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_NORTH: + v11 = -y - x; + v12 = ((x - y) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_EAST: + v11 = y - x; + v12 = ((-x - y) / 2) - z; + break; + case MAP_ELEMENT_DIRECTION_SOUTH: + v11 = x + y; + v12 = ((y - x) / 2) - z; + break; + } + rct_window* window = RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*); + while (1) { + window--; + if (window < RCT2_GLOBAL(RCT2_ADDRESS_WINDOW_LIST, rct_window*)) { + break; + } + rct_viewport* viewport = window->viewport; + if (viewport && viewport->flags & VIEWPORT_FLAG_SOUND_ON) { + uint16 v15 = v12 - viewport->view_y; + uint16 v16 = v11 - viewport->view_x; + int x = viewport->x + (v16 / viewport->zoom); + volume = RCT2_GLOBAL(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; + } + } } - } - other_sound->id = sound_id; - int pan; - if (x == 0x8000) { - pan = 0; } else { - int x2 = x << 16; - uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); - if (screenwidth < 64) { - screenwidth = 64; + int i = 0; + rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; + while (other_sound->id != 0xFFFF) { + i++; + other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; + if (i > RCT2_GLOBAL(0x009AAC76, uint8)) { // too many sounds playing + return sound_id; + } + } + other_sound->id = sound_id; + int pan; + if (ebx == 0x8000) { + pan = 0; + } else { + int x2 = ebx << 16; + uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); + if (screenwidth < 64) { + screenwidth = 64; + } + pan = ((x2 / screenwidth) - 0x8000) >> 4; + } + if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { + pan = 0; } - pan = ((x2 / screenwidth) - 0x8000) >> 4; - } - if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { - pan = 0; - } - RCT2_GLOBAL(0x014241BC, uint32) = 1; - sound_prepare(sound_id, &other_sound->sound, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); - RCT2_GLOBAL(0x014241BC, uint32) = 0; - RCT2_GLOBAL(0x014241BC, uint32) = 1; - result = sound_play(&other_sound->sound, 0, volume, pan, 0); - RCT2_GLOBAL(0x014241BC, uint32) = 0; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_prepare(sound_id, &other_sound->sound, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + result = sound_play(&other_sound->sound, 0, volume, pan, 0); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + } } return result; } diff --git a/src/audio.h b/src/audio.h index af0b1a9776..6eae5cbf8b 100644 --- a/src/audio.h +++ b/src/audio.h @@ -108,12 +108,10 @@ typedef struct { typedef struct { uint16 id; - uint8 var_2; - uint8 var_3; - uint8 var_4; - uint16 var_5; - uint8 var_7; - uint16 var_8; + sint16 var_2; + sint16 var_4; + uint16 frequency; // 0x6 + sint16 var_8; uint16 next; // 0xA } rct_sound_unknown; @@ -131,7 +129,7 @@ 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, uint16 x, uint16 y, uint16 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); @@ -229,7 +227,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/input.c b/src/input.c index 725b6e9eb9..f2de817ffc 100644 --- a/src/input.c +++ b/src/input.c @@ -533,7 +533,7 @@ static void input_leftmousedown(int x, int y, rct_window *w, int widgetIndex) if (!widget_is_enabled(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; diff --git a/src/mixer.cpp b/src/mixer.cpp index f7541c12ad..da2ee5d606 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -28,6 +28,8 @@ extern "C" { } #include "mixer.h" +Mixer gMixer; + Sample::Sample() { data = 0; @@ -40,7 +42,7 @@ Sample::~Sample() Unload(); } -bool Sample::Load(char* filename) +bool Sample::Load(const char* filename) { Unload(); SDL_RWops* rw = SDL_RWFromFile(filename, "rb"); @@ -50,18 +52,19 @@ bool Sample::Load(char* filename) } SDL_AudioSpec audiospec; memset(&audiospec, 0, sizeof(audiospec)); - SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &data, &length); + 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; - } else { issdlwav = true; + } else { + return false; } return true; } -bool Sample::LoadCSS1(char* filename, unsigned int offset) +bool Sample::LoadCSS1(const char* filename, unsigned int offset) { Unload(); SDL_RWops* rw = SDL_RWFromFile(filename, "rb"); @@ -84,9 +87,9 @@ bool Sample::LoadCSS1(char* filename, unsigned int offset) WAVEFORMATEX waveformat; SDL_RWread(rw, &waveformat, sizeof(waveformat), 1); format.freq = waveformat.nSamplesPerSec; - format.format = AUDIO_S16; - format.channels = (Uint8)waveformat.nChannels; - data = new Uint8[length]; + format.format = AUDIO_S16LSB; + format.channels = waveformat.nChannels; + data = new uint8[length]; SDL_RWread(rw, data, length, 1); SDL_RWclose(rw); return true; @@ -94,7 +97,6 @@ bool Sample::LoadCSS1(char* filename, unsigned int offset) void Sample::Unload() { - SDL_LockAudio(); if (data) { if (issdlwav) { SDL_FreeWAV(data); @@ -105,7 +107,6 @@ void Sample::Unload() } issdlwav = false; length = 0; - SDL_UnlockAudio(); } bool Sample::Convert(AudioFormat format) @@ -116,7 +117,7 @@ bool Sample::Convert(AudioFormat format) return false; } cvt.len = length; - cvt.buf = new Uint8[cvt.len * cvt.len_mult]; + cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult]; memcpy(cvt.buf, data, length); if (SDL_ConvertAudio(&cvt) < 0) { delete[] cvt.buf; @@ -130,12 +131,12 @@ bool Sample::Convert(AudioFormat format) return true; } -const Uint8* Sample::Data() +const uint8* Sample::Data() { return data; } -int Sample::Length() +unsigned long Sample::Length() { return length; } @@ -145,9 +146,9 @@ Stream::Stream() sourcetype = SOURCE_NONE; } -int Stream::GetSome(int offset, const Uint8** data, int length) +unsigned long Stream::GetSome(unsigned long offset, const uint8** data, unsigned long length) { - int size = length; + unsigned long size = length; switch(sourcetype) { case SOURCE_SAMPLE: if (offset >= sample->Length()) { @@ -163,7 +164,7 @@ int Stream::GetSome(int offset, const Uint8** data, int length) return 0; } -int Stream::Length() +unsigned long Stream::Length() { switch(sourcetype) { case SOURCE_SAMPLE: @@ -222,21 +223,34 @@ void Channel::SetRate(double rate) void Channel::SetVolume(int volume) { Channel::volume = volume; - if (Channel::volume > SDL_MIX_MAXVOLUME) { + if (volume > SDL_MIX_MAXVOLUME) { Channel::volume = SDL_MIX_MAXVOLUME; } - if (Channel::volume < 0) { + if (volume < 0) { Channel::volume = 0; } } -void Mixer::Init(char* device) +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 = 22050; - want.format = AUDIO_S16; + want.freq = 44100; + want.format = AUDIO_S16SYS; want.channels = 2; want.samples = 1024; want.callback = Callback; @@ -245,23 +259,23 @@ void Mixer::Init(char* device) format.format = have.format; format.channels = have.channels; format.freq = have.freq; - char css1filename[260]; - strcpy(css1filename, gGeneral_config.game_path); - strcat(css1filename, "\\Data\\css1.dat"); - for (int i = 0; i < 63; i++) { - css1samples[i].LoadCSS1(css1filename, i); + 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) +void SDLCALL Mixer::Callback(void* arg, uint8* stream, int length) { Mixer* mixer = (Mixer*)arg; memset(stream, 0, length); @@ -270,7 +284,7 @@ void SDLCALL Mixer::Callback(void* arg, Uint8* stream, int length) } } -void Mixer::MixChannel(Channel& channel, Uint8* data, int length) +void Mixer::MixChannel(Channel& channel, uint8* data, int length) { if (channel.stream) { if (!channel.resampler) { @@ -294,60 +308,66 @@ void Mixer::MixChannel(Channel& channel, Uint8* data, int length) } mustconvert = true; } - const Uint8* datastream; + + 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) { - Uint8* dataconverted; if (Convert(cvt, datastream, readfromstream, &dataconverted)) { - if (channel.rate != 1 && format.format == AUDIO_S16) { - spx_uint32_t in_len = (spx_uint32_t)(ceil((double)cvt.len_cvt / samplesize)); - Uint8* out = new Uint8[length + 200]; // needs some extra, otherwise resampler sometimes doesn't process all the input samples - spx_uint32_t out_len = samples + 20; - 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*)dataconverted, &in_len, (spx_int16_t*)out, &out_len); - int mixlength = (out_len * samplesize); - if (loaded + mixlength > length) { // check for overflow - mixlength = length - loaded; - } - lengthloaded = (out_len * samplesize); - SDL_MixAudioFormat(&data[loaded], out, format.format, mixlength, volume); - delete[] out; - } else { - lengthloaded = (cvt.len_cvt / samplesize) * samplesize; - int mixlength = lengthloaded; - if (loaded + cvt.len_cvt > length) { - mixlength = length - loaded; - } - SDL_MixAudioFormat(&data[loaded], dataconverted, format.format, mixlength, volume); - } - delete[] dataconverted; + tomix = dataconverted; + lengthloaded = (cvt.len_cvt / samplesize) * samplesize; + } else { + break; } } else { - if (channel.rate != 1 && format.format == AUDIO_S16) { - spx_uint32_t in_len = (spx_uint32_t)(ceil((double)readfromstream / samplesize)); - Uint8* out = new Uint8[length + 200]; - spx_uint32_t out_len = samples + 20; - 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*)datastream, &in_len, (spx_int16_t*)out, &out_len); - int mixlength = (out_len * samplesize); - if (loaded + mixlength > length) { - mixlength = length - loaded; - } - lengthloaded = (out_len * samplesize); - SDL_MixAudioFormat(&data[loaded], out, format.format, mixlength, volume); - delete[] out; - } else { - lengthloaded = readfromstream; - int mixlength = lengthloaded; - if (loaded + readfromstream > length) { - mixlength = length - loaded; - } - SDL_MixAudioFormat(&data[loaded], datastream, format.format, mixlength, volume); + 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; @@ -366,6 +386,26 @@ void Mixer::MixChannel(Channel& channel, Uint8* data, int length) } } +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(); @@ -378,13 +418,13 @@ bool Mixer::MustConvert(Stream& stream) return false; } -bool Mixer::Convert(SDL_AudioCVT& cvt, const Uint8* data, int length, Uint8** dataout) +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 char[cvt.len * cvt.len_mult]; + cvt.buf = (Uint8*)new uint8[cvt.len * cvt.len_mult]; memcpy(cvt.buf, data, length); if (SDL_ConvertAudio(&cvt) < 0) { delete[] cvt.buf; @@ -394,8 +434,7 @@ bool Mixer::Convert(SDL_AudioCVT& cvt, const Uint8* data, int length, Uint8** da return true; } -void Mixer_Init(char* device) +void Mixer_Init(const char* device) { - static Mixer mixer; - mixer.Init(device); + gMixer.Init(device); } \ No newline at end of file diff --git a/src/mixer.h b/src/mixer.h index 9b38dce756..353cbd1213 100644 --- a/src/mixer.h +++ b/src/mixer.h @@ -33,7 +33,7 @@ struct AudioFormat { int BytesPerSample() const { return (SDL_AUDIO_BITSIZE(format)) / 8; }; int freq; SDL_AudioFormat format; - Uint8 channels; + int channels; }; class Sample @@ -41,19 +41,19 @@ class Sample public: Sample(); ~Sample(); - bool Load(char* filename); - bool LoadCSS1(char* filename, unsigned int offset); + bool Load(const char* filename); + bool LoadCSS1(const char* filename, unsigned int offset); void Unload(); bool Convert(AudioFormat format); - const Uint8* Data(); - int Length(); + const uint8* Data(); + unsigned long Length(); friend class Stream; private: AudioFormat format; - Uint8* data; - Uint32 length; + uint8* data; + unsigned long length; bool issdlwav; }; @@ -61,8 +61,8 @@ class Stream { public: Stream(); - int GetSome(int offset, const Uint8** data, int length); - int Length(); + unsigned long GetSome(unsigned long offset, const uint8** data, unsigned long length); + unsigned long Length(); void SetSource_Sample(Sample& sample); const AudioFormat* Format(); @@ -84,14 +84,17 @@ public: void Play(Stream& stream, int loop); void SetRate(double rate); void SetVolume(int volume); + void SetPan(float pan); friend class Mixer; private: int loop; - int offset; + unsigned long offset; double rate; int volume; + float volume_l, volume_r; + float pan; SpeexResamplerState* resampler; Stream* stream; }; @@ -99,16 +102,19 @@ private: class Mixer { public: - void Init(char* device); + 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); + 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, int length, Uint8** dataout); + bool Convert(SDL_AudioCVT& cvt, const uint8* data, unsigned long length, uint8** dataout); SDL_AudioDeviceID deviceid; AudioFormat format; + uint8* effectbuffer; Sample css1samples[63]; Stream css1streams[63]; Channel channels[10]; @@ -118,7 +124,7 @@ extern "C" { #endif -void Mixer_Init(char* device); +void Mixer_Init(const char* device); #ifdef __cplusplus } diff --git a/src/news_item.c b/src/news_item.c index 986502d1fe..bc92fdf62c 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/peep.c b/src/peep.c index da7c594544..660665e01e 100644 --- a/src/peep.c +++ b/src/peep.c @@ -393,7 +393,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); } /** diff --git a/src/vehicle.c b/src/vehicle.c index 2bf9f92029..0141712582 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -19,11 +19,140 @@ *****************************************************************************/ #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 + 1) = *j; + 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->var_2 = (((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: 0x006D4204 diff --git a/src/vehicle.h b/src/vehicle.h index 046d719a34..9257ec63dd 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,35 @@ 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]; 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 var_80; + uint8 pad_82[0x39]; uint8 var_BB; uint8 var_BC; uint8 var_BD; - uint8 pad_BE[0x0E]; + uint8 var_BE; + sint8 var_BF; + uint8 pad_C0[0x0C]; uint8 var_CC; uint8 pad_CD[0x09]; uint8 var_D6; } rct_vehicle; void vehicle_update_all(); +int sub_6BC2F3(rct_vehicle* vehicle); /** 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 af22ebfc4a..89fb8d34cb 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 1e70c75998..784768cea5 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 0c8585f1ab..8e1f5f2f69 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_scenery.c b/src/window_scenery.c index 17c8ee2b2b..d39c96f1cb 100644 --- a/src/window_scenery.c +++ b/src/window_scenery.c @@ -777,7 +777,7 @@ void window_scenery_scrollmousedown() { RCT2_ADDRESS(0x00F64EDD, sint16)[tabIndex] = sceneryId; RCT2_GLOBAL(0x00F64F19, uint8) &= 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 38246736e7..6a1214b68d 100644 --- a/src/window_title_scenarioselect.c +++ b/src/window_title_scenarioselect.c @@ -243,7 +243,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; } From 298de189f5ef694fa457385cb91b542512dd1c42 Mon Sep 17 00:00:00 2001 From: zsilencer Date: Mon, 15 Sep 2014 15:12:25 -0600 Subject: [PATCH 5/9] 0x006BBC6B, hook --- projects/openrct2.vcxproj | 2 + projects/openrct2.vcxproj.filters | 16 ++ src/audio.c | 4 +- src/audio.h | 22 +-- src/game.c | 2 +- src/mixer.h | 4 +- src/vehicle.c | 308 +++++++++++++++++++++++++++++- src/vehicle.h | 6 +- 8 files changed, 344 insertions(+), 20 deletions(-) diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj index 52893c100c..d956ad6eed 100644 --- a/projects/openrct2.vcxproj +++ b/projects/openrct2.vcxproj @@ -28,6 +28,7 @@ + @@ -79,6 +80,7 @@ + diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters index 15753911e3..8a747533d6 100644 --- a/projects/openrct2.vcxproj.filters +++ b/projects/openrct2.vcxproj.filters @@ -168,6 +168,12 @@ Header Files + + Header Files + + + Header Files + @@ -398,6 +404,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -426,5 +441,6 @@ Data\Language + \ No newline at end of file diff --git a/src/audio.c b/src/audio.c index 212a27a1d9..36a3eb2fc6 100644 --- a/src/audio.c +++ b/src/audio.c @@ -1586,12 +1586,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; diff --git a/src/audio.h b/src/audio.h index 33f3fc3264..018e3b0f9f 100644 --- a/src/audio.h +++ b/src/audio.h @@ -90,15 +90,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 { @@ -108,11 +108,11 @@ typedef struct { typedef struct { uint16 id; - sint16 var_2; + sint16 pan; // 0x2 sint16 var_4; - uint16 frequency; // 0x6 + uint16 frequency; // 0x6 sint16 var_8; - uint16 next; // 0xA + uint16 next; // 0xA } rct_sound_unknown; int get_dsound_devices(); diff --git a/src/game.c b/src/game.c index 621ba79b3c..46e0ac7954 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/mixer.h b/src/mixer.h index 353cbd1213..8def3f987d 100644 --- a/src/mixer.h +++ b/src/mixer.h @@ -115,8 +115,8 @@ private: SDL_AudioDeviceID deviceid; AudioFormat format; uint8* effectbuffer; - Sample css1samples[63]; - Stream css1streams[63]; + Sample css1samples[SOUND_MAXID]; + Stream css1streams[SOUND_MAXID]; Channel channels[10]; }; diff --git a/src/vehicle.c b/src/vehicle.c index 0141712582..070ae41baa 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -63,8 +63,8 @@ void sub_6BB9FF(rct_vehicle* vehicle) } rct_sound_unknown* j = RCT2_GLOBAL(0x00F438B0, rct_sound_unknown*) - 1; while (j >= i) { - *(j + 1) = *j; j--; + *(j + 1) = *j; } i->next = v9; rct_widthheight v12; @@ -78,7 +78,7 @@ void sub_6BB9FF(rct_vehicle* vehicle) v14 = 64; } rct_widthheight v15; - i->var_2 = (((v12.width << 16) / v14) - 0x8000) >> 4; + 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; @@ -88,7 +88,7 @@ void sub_6BB9FF(rct_vehicle* vehicle) if (v18 < 64) { v18 = 64; } - i->var_4 = (sint16)((v15.both / v18) - 0x8000) >> 4; + i->var_4 = (sint16)(((v15.both / v18) - 0x8000) >> 4); sint32 v19 = vehicle->var_28; int testaddr = (vehicle->var_31 * 0x65); @@ -153,6 +153,308 @@ int sub_6BC2F3(rct_vehicle* vehicle) 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(0x00F438A4, rct_viewport*) = window->viewport; + 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; + (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; + (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)); + } + result = MAKEWORD(LOBYTE(result) - RCT2_GLOBAL(0x00F438AC, uint8), HIBYTE(result)); + if (!LOBYTE(result)) { + result = MAKEWORD(0, 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)) { + (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; + result = MAKEWORD(LOBYTE(result) - v22, HIBYTE(result)); + if (!result) { + result = MAKEWORD(0, 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); + (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; + 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); + (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 3de3a1215a..4fe38186b7 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -52,7 +52,9 @@ typedef struct { 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 uint32 var_40; uint16 var_44; @@ -77,6 +79,8 @@ typedef struct { 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) From 33ae29b11feeaf10fdf1dcf4a68699f6d2a10516 Mon Sep 17 00:00:00 2001 From: zsilencer Date: Mon, 15 Sep 2014 15:14:06 -0600 Subject: [PATCH 6/9] hook --- src/hook.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hook.h | 41 +++++++++++ 2 files changed, 242 insertions(+) create mode 100644 src/hook.c create mode 100644 src/hook.h 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..ee4ccd3c73 --- /dev/null +++ b/src/hook.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * 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_ + +extern void* g_hooktableaddress; +extern int g_hooktableoffset; +extern int g_maxhooks; + +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 From d87ede04a4627f4366c076a453ddd306643ea79c Mon Sep 17 00:00:00 2001 From: zsilencer Date: Mon, 15 Sep 2014 15:19:12 -0600 Subject: [PATCH 7/9] gcc fix? --- src/vehicle.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vehicle.c b/src/vehicle.c index 070ae41baa..f931c0df2e 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -226,7 +226,7 @@ void vehicle_sounds_update() if (v > 0) { v -= 0x400; v = -v; - (uint16)v /= 4; + v = (uint16)v / 4; result = MAKEWORD(LOBYTE(v), HIBYTE(result)); if (HIBYTE(v) != 0) { result = MAKEWORD(0xFF, HIBYTE(result)); @@ -247,7 +247,7 @@ void vehicle_sounds_update() if (w > 0) { w -= 0x400; w = -w; - (uint16)w /= 4; + w = (uint16)w / 4; result = MAKEWORD(LOBYTE(result), LOBYTE(w)); if (HIBYTE(w) != 0) { result = MAKEWORD(LOBYTE(result), 0xFF); @@ -275,7 +275,7 @@ void vehicle_sounds_update() vehicle_sound++; i++; if (i >= RCT2_GLOBAL(0x009AAC75, sint8)) { - (int)sound_unknown += sound_unknown->next; + sound_unknown = (rct_sound_unknown*)((int)sound_unknown + sound_unknown->next); goto label28; } } @@ -306,7 +306,7 @@ void vehicle_sounds_update() rct_sprite* sprite = &g_sprite_list[sound_unknown->id]; sint16 volume = sprite->vehicle.var_BC; volume *= LOBYTE(result); - (uint16)volume /= 8; + volume = (uint16)volume / 8; volume -= 0x1FFF; if (volume < -10000) { volume = -10000; @@ -374,7 +374,7 @@ void vehicle_sounds_update() sprite = &g_sprite_list[sound_unknown->id]; volume = sprite->vehicle.var_BE; volume *= LOBYTE(result); - (uint16)volume /= 8; + volume = (uint16)volume / 8; volume -= 0x1FFF; if (volume < -10000) { volume = -10000; From d276e40b7f6217068274588632d906a2b630a10b Mon Sep 17 00:00:00 2001 From: zsilencer Date: Wed, 17 Sep 2014 13:44:29 -0600 Subject: [PATCH 8/9] 0x00401AF3, 0x006BC6D8, corrections --- src/audio.c | 193 ++++++++++++++++++++++++++++++++++++++++++++-- src/audio.h | 36 ++++++++- src/hook.h | 4 - src/osinterface.c | 8 +- src/osinterface.h | 5 ++ src/vehicle.c | 145 +++++++++++++++++----------------- 6 files changed, 302 insertions(+), 89 deletions(-) diff --git a/src/audio.c b/src/audio.c index 36a3eb2fc6..3b1887eb0c 100644 --- a/src/audio.c +++ b/src/audio.c @@ -23,6 +23,7 @@ #include "addresses.h" #include "config.h" #include "map.h" +#include "osinterface.h" #include "rct2.h" #include "sprite.h" #include "viewport.h" @@ -480,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; @@ -525,7 +541,7 @@ 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; @@ -536,7 +552,7 @@ MMRESULT mmio_open(char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO mmcki 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) { @@ -1245,7 +1261,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; @@ -1687,4 +1703,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 018e3b0f9f..30c37853fe 100644 --- a/src/audio.h +++ b/src/audio.h @@ -62,7 +62,8 @@ typedef struct rct_sound { 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; @@ -115,15 +116,42 @@ typedef struct { 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); @@ -139,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(); diff --git a/src/hook.h b/src/hook.h index ee4ccd3c73..fed10792f3 100644 --- a/src/hook.h +++ b/src/hook.h @@ -21,10 +21,6 @@ #ifndef _HOOK_H_ #define _HOOK_H_ -extern void* g_hooktableaddress; -extern int g_hooktableoffset; -extern int g_maxhooks; - enum REGISTER_ARGS { EAX = 1 << 0, EBX = 1 << 1, 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 ba2f91a7b8..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 { @@ -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/vehicle.c b/src/vehicle.c index f931c0df2e..925025481e 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -173,7 +173,6 @@ void vehicle_sounds_update() RCT2_GLOBAL(0x00F438A4, rct_viewport*) = viewport; if (viewport != (rct_viewport*)-1) { if (window) { - RCT2_GLOBAL(0x00F438A4, rct_viewport*) = window->viewport; RCT2_GLOBAL(0x00F438A8, rct_window*) = window; RCT2_GLOBAL(0x00F438AC, uint8) = 0; if (viewport->zoom) { @@ -356,9 +355,11 @@ void vehicle_sounds_update() } if (sound_unknown->pan != vehicle_sound->sound1_pan) { vehicle_sound->sound1_pan = sound_unknown->pan; - RCT2_GLOBAL(0x014241BC, uint32) = 1; - sound_set_pan(&vehicle_sound->sound1, sound_unknown->pan); - RCT2_GLOBAL(0x014241BC, uint32) = 0; + 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; @@ -370,86 +371,86 @@ void vehicle_sounds_update() 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; + } + 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; } - if (sprite->vehicle.var_BD == (uint8)-1) { - if (vehicle_sound->sound2_id != (uint16)-1) { - vehicle_sound->sound2_id = -1; + } 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_stop(&vehicle_sound->sound2); + sound_set_pan(&vehicle_sound->sound2, sound_unknown->pan); 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 (!(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; } - 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); + sound_set_frequency(&vehicle_sound->sound2, frequency); 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; } } + label114: + 1; } } } From 961e15123283cf7dcaf310b6998decd4b110c29d Mon Sep 17 00:00:00 2001 From: zsilencer Date: Thu, 18 Sep 2014 20:48:04 -0600 Subject: [PATCH 9/9] 0x0069A5A0, 0x006AC988, couple fixes --- src/addresses.h | 2 + src/audio.c | 109 +++++++++++++++++++++++++----------------------- src/audio.h | 2 +- src/mixer.cpp | 2 +- src/mixer.h | 3 ++ src/peep.c | 34 +++++++++++++++ src/peep.h | 1 + src/ride.c | 49 ++++++++++++++++++++++ src/ride.h | 16 ++++--- src/vehicle.c | 10 +++-- src/vehicle.h | 15 +++++-- 11 files changed, 176 insertions(+), 67 deletions(-) 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 3b1887eb0c..9125c36b84 100644 --- a/src/audio.c +++ b/src/audio.c @@ -577,15 +577,15 @@ MMRESULT mmio_open(const char* filename, HMMIO* hmmio, HGLOBAL* hmem, LPMMCKINFO //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, &waveformat, 16); - *((uint16*)*hmemold + 8) = (uint16)hmem; - if (!(uint16)hmem || mmioRead(hmmio1, (char*)*hmemold + 18, (uint16)hmem) == (uint16)hmem) { + *((uint16*)*hmemold + 8) = hmem; + if (!hmem || mmioRead(hmmio1, (char*)*hmemold + 18, hmem) == hmem) { result = mmioAscend(hmmio1, &mmckinfo1, 0); if (!result) { goto label24; @@ -1151,16 +1151,22 @@ 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 ebx, uint16 x, uint16 y, uint16 z) +int sound_play_panned(int sound_id, int ebx, sint16 x, sint16 y, sint16 z) { int result = 0; if (RCT2_GLOBAL(0x009AF59D, uint8) & 1) { RCT2_GLOBAL(0x00F438AD, uint8) = 0; int volume = 0; if (ebx == 0x8001) { - uint16 x2 = x & 0xFFE0; // round by 32 - uint16 y2 = y & 0xFFE0; + 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) { @@ -1170,76 +1176,75 @@ int sound_play_panned(int sound_id, int ebx, uint16 x, uint16 y, uint16 z) RCT2_GLOBAL(0x00F438AD, uint8) = 10; } } - uint16 v11; - uint16 v12; + sint16 v11; + sint16 v12; switch (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint32)) { case MAP_ELEMENT_DIRECTION_WEST: - v11 = x - y; + v11 = y - x; v12 = ((y + x) / 2) - z; break; case MAP_ELEMENT_DIRECTION_NORTH: - v11 = -y - x; - v12 = ((x - y) / 2) - z; + v11 = -x - y; + v12 = ((y - x) / 2) - z; break; case MAP_ELEMENT_DIRECTION_EAST: - v11 = y - x; - v12 = ((-x - y) / 2) - z; + v11 = x - y; + v12 = ((-y - x) / 2) - z; break; case MAP_ELEMENT_DIRECTION_SOUTH: - v11 = x + y; - v12 = ((y - x) / 2) - z; + 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_GLOBAL(RCT2_ADDRESS_WINDOW_LIST, rct_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) { - uint16 v15 = v12 - viewport->view_y; - uint16 v16 = v11 - viewport->view_x; - int x = viewport->x + (v16 / viewport->zoom); - volume = RCT2_GLOBAL(0x0099282C, int*)[sound_id] + ((-1024 * viewport->zoom - 1) << RCT2_GLOBAL(0x00F438AD, uint8)) + 1; + 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; } } } - } else { - int i = 0; - rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; - while (other_sound->id != 0xFFFF) { - i++; - other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; - if (i > RCT2_GLOBAL(0x009AAC76, uint8)) { // too many sounds playing - return sound_id; - } - } - other_sound->id = sound_id; - int pan; - if (ebx == 0x8000) { - pan = 0; - } else { - int x2 = ebx << 16; - uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); - if (screenwidth < 64) { - screenwidth = 64; - } - pan = ((x2 / screenwidth) - 0x8000) >> 4; - } - if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { - pan = 0; - } - - RCT2_GLOBAL(0x014241BC, uint32) = 1; - sound_prepare(sound_id, &other_sound->sound, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); - RCT2_GLOBAL(0x014241BC, uint32) = 0; - RCT2_GLOBAL(0x014241BC, uint32) = 1; - result = sound_play(&other_sound->sound, 0, volume, pan, 0); - RCT2_GLOBAL(0x014241BC, uint32) = 0; } + int i = 0; + rct_other_sound* other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; + while (other_sound->id != 0xFFFF) { + i++; + other_sound = &RCT2_ADDRESS(0x009AF484, rct_other_sound)[i]; + if (i > RCT2_GLOBAL(0x009AAC76, uint8)) { // too many sounds playing + return sound_id; + } + } + other_sound->id = sound_id; + int pan; + if (ebx == 0x8000) { + pan = 0; + } else { + int x2 = ebx << 16; + uint16 screenwidth = RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16); + if (screenwidth < 64) { + screenwidth = 64; + } + pan = ((x2 / screenwidth) - 0x8000) >> 4; + } + if (!RCT2_GLOBAL(0x009AAC6D, uint8)) { + pan = 0; + } + + RCT2_GLOBAL(0x014241BC, uint32) = 1; + sound_prepare(sound_id, &other_sound->sound, 1, RCT2_GLOBAL(RCT2_ADDRESS_CONFIG_SOUND_SW_BUFFER, uint32)); + RCT2_GLOBAL(0x014241BC, uint32) = 0; + RCT2_GLOBAL(0x014241BC, uint32) = 1; + result = sound_play(&other_sound->sound, 0, volume, pan, 0); + RCT2_GLOBAL(0x014241BC, uint32) = 0; } return result; } diff --git a/src/audio.h b/src/audio.h index 30c37853fe..278f6bd317 100644 --- a/src/audio.h +++ b/src/audio.h @@ -157,7 +157,7 @@ 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 ebx, uint16 x, uint16 y, uint16 z); +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); diff --git a/src/mixer.cpp b/src/mixer.cpp index da2ee5d606..baabda9f85 100644 --- a/src/mixer.cpp +++ b/src/mixer.cpp @@ -205,7 +205,7 @@ Channel::~Channel() } } -void Channel::Play(Stream& stream, int loop = 0) +void Channel::Play(Stream& stream, int loop = MIXER_LOOP_NONE) { Channel::stream = &stream; Channel::loop = loop; diff --git a/src/mixer.h b/src/mixer.h index 8def3f987d..b2b453034b 100644 --- a/src/mixer.h +++ b/src/mixer.h @@ -23,6 +23,9 @@ #include "rct2.h" +#define MIXER_LOOP_NONE 0 +#define MIXER_LOOP_INFINITE -1 + #ifdef __cplusplus extern "C" { diff --git a/src/peep.c b/src/peep.c index 9ae3326cb9..a7d12f6840 100644 --- a/src/peep.c +++ b/src/peep.c @@ -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; +} \ No newline at end of file diff --git a/src/peep.h b/src/peep.h index c9285a2fc9..5e914de910 100644 --- a/src/peep.h +++ b/src/peep.h @@ -449,6 +449,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/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 925025481e..6cd47e648a 100644 --- a/src/vehicle.c +++ b/src/vehicle.c @@ -259,9 +259,10 @@ void vehicle_sounds_update() if (LOBYTE(result) >= HIBYTE(result)) { result = MAKEWORD(HIBYTE(result), HIBYTE(result)); } - result = MAKEWORD(LOBYTE(result) - RCT2_GLOBAL(0x00F438AC, uint8), HIBYTE(result)); - if (!LOBYTE(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); @@ -296,9 +297,10 @@ void vehicle_sounds_update() } } vehicle_sound->var_2 = v22; - result = MAKEWORD(LOBYTE(result) - v22, HIBYTE(result)); - if (!result) { + 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; diff --git a/src/vehicle.h b/src/vehicle.h index 4fe38186b7..fd290675a9 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -63,17 +63,24 @@ typedef struct { 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[0x39]; + uint8 pad_82[0x31]; + uint8 var_B3; + uint8 pad_B4[0x07]; uint8 var_BB; uint8 var_BC; uint8 var_BD; uint8 var_BE; sint8 var_BF; - uint8 pad_C0[0x0C]; + 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;