From f72500dd0913f8b47958bf5556820c02d1b32fa3 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 08:55:44 +0100 Subject: [PATCH 1/8] Update kissfft to v131.1 --- cmake/SfizzDeps.cmake | 6 +- common.mk | 5 +- src/external/kiss_fft/_kiss_fft_guts.h | 89 ++++++++++--------- src/external/kiss_fft/kiss_fft.c | 62 +++++++------ src/external/kiss_fft/kiss_fft.h | 82 ++++++++++++----- src/external/kiss_fft/kiss_fft_log.h | 36 ++++++++ src/external/kiss_fft/{tools => }/kiss_fftr.c | 44 +++++---- src/external/kiss_fft/kiss_fftr.h | 54 +++++++++++ src/external/kiss_fft/tools/kiss_fftr.h | 46 ---------- 9 files changed, 256 insertions(+), 168 deletions(-) create mode 100644 src/external/kiss_fft/kiss_fft_log.h rename src/external/kiss_fft/{tools => }/kiss_fftr.c (68%) create mode 100644 src/external/kiss_fft/kiss_fftr.h delete mode 100644 src/external/kiss_fft/tools/kiss_fftr.h diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index 85c9dbae..44a87134 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -203,10 +203,8 @@ if (SFIZZ_USE_SYSTEM_KISS_FFT) else() add_library(sfizz_kissfft STATIC "src/external/kiss_fft/kiss_fft.c" - "src/external/kiss_fft/tools/kiss_fftr.c") - target_include_directories(sfizz_kissfft - PUBLIC "src/external/kiss_fft" - PUBLIC "src/external/kiss_fft/tools") + "src/external/kiss_fft/kiss_fftr.c") + target_include_directories(sfizz_kissfft PUBLIC "src/external/kiss_fft") endif() add_library(sfizz::kissfft ALIAS sfizz_kissfft) diff --git a/common.mk b/common.mk index 24a261a8..3526e33c 100644 --- a/common.mk +++ b/common.mk @@ -339,11 +339,10 @@ SFIZZ_SOURCES += src/external/pugixml/src/pugixml.cpp ### Kissfft dependency SFIZZ_C_FLAGS += \ - -I$(SFIZZ_DIR)/src/external/kiss_fft \ - -I$(SFIZZ_DIR)/src/external/kiss_fft/tools + -I$(SFIZZ_DIR)/src/external/kiss_fft SFIZZ_SOURCES += \ src/external/kiss_fft/kiss_fft.c \ - src/external/kiss_fft/tools/kiss_fftr.c + src/external/kiss_fft/kiss_fftr.c ### Surge tuning library dependency diff --git a/src/external/kiss_fft/_kiss_fft_guts.h b/src/external/kiss_fft/_kiss_fft_guts.h index ba661444..4bd8d1c1 100644 --- a/src/external/kiss_fft/_kiss_fft_guts.h +++ b/src/external/kiss_fft/_kiss_fft_guts.h @@ -1,26 +1,25 @@ /* -Copyright (c) 2003-2010, Mark Borgerding - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the author nor the names of any 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 COPYRIGHT OWNER 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. -*/ + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ /* kiss_fft.h defines kiss_fft_scalar as either short or a float type and defines typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */ + +#ifndef _kiss_fft_guts_h +#define _kiss_fft_guts_h + #include "kiss_fft.h" +#include "kiss_fft_log.h" #include #define MAXFACTORS 32 -/* e.g. an fft of length 128 has 4 factors +/* e.g. an fft of length 128 has 4 factors as far as kissfft is concerned 4*4*4*2 */ @@ -42,22 +41,23 @@ struct kiss_fft_state{ C_ADDTO( res , a) : res += a * */ #ifdef FIXED_POINT +#include #if (FIXED_POINT==32) # define FRACBITS 31 # define SAMPPROD int64_t -#define SAMP_MAX 2147483647 +#define SAMP_MAX INT32_MAX +#define SAMP_MIN INT32_MIN #else # define FRACBITS 15 -# define SAMPPROD int32_t -#define SAMP_MAX 32767 +# define SAMPPROD int32_t +#define SAMP_MAX INT16_MAX +#define SAMP_MIN INT16_MIN #endif -#define SAMP_MIN -SAMP_MAX - #if defined(CHECK_OVERFLOW) # define CHECK_OVERFLOW_OP(a,op,b) \ - if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ - fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) ); } + if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \ + KISS_FFT_WARNING("overflow (%d " #op" %d) = %ld", (a),(b),(SAMPPROD)(a) op (SAMPPROD)(b)); } #endif @@ -71,11 +71,11 @@ struct kiss_fft_state{ (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0) # define DIVSCALAR(x,k) \ - (x) = sround( smul( x, SAMP_MAX/k ) ) + (x) = sround( smul( x, SAMP_MAX/k ) ) # define C_FIXDIV(c,div) \ - do { DIVSCALAR( (c).r , div); \ - DIVSCALAR( (c).i , div); }while (0) + do { DIVSCALAR( (c).r , div); \ + DIVSCALAR( (c).i , div); }while (0) # define C_MULBYSCALAR( c, s ) \ do{ (c).r = sround( smul( (c).r , s ) ) ;\ @@ -99,28 +99,28 @@ struct kiss_fft_state{ #define C_ADD( res, a,b)\ do { \ - CHECK_OVERFLOW_OP((a).r,+,(b).r)\ - CHECK_OVERFLOW_OP((a).i,+,(b).i)\ - (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ + CHECK_OVERFLOW_OP((a).r,+,(b).r)\ + CHECK_OVERFLOW_OP((a).i,+,(b).i)\ + (res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \ }while(0) #define C_SUB( res, a,b)\ do { \ - CHECK_OVERFLOW_OP((a).r,-,(b).r)\ - CHECK_OVERFLOW_OP((a).i,-,(b).i)\ - (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ + CHECK_OVERFLOW_OP((a).r,-,(b).r)\ + CHECK_OVERFLOW_OP((a).i,-,(b).i)\ + (res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \ }while(0) #define C_ADDTO( res , a)\ do { \ - CHECK_OVERFLOW_OP((res).r,+,(a).r)\ - CHECK_OVERFLOW_OP((res).i,+,(a).i)\ - (res).r += (a).r; (res).i += (a).i;\ + CHECK_OVERFLOW_OP((res).r,+,(a).r)\ + CHECK_OVERFLOW_OP((res).i,+,(a).i)\ + (res).r += (a).r; (res).i += (a).i;\ }while(0) #define C_SUBFROM( res , a)\ do {\ - CHECK_OVERFLOW_OP((res).r,-,(a).r)\ - CHECK_OVERFLOW_OP((res).i,-,(a).i)\ - (res).r -= (a).r; (res).i -= (a).i; \ + CHECK_OVERFLOW_OP((res).r,-,(a).r)\ + CHECK_OVERFLOW_OP((res).i,-,(a).i)\ + (res).r -= (a).r; (res).i -= (a).i; \ }while(0) @@ -135,30 +135,33 @@ struct kiss_fft_state{ #else # define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase) # define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase) -# define HALF_OF(x) ((x)*.5) +# define HALF_OF(x) ((x)*((kiss_fft_scalar).5)) #endif #define kf_cexp(x,phase) \ - do{ \ - (x)->r = KISS_FFT_COS(phase);\ - (x)->i = KISS_FFT_SIN(phase);\ - }while(0) + do{ \ + (x)->r = KISS_FFT_COS(phase);\ + (x)->i = KISS_FFT_SIN(phase);\ + }while(0) /* a debugging function */ #define pcpx(c)\ - fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) ) + KISS_FFT_DEBUG("%g + %gi\n",(double)((c)->r),(double)((c)->i)) #ifdef KISS_FFT_USE_ALLOCA // define this to allow use of alloca instead of malloc for temporary buffers -// Temporary buffers are used in two case: +// Temporary buffers are used in two case: // 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5 // 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform. #include #define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes) -#define KISS_FFT_TMP_FREE(ptr) +#define KISS_FFT_TMP_FREE(ptr) #else #define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes) #define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr) #endif + +#endif /* _kiss_fft_guts_h */ + diff --git a/src/external/kiss_fft/kiss_fft.c b/src/external/kiss_fft/kiss_fft.c index 465d6c97..58c24a06 100644 --- a/src/external/kiss_fft/kiss_fft.c +++ b/src/external/kiss_fft/kiss_fft.c @@ -1,16 +1,10 @@ /* -Copyright (c) 2003-2010, Mark Borgerding - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the author nor the names of any 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 COPYRIGHT OWNER 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. -*/ + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ #include "_kiss_fft_guts.h" @@ -209,6 +203,10 @@ static void kf_bfly_generic( int Norig = st->nfft; kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p); + if (scratch == NULL){ + KISS_FFT_ERROR("Memory allocation failed."); + return; + } for ( u=0; unfft); + if (tmpbuf == NULL){ + KISS_FFT_ERROR("Memory allocation error."); + return; + } + + + kf_work(tmpbuf,fin,1,in_stride, st->factors,st); memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft); KISS_FFT_TMP_FREE(tmpbuf); diff --git a/src/external/kiss_fft/kiss_fft.h b/src/external/kiss_fft/kiss_fft.h index 64c50f4a..f3980694 100644 --- a/src/external/kiss_fft/kiss_fft.h +++ b/src/external/kiss_fft/kiss_fft.h @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + #ifndef KISS_FFT_H #define KISS_FFT_H @@ -6,6 +14,21 @@ #include #include +// Define KISS_FFT_SHARED macro to properly export symbols +#ifdef KISS_FFT_SHARED +# ifdef _WIN32 +# ifdef KISS_FFT_BUILD +# define KISS_FFT_API __declspec(dllexport) +# else +# define KISS_FFT_API __declspec(dllimport) +# endif +# else +# define KISS_FFT_API __attribute__ ((visibility ("default"))) +# endif +#else +# define KISS_FFT_API +#endif + #ifdef __cplusplus extern "C" { #endif @@ -23,22 +46,35 @@ extern "C" { in the tools/ directory. */ +/* User may override KISS_FFT_MALLOC and/or KISS_FFT_FREE. */ #ifdef USE_SIMD # include # define kiss_fft_scalar __m128 -#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) -#define KISS_FFT_FREE _mm_free -#else -#define KISS_FFT_MALLOC malloc -#define KISS_FFT_FREE free -#endif +# ifndef KISS_FFT_MALLOC +# define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16) +# define KISS_FFT_ALIGN_CHECK(ptr) +# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL) +# endif +# ifndef KISS_FFT_FREE +# define KISS_FFT_FREE _mm_free +# endif +#else +# define KISS_FFT_ALIGN_CHECK(ptr) +# define KISS_FFT_ALIGN_SIZE_UP(size) (size) +# ifndef KISS_FFT_MALLOC +# define KISS_FFT_MALLOC malloc +# endif +# ifndef KISS_FFT_FREE +# define KISS_FFT_FREE free +# endif +#endif #ifdef FIXED_POINT -#include +#include # if (FIXED_POINT == 32) # define kiss_fft_scalar int32_t -# else +# else # define kiss_fft_scalar int16_t # endif #else @@ -55,9 +91,9 @@ typedef struct { typedef struct kiss_fft_state* kiss_fft_cfg; -/* +/* * kiss_fft_alloc - * + * * Initialize a FFT (or IFFT) algorithm's cfg/state buffer. * * typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL); @@ -67,18 +103,18 @@ typedef struct kiss_fft_state* kiss_fft_cfg; * * If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc. * The returned value should be free()d when done to avoid memory leaks. - * + * * The state can be placed in a user supplied buffer 'mem': * If lenmem is not NULL and mem is not NULL and *lenmem is large enough, * then the function places the cfg in mem and the size used in *lenmem * and returns mem. - * + * * If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough), - * then the function returns NULL and places the minimum cfg + * then the function returns NULL and places the minimum cfg * buffer size in *lenmem. * */ -kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); +kiss_fft_cfg KISS_FFT_API kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); /* * kiss_fft(cfg,in_out_buf) @@ -90,35 +126,35 @@ kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) * Note that each element is complex and can be accessed like f[k].r and f[k].i * */ -void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); +void KISS_FFT_API kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout); /* A more generic version of the above function. It reads its input from every Nth sample. * */ -void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); +void KISS_FFT_API kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride); -/* If kiss_fft_alloc allocated a buffer, it is one contiguous +/* If kiss_fft_alloc allocated a buffer, it is one contiguous buffer and can be simply free()d when no longer needed*/ -#define kiss_fft_free free +#define kiss_fft_free KISS_FFT_FREE /* - Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up + Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up your compiler output to call this before you exit. */ -void kiss_fft_cleanup(void); - +void KISS_FFT_API kiss_fft_cleanup(void); + /* * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5) */ -int kiss_fft_next_fast_size(int n); +int KISS_FFT_API kiss_fft_next_fast_size(int n); /* for real ffts, we need an even size */ #define kiss_fftr_next_fast_size_real(n) \ (kiss_fft_next_fast_size( ((n)+1)>>1)<<1) #ifdef __cplusplus -} +} #endif #endif diff --git a/src/external/kiss_fft/kiss_fft_log.h b/src/external/kiss_fft/kiss_fft_log.h new file mode 100644 index 00000000..b5b631a0 --- /dev/null +++ b/src/external/kiss_fft/kiss_fft_log.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2003-2010, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#ifndef kiss_fft_log_h +#define kiss_fft_log_h + +#define ERROR 1 +#define WARNING 2 +#define INFO 3 +#define DEBUG 4 + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#if defined(NDEBUG) +# define KISS_FFT_LOG_MSG(severity, ...) ((void)0) +#else +# define KISS_FFT_LOG_MSG(severity, ...) \ + fprintf(stderr, "[" #severity "] " __FILE__ ":" TOSTRING(__LINE__) " "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n") +#endif + +#define KISS_FFT_ERROR(...) KISS_FFT_LOG_MSG(ERROR, __VA_ARGS__) +#define KISS_FFT_WARNING(...) KISS_FFT_LOG_MSG(WARNING, __VA_ARGS__) +#define KISS_FFT_INFO(...) KISS_FFT_LOG_MSG(INFO, __VA_ARGS__) +#define KISS_FFT_DEBUG(...) KISS_FFT_LOG_MSG(DEBUG, __VA_ARGS__) + + + +#endif /* kiss_fft_log_h */ \ No newline at end of file diff --git a/src/external/kiss_fft/tools/kiss_fftr.c b/src/external/kiss_fft/kiss_fftr.c similarity index 68% rename from src/external/kiss_fft/tools/kiss_fftr.c rename to src/external/kiss_fft/kiss_fftr.c index b8e238b1..778a9a62 100644 --- a/src/external/kiss_fft/tools/kiss_fftr.c +++ b/src/external/kiss_fft/kiss_fftr.c @@ -1,16 +1,10 @@ /* -Copyright (c) 2003-2004, Mark Borgerding - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the author nor the names of any 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 COPYRIGHT OWNER 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. -*/ + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ #include "kiss_fftr.h" #include "_kiss_fft_guts.h" @@ -26,12 +20,14 @@ struct kiss_fftr_state{ kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem) { + KISS_FFT_ALIGN_CHECK(mem) + int i; kiss_fftr_cfg st = NULL; - size_t subsize, memneeded; + size_t subsize = 0, memneeded; if (nfft & 1) { - fprintf(stderr,"Real FFT optimization must be even.\n"); + KISS_FFT_ERROR("Real FFT optimization must be even."); return NULL; } nfft >>= 1; @@ -71,8 +67,8 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc; if ( st->substate->inverse) { - fprintf(stderr,"kiss fft usage error: improper alloc\n"); - exit(1); + KISS_FFT_ERROR("kiss fft usage error: improper alloc"); + return;/* The caller did not call the correct function */ } ncfft = st->substate->nfft; @@ -83,12 +79,12 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr * contains the sum of the even-numbered elements of the input time sequence * The imag part is the sum of the odd-numbered elements * - * The sum of tdc.r and tdc.i is the sum of the input time sequence. + * The sum of tdc.r and tdc.i is the sum of the input time sequence. * yielding DC of input time sequence - * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... + * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... * yielding Nyquist bin of input time sequence */ - + tdc.r = st->tmpbuf[0].r; tdc.i = st->tmpbuf[0].i; C_FIXDIV(tdc,2); @@ -96,14 +92,14 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i); freqdata[0].r = tdc.r + tdc.i; freqdata[ncfft].r = tdc.r - tdc.i; -#ifdef USE_SIMD +#ifdef USE_SIMD freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0); #else freqdata[ncfft].i = freqdata[0].i = 0; #endif for ( k=1;k <= ncfft/2 ; ++k ) { - fpk = st->tmpbuf[k]; + fpk = st->tmpbuf[k]; fpnk.r = st->tmpbuf[ncfft-k].r; fpnk.i = - st->tmpbuf[ncfft-k].i; C_FIXDIV(fpk,2); @@ -126,8 +122,8 @@ void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *t int k, ncfft; if (st->substate->inverse == 0) { - fprintf (stderr, "kiss fft usage error: improper alloc\n"); - exit (1); + KISS_FFT_ERROR("kiss fft usage error: improper alloc"); + return;/* The caller did not call the correct function */ } ncfft = st->substate->nfft; @@ -149,7 +145,7 @@ void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *t C_MUL (fok, tmp, st->super_twiddles[k-1]); C_ADD (st->tmpbuf[k], fek, fok); C_SUB (st->tmpbuf[ncfft - k], fek, fok); -#ifdef USE_SIMD +#ifdef USE_SIMD st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0); #else st->tmpbuf[ncfft - k].i *= -1; diff --git a/src/external/kiss_fft/kiss_fftr.h b/src/external/kiss_fft/kiss_fftr.h new file mode 100644 index 00000000..7fd73d2d --- /dev/null +++ b/src/external/kiss_fft/kiss_fftr.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2003-2004, Mark Borgerding. All rights reserved. + * This file is part of KISS FFT - https://github.com/mborgerding/kissfft + * + * SPDX-License-Identifier: BSD-3-Clause + * See COPYING file for more information. + */ + +#ifndef KISS_FTR_H +#define KISS_FTR_H + +#include "kiss_fft.h" +#ifdef __cplusplus +extern "C" { +#endif + + +/* + + Real optimized version can save about 45% cpu time vs. complex fft of a real seq. + + + + */ + +typedef struct kiss_fftr_state *kiss_fftr_cfg; + + +kiss_fftr_cfg KISS_FFT_API kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); +/* + nfft must be even + + If you don't care to allocate space, use mem = lenmem = NULL +*/ + + +void KISS_FFT_API kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); +/* + input timedata has nfft scalar points + output freqdata has nfft/2+1 complex points +*/ + +void KISS_FFT_API kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); +/* + input freqdata has nfft/2+1 complex points + output timedata has nfft scalar points +*/ + +#define kiss_fftr_free KISS_FFT_FREE + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/external/kiss_fft/tools/kiss_fftr.h b/src/external/kiss_fft/tools/kiss_fftr.h deleted file mode 100644 index 72e5a577..00000000 --- a/src/external/kiss_fft/tools/kiss_fftr.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef KISS_FTR_H -#define KISS_FTR_H - -#include "kiss_fft.h" -#ifdef __cplusplus -extern "C" { -#endif - - -/* - - Real optimized version can save about 45% cpu time vs. complex fft of a real seq. - - - - */ - -typedef struct kiss_fftr_state *kiss_fftr_cfg; - - -kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem); -/* - nfft must be even - - If you don't care to allocate space, use mem = lenmem = NULL -*/ - - -void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata); -/* - input timedata has nfft scalar points - output freqdata has nfft/2+1 complex points -*/ - -void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata); -/* - input freqdata has nfft/2+1 complex points - output timedata has nfft scalar points -*/ - -#define kiss_fftr_free free - -#ifdef __cplusplus -} -#endif -#endif From 5a31a4fb07d6f9f60e326da38a62255d86728fb4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 08:57:44 +0100 Subject: [PATCH 2/8] Update pugixml to v1.11 --- src/external/pugixml/src/pugiconfig.hpp | 9 ++- src/external/pugixml/src/pugixml.cpp | 96 +++++++++++++++++++++---- src/external/pugixml/src/pugixml.hpp | 25 ++++--- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/external/pugixml/src/pugiconfig.hpp b/src/external/pugixml/src/pugiconfig.hpp index 2fb69180..405a66b6 100644 --- a/src/external/pugixml/src/pugiconfig.hpp +++ b/src/external/pugixml/src/pugiconfig.hpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.10 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end @@ -40,6 +40,9 @@ // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 +// Tune this constant to adjust max nesting for XPath queries +// #define PUGIXML_XPATH_DEPTH_LIMIT 1024 + // Uncomment this to switch to header-only version // #define PUGIXML_HEADER_ONLY @@ -49,7 +52,7 @@ #endif /** - * Copyright (c) 2006-2019 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/src/external/pugixml/src/pugixml.cpp b/src/external/pugixml/src/pugixml.cpp index c3df93b1..efdcdf69 100644 --- a/src/external/pugixml/src/pugixml.cpp +++ b/src/external/pugixml/src/pugixml.cpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.10 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end @@ -378,7 +378,7 @@ PUGI__NS_BEGIN static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key) { - unsigned int h = static_cast(reinterpret_cast(key)); + unsigned int h = static_cast(reinterpret_cast(key) & 0xffffffff); // MurmurHash3 32-bit finalizer h ^= h >> 16; @@ -4981,7 +4981,12 @@ PUGI__NS_BEGIN #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return _wfopen_s(&file, path, mode) == 0 ? file : 0; +#else return _wfopen(path, mode); +#endif } #else PUGI__FN char* convert_path_heap(const wchar_t* str) @@ -5025,6 +5030,16 @@ PUGI__NS_BEGIN } #endif + PUGI__FN FILE* open_file(const char* path, const char* mode) + { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return fopen_s(&file, path, mode) == 0 ? file : 0; +#else + return fopen(path, mode); +#endif + } + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; @@ -6127,13 +6142,13 @@ namespace pugi impl::xml_allocator& alloc = impl::get_allocator(_root); if (!alloc.reserve()) return false; - for (xml_node_struct* child = _root->first_child; child; ) + for (xml_node_struct* cur = _root->first_child; cur; ) { - xml_node_struct* next = child->next_sibling; + xml_node_struct* next = cur->next_sibling; - impl::destroy_node(child, alloc); + impl::destroy_node(cur, alloc); - child = next; + cur = next; } _root->first_child = 0; @@ -7187,7 +7202,7 @@ namespace pugi reset(); using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, "rb"), impl::close_file); + auto_deleter file(impl::open_file(path_, "rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } @@ -7270,7 +7285,7 @@ namespace pugi PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); return impl::save_file_impl(*this, file.data, indent, flags, encoding); } @@ -11143,6 +11158,14 @@ PUGI__NS_BEGIN } }; + static const size_t xpath_ast_depth_limit = + #ifdef PUGIXML_XPATH_DEPTH_LIMIT + PUGIXML_XPATH_DEPTH_LIMIT + #else + 1024 + #endif + ; + struct xpath_parser { xpath_allocator* _alloc; @@ -11155,6 +11178,8 @@ PUGI__NS_BEGIN char_t _scratch[32]; + size_t _depth; + xpath_ast_node* error(const char* message) { _result->error = message; @@ -11171,6 +11196,11 @@ PUGI__NS_BEGIN return 0; } + xpath_ast_node* error_rec() + { + return error("Exceeded maximum allowed query depth"); + } + void* alloc_node() { return _alloc->allocate(sizeof(xpath_ast_node)); @@ -11526,6 +11556,8 @@ PUGI__NS_BEGIN return error("Unrecognized function call"); _lexer.next(); + size_t old_depth = _depth; + while (_lexer.current() != lex_close_brace) { if (argc > 0) @@ -11535,6 +11567,9 @@ PUGI__NS_BEGIN _lexer.next(); } + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_expression(); if (!n) return 0; @@ -11547,6 +11582,8 @@ PUGI__NS_BEGIN _lexer.next(); + _depth = old_depth; + return parse_function(function, argc, args); } @@ -11563,10 +11600,15 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_primary_expression(); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (n->rettype() != xpath_type_node_set) return error("Predicate has to be applied to node set"); @@ -11582,6 +11624,8 @@ PUGI__NS_BEGIN _lexer.next(); } + _depth = old_depth; + return n; } @@ -11733,12 +11777,17 @@ PUGI__NS_BEGIN xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); if (!n) return 0; + size_t old_depth = _depth; + xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* expr = parse_expression(); if (!expr) return 0; @@ -11755,6 +11804,8 @@ PUGI__NS_BEGIN last = pred; } + _depth = old_depth; + return n; } @@ -11764,11 +11815,16 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_step(set); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (l == lex_double_slash) { n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); @@ -11779,6 +11835,8 @@ PUGI__NS_BEGIN if (!n) return 0; } + _depth = old_depth; + return n; } @@ -11964,6 +12022,9 @@ PUGI__NS_BEGIN { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* rhs = parse_path_or_unary_expression(); if (!rhs) return 0; @@ -12009,13 +12070,22 @@ PUGI__NS_BEGIN // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression(int limit = 0) { + size_t old_depth = _depth; + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_path_or_unary_expression(); if (!n) return 0; - return parse_expression_rec(n, limit); + n = parse_expression_rec(n, limit); + + _depth = old_depth; + + return n; } - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) { } @@ -12024,6 +12094,8 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_expression(); if (!n) return 0; + assert(_depth == 0); + // check if there are unparsed tokens left if (_lexer.current() != lex_eof) return error("Incorrect query"); @@ -12923,7 +12995,7 @@ namespace pugi #endif /** - * Copyright (c) 2006-2019 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/src/external/pugixml/src/pugixml.hpp b/src/external/pugixml/src/pugixml.hpp index f6581091..7e2ce777 100644 --- a/src/external/pugixml/src/pugixml.hpp +++ b/src/external/pugixml/src/pugixml.hpp @@ -1,7 +1,7 @@ /** - * pugixml parser - version 1.10 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2019, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end @@ -14,7 +14,7 @@ #ifndef PUGIXML_VERSION // Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons // Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits -# define PUGIXML_VERSION 1100 +# define PUGIXML_VERSION 1110 #endif // Include user configuration file (this can define various configuration macros) @@ -111,6 +111,15 @@ # endif #endif +// If C++ is 2011 or higher, use 'nullptr' +#ifndef PUGIXML_NULL +# if __cplusplus >= 201103 +# define PUGIXML_NULL nullptr +# else +# define PUGIXML_NULL 0 +# endif +#endif + // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t @@ -661,15 +670,15 @@ namespace pugi #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node select_node(const xpath_query& query) const; // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node_set select_nodes(const xpath_query& query) const; // (deprecated: use select_node instead) Select single node by evaluating XPath query. - PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; #endif @@ -1212,7 +1221,7 @@ namespace pugi public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + explicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL); // Constructor xpath_query(); @@ -1465,7 +1474,7 @@ namespace std #endif /** - * Copyright (c) 2006-2019 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From e90a11d95dc9cbbf88fd4b36505c9f6cb213f431 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:09:58 +0100 Subject: [PATCH 3/8] Update cxxopts to v3 --- external/cxxopts/cxxopts.hpp | 675 ++++++++++++++++++++++++++--------- 1 file changed, 497 insertions(+), 178 deletions(-) diff --git a/external/cxxopts/cxxopts.hpp b/external/cxxopts/cxxopts.hpp index 6ec7998a..12456a70 100644 --- a/external/cxxopts/cxxopts.hpp +++ b/external/cxxopts/cxxopts.hpp @@ -33,17 +33,32 @@ THE SOFTWARE. #include #include #include -#include #include #include #include #include #include #include +#include -#ifdef __cpp_lib_optional -#include -#define CXXOPTS_HAS_OPTIONAL +#if defined(__GNUC__) && !defined(__clang__) +# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49 +# define CXXOPTS_NO_REGEX true +# endif +#endif + +#ifndef CXXOPTS_NO_REGEX +# include +#endif // CXXOPTS_NO_REGEX + +// Nonstandard before C++17, which is coincidentally what we also need for +#ifdef __has_include +# if __has_include() +# include +# ifdef __cpp_lib_optional +# define CXXOPTS_HAS_OPTIONAL +# endif +# endif #endif #if __cplusplus >= 201603L @@ -60,6 +75,10 @@ THE SOFTWARE. #define CXXOPTS__VERSION_MINOR 0 #define CXXOPTS__VERSION_PATCH 0 +#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6 + #define CXXOPTS_NULL_DEREF_IGNORE +#endif + namespace cxxopts { static constexpr struct { @@ -82,7 +101,7 @@ namespace cxxopts namespace cxxopts { - typedef icu::UnicodeString String; + using String = icu::UnicodeString; inline String @@ -91,6 +110,14 @@ namespace cxxopts return icu::UnicodeString::fromUTF8(std::move(s)); } +#if defined(__GNUC__) +// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it: +// warning: base class 'class std::enable_shared_from_this' has accessible non-virtual destructor +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Weffc++" +// This will be ignored under other compilers like LLVM clang. +#endif class UnicodeStringIterator : public std::iterator { @@ -137,6 +164,9 @@ namespace cxxopts const icu::UnicodeString* s; int32_t i; }; +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif inline String& @@ -217,7 +247,7 @@ namespace std namespace cxxopts { - typedef std::string String; + using String = std::string; template T @@ -288,8 +318,9 @@ namespace cxxopts #if defined(__GNUC__) // GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it: // warning: base class 'class std::enable_shared_from_this' has accessible non-virtual destructor -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic ignored "-Weffc++" // This will be ignored under other compilers like LLVM clang. #endif class Value : public std::enable_shared_from_this @@ -465,7 +496,7 @@ namespace cxxopts public: explicit option_has_no_value_exception(const std::string& option) : OptionException( - option.empty() ? + !option.empty() ? ("Option " + LQUOTE + option + RQUOTE + " has no value") : "Option has no value") { @@ -518,15 +549,257 @@ namespace cxxopts namespace values { - namespace + namespace parser_tool { - std::basic_regex integer_pattern - ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); - std::basic_regex truthy_pattern - ("(t|T)(rue)?|1"); - std::basic_regex falsy_pattern - ("(f|F)(alse)?|0"); - } // namespace + struct IntegerDesc + { + std::string negative = ""; + std::string base = ""; + std::string value = ""; + }; + struct ArguDesc { + std::string arg_name = ""; + bool grouping = false; + bool set_value = false; + std::string value = ""; + }; +#ifdef CXXOPTS_NO_REGEX + inline IntegerDesc SplitInteger(const std::string &text) + { + if (text.empty()) + { + throw_or_mimic(text); + } + IntegerDesc desc; + const char *pdata = text.c_str(); + if (*pdata == '-') + { + pdata += 1; + desc.negative = "-"; + } + if (strncmp(pdata, "0x", 2) == 0) + { + pdata += 2; + desc.base = "0x"; + } + if (*pdata != '\0') + { + desc.value = std::string(pdata); + } + else + { + throw_or_mimic(text); + } + return desc; + } + + inline bool IsTrueText(const std::string &text) + { + const char *pdata = text.c_str(); + if (*pdata == 't' || *pdata == 'T') + { + pdata += 1; + if (strncmp(pdata, "rue\0", 4) == 0) + { + return true; + } + } + else if (strncmp(pdata, "1\0", 2) == 0) + { + return true; + } + return false; + } + + inline bool IsFalseText(const std::string &text) + { + const char *pdata = text.c_str(); + if (*pdata == 'f' || *pdata == 'F') + { + pdata += 1; + if (strncmp(pdata, "alse\0", 5) == 0) + { + return true; + } + } + else if (strncmp(pdata, "0\0", 2) == 0) + { + return true; + } + return false; + } + + inline std::pair SplitSwitchDef(const std::string &text) + { + std::string short_sw, long_sw; + const char *pdata = text.c_str(); + if (isalnum(*pdata) && *(pdata + 1) == ',') { + short_sw = std::string(1, *pdata); + pdata += 2; + } + while (*pdata == ' ') { pdata += 1; } + if (isalnum(*pdata)) { + const char *store = pdata; + pdata += 1; + while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') { + pdata += 1; + } + if (*pdata == '\0') { + long_sw = std::string(store, pdata - store); + } else { + throw_or_mimic(text); + } + } + return std::pair(short_sw, long_sw); + } + + inline ArguDesc ParseArgument(const char *arg, bool &matched) + { + ArguDesc argu_desc; + const char *pdata = arg; + matched = false; + if (strncmp(pdata, "--", 2) == 0) + { + pdata += 2; + if (isalnum(*pdata)) + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + while (isalnum(*pdata) || *pdata == '-' || *pdata == '_') + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + } + if (argu_desc.arg_name.length() > 1) + { + if (*pdata == '=') + { + argu_desc.set_value = true; + pdata += 1; + if (*pdata != '\0') + { + argu_desc.value = std::string(pdata); + } + matched = true; + } + else if (*pdata == '\0') + { + matched = true; + } + } + } + } + else if (strncmp(pdata, "-", 1) == 0) + { + pdata += 1; + argu_desc.grouping = true; + while (isalnum(*pdata)) + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + } + matched = !argu_desc.arg_name.empty() && *pdata == '\0'; + } + return argu_desc; + } + +#else // CXXOPTS_NO_REGEX + + namespace + { + + std::basic_regex integer_pattern + ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); + std::basic_regex truthy_pattern + ("(t|T)(rue)?|1"); + std::basic_regex falsy_pattern + ("(f|F)(alse)?|0"); + + std::basic_regex option_matcher + ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); + std::basic_regex option_specifier + ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); + + } // namespace + + inline IntegerDesc SplitInteger(const std::string &text) + { + std::smatch match; + std::regex_match(text, match, integer_pattern); + + if (match.length() == 0) + { + throw_or_mimic(text); + } + + IntegerDesc desc; + desc.negative = match[1]; + desc.base = match[2]; + desc.value = match[3]; + + if (match.length(4) > 0) + { + desc.base = match[5]; + desc.value = "0"; + return desc; + } + + return desc; + } + + inline bool IsTrueText(const std::string &text) + { + std::smatch result; + std::regex_match(text, result, truthy_pattern); + return !result.empty(); + } + + inline bool IsFalseText(const std::string &text) + { + std::smatch result; + std::regex_match(text, result, falsy_pattern); + return !result.empty(); + } + + inline std::pair SplitSwitchDef(const std::string &text) + { + std::match_results result; + std::regex_match(text.c_str(), result, option_specifier); + if (result.empty()) + { + throw_or_mimic(text); + } + + const std::string& short_sw = result[2]; + const std::string& long_sw = result[3]; + + return std::pair(short_sw, long_sw); + } + + inline ArguDesc ParseArgument(const char *arg, bool &matched) + { + std::match_results result; + std::regex_match(arg, result, option_matcher); + matched = !result.empty(); + + ArguDesc argu_desc; + if (matched) { + argu_desc.arg_name = result[1].str(); + argu_desc.set_value = result[2].length() > 0; + argu_desc.value = result[3].str(); + if (result[4].length() > 0) + { + argu_desc.grouping = true; + argu_desc.arg_name = result[4].str(); + } + } + + return argu_desc; + } + +#endif // CXXOPTS_NO_REGEX +#undef CXXOPTS_NO_REGEX + } namespace detail { @@ -562,7 +835,7 @@ namespace cxxopts { template void - operator()(bool, U, const std::string&) {} + operator()(bool, U, const std::string&) const {} }; template @@ -594,45 +867,32 @@ namespace cxxopts void integer_parser(const std::string& text, T& value) { - std::smatch match; - std::regex_match(text, match, integer_pattern); - - if (match.length() == 0) - { - throw_or_mimic(text); - } - - if (match.length(4) > 0) - { - value = 0; - return; - } + parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text); using US = typename std::make_unsigned::type; - constexpr bool is_signed = std::numeric_limits::is_signed; - const bool negative = match.length(1) > 0; - const uint8_t base = match.length(2) > 0 ? 16 : 10; - auto value_match = match[3]; + const bool negative = int_desc.negative.length() > 0; + const uint8_t base = int_desc.base.length() > 0 ? 16 : 10; + const std::string & value_match = int_desc.value; US result = 0; - for (auto iter = value_match.first; iter != value_match.second; ++iter) + for (char ch : value_match) { US digit = 0; - if (*iter >= '0' && *iter <= '9') + if (ch >= '0' && ch <= '9') { - digit = static_cast(*iter - '0'); + digit = static_cast(ch - '0'); } - else if (base == 16 && *iter >= 'a' && *iter <= 'f') + else if (base == 16 && ch >= 'a' && ch <= 'f') { - digit = static_cast(*iter - 'a' + 10); + digit = static_cast(ch - 'a' + 10); } - else if (base == 16 && *iter >= 'A' && *iter <= 'F') + else if (base == 16 && ch >= 'A' && ch <= 'F') { - digit = static_cast(*iter - 'A' + 10); + digit = static_cast(ch - 'A' + 10); } else { @@ -670,77 +930,25 @@ namespace cxxopts } } - inline - void - parse_value(const std::string& text, uint8_t& value) + template ::value>::type* = nullptr + > + void parse_value(const std::string& text, T& value) { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, int8_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, uint16_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, int16_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, uint32_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, int32_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, uint64_t& value) - { - integer_parser(text, value); - } - - inline - void - parse_value(const std::string& text, int64_t& value) - { - integer_parser(text, value); + integer_parser(text, value); } inline void parse_value(const std::string& text, bool& value) { - std::smatch result; - std::regex_match(text, result, truthy_pattern); - - if (!result.empty()) + if (parser_tool::IsTrueText(text)) { value = true; return; } - std::regex_match(text, result, falsy_pattern); - if (!result.empty()) + if (parser_tool::IsFalseText(text)) { value = false; return; @@ -759,7 +967,9 @@ namespace cxxopts // The fallback parser. It uses the stringstream parser to parse all types // that have not been overloaded explicitly. It has to be placed in the // source code before all other more specialized templates. - template + template ::value>::type* = nullptr + > void parse_value(const std::string& text, T& value) { stringstream_parser(text, value); @@ -769,6 +979,12 @@ namespace cxxopts void parse_value(const std::string& text, std::vector& value) { + if (text.empty()) { + T v; + parse_value(text, v); + value.emplace_back(std::move(v)); + return; + } std::stringstream in(text); std::string token; while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { @@ -1030,9 +1246,9 @@ namespace cxxopts OptionDetails(const OptionDetails& rhs) : m_desc(rhs.m_desc) + , m_value(rhs.m_value->clone()) , m_count(rhs.m_count) { - m_value = rhs.m_value->clone(); } OptionDetails(OptionDetails&& rhs) = default; @@ -1133,6 +1349,17 @@ namespace cxxopts m_value->parse(); } + void + parse_no_value(const std::shared_ptr& details) + { + m_long_name = &details->long_name(); + } + +#if defined(CXXOPTS_NULL_DEREF_IGNORE) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" +#endif + CXXOPTS_NODISCARD size_t count() const noexcept @@ -1140,6 +1367,10 @@ namespace cxxopts return m_count; } +#if defined(CXXOPTS_NULL_DEREF_IGNORE) +#pragma GCC diagnostic pop +#endif + // TODO: maybe default options should count towards the number of arguments CXXOPTS_NODISCARD bool @@ -1227,8 +1458,7 @@ namespace cxxopts { public: - ParseResult() {} - + ParseResult() = default; ParseResult(const ParseResult&) = default; ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector sequential, std::vector&& unmatched_args) @@ -1366,6 +1596,9 @@ namespace cxxopts void parse_default(const std::shared_ptr& details); + void + parse_no_value(const std::shared_ptr& details); + private: void finalise_aliases(); @@ -1391,6 +1624,8 @@ namespace cxxopts , m_positional_help("positional parameters") , m_show_positional(false) , m_allow_unrecognised(false) + , m_width(76) + , m_tab_expansion(false) , m_options(std::make_shared()) { } @@ -1423,6 +1658,20 @@ namespace cxxopts return *this; } + Options& + set_width(size_t width) + { + m_width = width; + return *this; + } + + Options& + set_tab_expansion(bool expansion=true) + { + m_tab_expansion = expansion; + return *this; + } + ParseResult parse(int argc, const char* const* argv); @@ -1507,6 +1756,8 @@ namespace cxxopts std::string m_positional_help{}; bool m_show_positional; bool m_allow_unrecognised; + size_t m_width; + bool m_tab_expansion; std::shared_ptr m_options; std::vector m_positional{}; @@ -1545,14 +1796,8 @@ namespace cxxopts namespace { - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; - - std::basic_regex option_matcher - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - - std::basic_regex option_specifier - ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); + constexpr size_t OPTION_LONGEST = 30; + constexpr size_t OPTION_DESC_GAP = 2; String format_option @@ -1605,7 +1850,8 @@ namespace cxxopts ( const HelpOptionDetails& o, size_t start, - size_t width + size_t allowed, + bool tab_expansion ) { auto desc = o.desc; @@ -1624,54 +1870,107 @@ namespace cxxopts String result; + if (tab_expansion) + { + String desc2; + auto size = size_t{ 0 }; + for (auto c = std::begin(desc); c != std::end(desc); ++c) + { + if (*c == '\n') + { + desc2 += *c; + size = 0; + } + else if (*c == '\t') + { + auto skip = 8 - size % 8; + stringAppend(desc2, skip, ' '); + size += skip; + } + else + { + desc2 += *c; + ++size; + } + } + desc = desc2; + } + + desc += " "; + auto current = std::begin(desc); + auto previous = current; auto startLine = current; auto lastSpace = current; auto size = size_t{}; + bool appendNewLine; + bool onlyWhiteSpace = true; + while (current != std::end(desc)) { - if (*current == ' ') + appendNewLine = false; + + if (std::isblank(*previous)) { lastSpace = current; } - if (*current == '\n') + if (!std::isblank(*current)) { - startLine = current + 1; - lastSpace = startLine; + onlyWhiteSpace = false; } - else if (size > width) + + while (*current == '\n') { - if (lastSpace == startLine) + previous = current; + ++current; + appendNewLine = true; + } + + if (!appendNewLine && size >= allowed) + { + if (lastSpace != startLine) { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; + current = lastSpace; + previous = current; } - else + appendNewLine = true; + } + + if (appendNewLine) + { + stringAppend(result, startLine, current); + startLine = current; + lastSpace = current; + + if (*previous != '\n') { - stringAppend(result, startLine, lastSpace); stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - lastSpace = startLine; } + + stringAppend(result, start, ' '); + + if (*previous != '\n') + { + stringAppend(result, lastSpace, current); + } + + onlyWhiteSpace = true; size = 0; } - else - { - ++size; - } + previous = current; ++current; + ++size; } - //append whatever is left - stringAppend(result, startLine, current); + //append whatever is left but ignore whitespace + if (!onlyWhiteSpace) + { + stringAppend(result, startLine, previous); + } return result; } @@ -1709,37 +2008,30 @@ OptionAdder::operator() std::string arg_help ) { - std::match_results result; - std::regex_match(opts.c_str(), result, option_specifier); + std::string short_sw, long_sw; + std::tie(short_sw, long_sw) = values::parser_tool::SplitSwitchDef(opts); - if (result.empty()) + if (!short_sw.length() && !long_sw.length()) { throw_or_mimic(opts); } - - const auto& short_match = result[2]; - const auto& long_match = result[3]; - - if (!short_match.length() && !long_match.length()) - { - throw_or_mimic(opts); - } else if (long_match.length() == 1 && short_match.length()) + else if (long_sw.length() == 1 && short_sw.length()) { throw_or_mimic(opts); } auto option_names = [] ( - const std::sub_match& short_, - const std::sub_match& long_ + const std::string &short_, + const std::string &long_ ) { if (long_.length() == 1) { - return std::make_tuple(long_.str(), short_.str()); + return std::make_tuple(long_, short_); } - return std::make_tuple(short_.str(), long_.str()); - }(short_match, long_match); + return std::make_tuple(short_, long_); + }(short_sw, long_sw); m_options.add_option ( @@ -1763,6 +2055,14 @@ OptionParser::parse_default(const std::shared_ptr& details) store.parse_default(details); } +inline +void +OptionParser::parse_no_value(const std::shared_ptr& details) +{ + auto& store = m_parsed[details->hash()]; + store.parse_no_value(details); +} + inline void OptionParser::parse_option @@ -1831,9 +2131,9 @@ OptionParser::consume_positional(const std::string& a, PositionalListIterator& n auto iter = m_options.find(*next); if (iter != m_options.end()) { - auto& result = m_parsed[iter->second->hash()]; if (!iter->second->value().is_container()) { + auto& result = m_parsed[iter->second->hash()]; if (result.count() == 0) { add_to_option(iter, *next, a); @@ -1889,7 +2189,7 @@ OptionParser::parse(int argc, const char* const* argv) { int current = 1; bool consume_remaining = false; - PositionalListIterator next_positional = m_positional.begin(); + auto next_positional = m_positional.begin(); std::vector unmatched; @@ -1901,11 +2201,11 @@ OptionParser::parse(int argc, const char* const* argv) ++current; break; } + bool matched = false; + values::parser_tool::ArguDesc argu_desc = + values::parser_tool::ParseArgument(argv[current], matched); - std::match_results result; - std::regex_match(argv[current], result, option_matcher); - - if (result.empty()) + if (!matched) { //not a flag @@ -1923,16 +2223,16 @@ OptionParser::parse(int argc, const char* const* argv) } else { - unmatched.push_back(argv[current]); + unmatched.emplace_back(argv[current]); } //if we return from here then it was parsed successfully, so continue } else { //short or long option? - if (result[4].length() != 0) + if (argu_desc.grouping) { - const std::string& s = result[4]; + const std::string& s = argu_desc.arg_name; for (std::size_t i = 0; i != s.size(); ++i) { @@ -1943,6 +2243,7 @@ OptionParser::parse(int argc, const char* const* argv) { if (m_allow_unrecognised) { + unmatched.push_back(std::string("-") + s[i]); continue; } //error @@ -1960,6 +2261,12 @@ OptionParser::parse(int argc, const char* const* argv) { parse_option(value, name, value->value().get_implicit_value()); } + else if (i + 1 < s.size()) + { + std::string arg_value = s.substr(i + 1); + parse_option(value, name, arg_value); + break; + } else { //error @@ -1967,9 +2274,9 @@ OptionParser::parse(int argc, const char* const* argv) } } } - else if (result[1].length() != 0) + else if (argu_desc.arg_name.length() != 0) { - const std::string& name = result[1]; + const std::string& name = argu_desc.arg_name; auto iter = m_options.find(name); @@ -1978,7 +2285,7 @@ OptionParser::parse(int argc, const char* const* argv) if (m_allow_unrecognised) { // keep unrecognised options in argument list, skip to next argument - unmatched.push_back(argv[current]); + unmatched.emplace_back(argv[current]); ++current; continue; } @@ -1989,11 +2296,11 @@ OptionParser::parse(int argc, const char* const* argv) auto opt = iter->second; //equals provided for long option? - if (result[2].length() != 0) + if (argu_desc.set_value) { //parse the option given - parse_option(opt, name, result[3]); + parse_option(opt, name, argu_desc.value); } else { @@ -2014,8 +2321,13 @@ OptionParser::parse(int argc, const char* const* argv) auto& store = m_parsed[detail->hash()]; - if(value.has_default() && !store.count() && !store.has_default()){ - parse_default(detail); + if (value.has_default()) { + if (!store.count() && !store.has_default()) { + parse_default(detail); + } + } + else { + parse_no_value(detail); } } @@ -2031,7 +2343,7 @@ OptionParser::parse(int argc, const char* const* argv) //adjust argv for any that couldn't be swallowed while (current != argc) { - unmatched.push_back(argv[current]); + unmatched.emplace_back(argv[current]); ++current; } } @@ -2160,11 +2472,14 @@ Options::help_one_group(const std::string& g) const longest = (std::max)(longest, stringLength(s)); format.push_back(std::make_pair(s, String())); } + longest = (std::min)(longest, OPTION_LONGEST); - longest = (std::min)(longest, static_cast(OPTION_LONGEST)); - - //widest allowed description - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; + //widest allowed description -- min 10 chars for helptext/line + size_t allowed = 10; + if (m_width > allowed + longest + OPTION_DESC_GAP) + { + allowed = m_width - longest - OPTION_DESC_GAP; + } auto fiter = format.begin(); for (const auto& o : group->second.options) @@ -2175,7 +2490,7 @@ Options::help_one_group(const std::string& g) const continue; } - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); + auto d = format_description(o, longest + OPTION_DESC_GAP, allowed, m_tab_expansion); result += fiter->first; if (stringLength(fiter->first) > longest) @@ -2226,12 +2541,16 @@ void Options::generate_all_groups_help(String& result) const { std::vector all_groups; - all_groups.reserve(m_help.size()); - for (const auto& group : m_help) - { - all_groups.push_back(group.first); - } + std::transform( + m_help.begin(), + m_help.end(), + std::back_inserter(all_groups), + [] (const std::map::value_type& group) + { + return group.first; + } + ); generate_group_help(result, all_groups); } From a76ff4919259374935c2e5d8be08c07c2044d418 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:14:14 +0100 Subject: [PATCH 4/8] Update GHC Filesystem to v1.5.10 --- external/filesystem | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/filesystem b/external/filesystem index 7bc5c173..614bbe87 160000 --- a/external/filesystem +++ b/external/filesystem @@ -1 +1 @@ -Subproject commit 7bc5c17305fe70518ca72162e244af1d12455a91 +Subproject commit 614bbe87b80435d87ab8791564370e0c1d13627d From d8feff462e55b67a3ac8d8c242ea5b745a5c9a7b Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:34:13 +0100 Subject: [PATCH 5/8] Update fmidi to v0.1.1 --- external/fmidi/sources/fmidi/fmidi.h | 5 + external/fmidi/sources/fmidi/fmidi_mini.cpp | 2603 ++++++++++--------- 2 files changed, 1434 insertions(+), 1174 deletions(-) diff --git a/external/fmidi/sources/fmidi/fmidi.h b/external/fmidi/sources/fmidi/fmidi.h index 94297f44..d56a261c 100644 --- a/external/fmidi/sources/fmidi/fmidi.h +++ b/external/fmidi/sources/fmidi/fmidi.h @@ -65,6 +65,7 @@ FMIDI_API bool fmidi_smf_stream_write(const fmidi_smf_t *smf, FILE *stream); typedef enum fmidi_fileformat { fmidi_fileformat_smf, fmidi_fileformat_xmi, + fmidi_fileformat_mus, } fmidi_fileformat_t; FMIDI_API fmidi_fileformat_t fmidi_mem_identify(const uint8_t *data, size_t length); @@ -117,6 +118,10 @@ FMIDI_API fmidi_smf_t *fmidi_xmi_mem_read(const uint8_t *data, size_t length); FMIDI_API fmidi_smf_t *fmidi_xmi_file_read(const char *filename); FMIDI_API fmidi_smf_t *fmidi_xmi_stream_read(FILE *stream); +FMIDI_API fmidi_smf_t *fmidi_mus_mem_read(const uint8_t *data, size_t length); +FMIDI_API fmidi_smf_t *fmidi_mus_file_read(const char *filename); +FMIDI_API fmidi_smf_t *fmidi_mus_stream_read(FILE *stream); + /////////////// // SEQUENCER // /////////////// diff --git a/external/fmidi/sources/fmidi/fmidi_mini.cpp b/external/fmidi/sources/fmidi/fmidi_mini.cpp index 5c9cd188..b673bf00 100644 --- a/external/fmidi/sources/fmidi/fmidi_mini.cpp +++ b/external/fmidi/sources/fmidi/fmidi_mini.cpp @@ -1,7 +1,7 @@ // ============================================================================= // // The Fmidi library - a free software toolkit for MIDI file processing -// Single-file implementation, based on software revision: c513b4f +// Single-file implementation, based on software revision: 3e6d5b5 // // ============================================================================= // Copyright Jean Pierre Cimalando 2018-2020. @@ -10,32 +10,395 @@ // http://www.boost.org/LICENSE_1_0.txt) // ============================================================================= - #include "fmidi/fmidi.h" -#include +#include +#include +#include -struct fmidi_raw_track { - std::unique_ptr data; - uint32_t length; +struct fmidi_player_context { + fmidi_player_t *plr; + fmidi_seq_u seq; + double timepos; + double speed; + bool have_event; + fmidi_seq_event_t sqevt; + void (*cbfn)(const fmidi_event_t *, void *); + void *cbdata; + void (*finifn)(void *); + void *finidata; }; -struct fmidi_smf { - fmidi_smf_info_t info; - std::unique_ptr track; +struct fmidi_player { + bool running; + fmidi_player_context ctx; }; -//------------------------------------------------------------------------------ -uintptr_t fmidi_event_pad(uintptr_t size); -fmidi_event_t *fmidi_event_alloc(std::vector &buf, uint32_t datalen); -unsigned fmidi_message_sizeof(uint8_t id); - -//------------------------------------------------------------------------------ -inline uintptr_t fmidi_event_pad(uintptr_t size) +fmidi_player_t *fmidi_player_new(fmidi_smf_t *smf) { - uintptr_t nb = size % alignof(fmidi_event_t); - return nb ? (size + alignof(fmidi_event_t) - nb) : size; + fmidi_player_u plr(new fmidi_player_t); + plr->running = false; + + fmidi_player_context &ctx = plr->ctx; + ctx.plr = plr.get(); + ctx.seq.reset(fmidi_seq_new(smf)); + ctx.timepos = 0; + ctx.speed = 1; + ctx.have_event = false; + ctx.cbfn = nullptr; + ctx.cbdata = nullptr; + ctx.finifn = nullptr; + ctx.finidata = nullptr; + + return plr.release(); } +void fmidi_player_tick(fmidi_player_t *plr, double delta) +{ + fmidi_player_context &ctx = plr->ctx; + fmidi_seq_t &seq = *ctx.seq; + void (*cbfn)(const fmidi_event_t *, void *) = ctx.cbfn; + void *cbdata = ctx.cbdata; + + double timepos = ctx.timepos; + bool have_event = ctx.have_event; + fmidi_seq_event_t &sqevt = ctx.sqevt; + + timepos += ctx.speed * delta; + + bool more = have_event || fmidi_seq_next_event(&seq, &sqevt); + if (more) { + have_event = true; + while (more && timepos > sqevt.time) { + const fmidi_event_t &event = *sqevt.event; + if (cbfn) + cbfn(&event, cbdata); + have_event = more = fmidi_seq_next_event(&seq, &sqevt); + } + } + + ctx.have_event = have_event; + ctx.timepos = timepos; + + if (!more) { + plr->running = false; + if (ctx.finifn) + ctx.finifn(ctx.finidata); + } +} + +void fmidi_player_free(fmidi_player_t *plr) +{ + delete plr; +} + +void fmidi_player_start(fmidi_player_t *plr) +{ + plr->running = true; +} + +void fmidi_player_stop(fmidi_player_t *plr) +{ + plr->running = false; +} + +void fmidi_player_rewind(fmidi_player_t *plr) +{ + fmidi_player_context &ctx = plr->ctx; + fmidi_seq_rewind(ctx.seq.get()); + ctx.timepos = 0; + ctx.have_event = false; +} + +bool fmidi_player_running(const fmidi_player_t *plr) +{ + return plr->running; +} + +double fmidi_player_current_time(const fmidi_player_t *plr) +{ + return plr->ctx.timepos; +} + +void fmidi_player_goto_time(fmidi_player_t *plr, double time) +{ + fmidi_player_context &ctx = plr->ctx; + fmidi_seq_t &seq = *ctx.seq; + + uint8_t programs[16]; + uint8_t controls[16 * 128]; + std::fill_n(programs, 16, 0); + std::fill_n(controls, 16 * 128, 255); + + fmidi_player_rewind(plr); + + for (fmidi_seq_event_t sqevt; + fmidi_seq_peek_event(&seq, &sqevt) && sqevt.time < time;) { + const fmidi_event_t &evt = *sqevt.event; + if (evt.type == fmidi_event_message) { + uint8_t status = evt.data[0]; + if (status >> 4 == 0b1100 && evt.datalen == 2) { // program change + uint8_t channel = status & 0xf; + programs[channel] = evt.data[1] & 127; + } + else if (status >> 4 == 0b1011 && evt.datalen == 3) { // control change + uint8_t channel = status & 0xf; + uint8_t id = evt.data[1] & 127; + controls[channel * 128 + id] = evt.data[2] & 127; + } + } + fmidi_seq_next_event(&seq, nullptr); + } + + ctx.timepos = time; + + if (ctx.cbfn) { + uint8_t evtbuf[fmidi_event_sizeof(3)]; + fmidi_event_t *evt = (fmidi_event_t *)evtbuf; + evt->type = fmidi_event_message; + evt->delta = 0; + + for (unsigned c = 0; c < 16; ++c) { + // all sound off + evt->datalen = 3; + evt->data[0] = (0b1011 << 4) | c; + evt->data[1] = 120; + evt->data[2] = 0; + ctx.cbfn(evt, ctx.cbdata); + // reset all controllers + evt->datalen = 3; + evt->data[0] = (0b1011 << 4) | c; + evt->data[1] = 121; + evt->data[2] = 0; + ctx.cbfn(evt, ctx.cbdata); + // program change + evt->datalen = 2; + evt->data[0] = (0b1100 << 4) | c; + evt->data[1] = programs[c]; + ctx.cbfn(evt, ctx.cbdata); + // control change + for (unsigned id = 0; id < 128; ++id) { + uint8_t val = controls[c * 128 + id]; + if (val < 128) { + evt->datalen = 3; + evt->data[0] = (0b1011 << 4) | c; + evt->data[1] = id; + evt->data[2] = val; + ctx.cbfn(evt, ctx.cbdata); + } + } + } + } +} + +double fmidi_player_current_speed(const fmidi_player_t *plr) +{ + return plr->ctx.speed; +} + +void fmidi_player_set_speed(fmidi_player_t *plr, double speed) +{ + plr->ctx.speed = speed; +} + +void fmidi_player_event_callback( + fmidi_player_t *plr, void (*cbfn)(const fmidi_event_t *, void *), void *cbdata) +{ + fmidi_player_context &ctx = plr->ctx; + ctx.cbfn = cbfn; + ctx.cbdata = cbdata; +} + +void fmidi_player_finish_callback( + fmidi_player_t *plr, void (*cbfn)(void *), void *cbdata) +{ + fmidi_player_context &ctx = plr->ctx; + ctx.finifn = cbfn; + ctx.finidata = cbdata; +} + + +#include +#include +#include +#include + +enum memstream_status { // make it match fmidi status codes + ms_ok, + ms_err_format, + ms_err_eof, +}; + +class memstream { +public: + memstream(const uint8_t *data, size_t length); + + size_t endpos() const; + size_t getpos() const; + memstream_status setpos(size_t off); + + memstream_status skip(size_t count); + memstream_status skipbyte(unsigned byte); + + const uint8_t *peek(size_t length); + const uint8_t *read(size_t length); + + memstream_status peekbyte(unsigned *retp); + memstream_status readbyte(unsigned *retp); + + memstream_status readintLE(uint32_t *retp, unsigned length); + memstream_status readintBE(uint32_t *retp, unsigned length); + memstream_status readvlq(uint32_t *retp); + memstream_status peekvlq(uint32_t *retp); + +private: + const uint8_t *base_ = nullptr; + size_t length_ = 0; + size_t offset_ = 0; + typedef std::tuple vlq_result; + vlq_result doreadvlq(); +}; + +//------------------------------------------------------------------------------ +inline memstream::memstream(const uint8_t *data, size_t length) + : base_(data), length_(length) { +} + +inline size_t memstream::endpos() const { + return length_; +} + +inline size_t memstream::getpos() const { + return offset_; +} + +memstream_status memstream::setpos(size_t off) +{ + if (off > length_) + return ms_err_eof; + offset_ = off; + return ms_ok; +} + +memstream_status memstream::skip(size_t count) +{ + if (length_ - offset_ < count) + return ms_err_eof; + offset_ += count; + return ms_ok; +} + +memstream_status memstream::skipbyte(unsigned byte) +{ + unsigned otherbyte; + memstream_status status = peekbyte(&otherbyte); + if (status) + return status; + if (byte != otherbyte) + return ms_err_format; + ++offset_; + return ms_ok; +} + +const uint8_t *memstream::peek(size_t length) +{ + if (length > length_ - offset_) + return nullptr; + return base_ + offset_; +} + +const uint8_t *memstream::read(size_t length) +{ + const uint8_t *ptr = peek(length); + if (ptr) + offset_ += length; + return ptr; +} + +memstream_status memstream::peekbyte(unsigned *retp) +{ + if (length_ <= offset_) + return ms_err_eof; + if (retp) + *retp = base_[offset_]; + return ms_ok; +} + +memstream_status memstream::readbyte(unsigned *retp) +{ + memstream_status ret = peekbyte(retp); + if (ret) + return ret; + ++offset_; + return ms_ok; +} + +memstream_status memstream::readintLE(uint32_t *retp, unsigned length) +{ + const uint8_t *ptr = read(length); + if (!ptr) + return ms_err_eof; + uint32_t ret = 0; + for (unsigned i = length; i-- > 0;) + ret = (ret << 8) | ptr[i]; + if (retp) + *retp = ret; + return ms_ok; +} + +memstream_status memstream::readintBE(uint32_t *retp, unsigned length) +{ + const uint8_t *ptr = read(length); + if (!ptr) + return ms_err_eof; + uint32_t ret = 0; + for (unsigned i = 0; i < length; ++i) + ret = (ret << 8) | ptr[i]; + if (retp) + *retp = ret; + return ms_ok; +} + +memstream_status memstream::readvlq(uint32_t *retp) +{ + memstream_status ret; + uint32_t value; + unsigned length; + std::tie(ret, value, length) = doreadvlq(); + offset_ += length; + if (retp) + *retp = value; + return ret; +} + +memstream_status memstream::peekvlq(uint32_t *retp) +{ + memstream_status ret; + uint32_t value; + unsigned length; + std::tie(ret, value, length) = doreadvlq(); + if (retp) + *retp = value; + return ret; +} + +memstream::vlq_result memstream::doreadvlq() +{ + uint32_t ret = 0; + unsigned length; + bool cont = true; + for (length = 0; cont && length < 4; ++length) { + if (offset_ + length >= length_) + return vlq_result{ms_err_eof, 0, 0}; + uint8_t byte = base_[offset_ + length]; + ret = (ret << 7) | (byte & ((1u << 7) - 1)); + cont = byte & (1u << 7); + } + if (cont) + return vlq_result{ms_err_format, 0, 0}; + return vlq_result{ms_ok, ret, length}; +} + + #include "fmidi/fmidi.h" #if !defined(FMIDI_DISABLE_DESCRIBE_API) @@ -183,58 +546,118 @@ inline off_t Stream_Writer::tell() const return ftell(stream); } -#include -#include -#include -#include +thread_local fmidi_error_info_t fmidi_last_error; -enum memstream_status { // make it match fmidi status codes - ms_ok, - ms_err_format, - ms_err_eof, +fmidi_status_t fmidi_errno() +{ + return fmidi_last_error.code; +} + +const fmidi_error_info_t *fmidi_errinfo() +{ + return &fmidi_last_error; +} + +const char *fmidi_strerror(fmidi_status_t status) +{ + switch (status) { + case fmidi_ok: return "success"; + case fmidi_err_format: return "invalid format"; + case fmidi_err_eof: return "premature end of file"; + case fmidi_err_input: return "input error"; + case fmidi_err_largefile: return "file too large"; + case fmidi_err_output: return "output error"; + } + return nullptr; +} + +//------------------------------------------------------------------------------ +void Memory_Writer::put(uint8_t byte) +{ + size_t size = mem.size(); + size_t index = this->index; + + if (index < size) + mem[index] = byte; + else + { + assert(index == size); + mem.push_back(byte); + } + + this->index = index + 1; +} + +void Memory_Writer::write(const void *data, size_t size) +{ + size_t memsize = mem.size(); + size_t index = this->index; + + const uint8_t *bytes = (const uint8_t *)data; + size_t ncopy = std::min(size, memsize - index); + std::copy(bytes, bytes + ncopy, &mem[index]); + + mem.insert(mem.end(), bytes + ncopy, bytes + size); + this->index = index + size; +} + +bool Memory_Writer::seek(off_t offset, int whence) +{ + std::make_unsigned::type uoffset(offset); + size_t size = mem.size(); + size_t index = this->index; + + switch (whence) { + case SEEK_SET: + if (uoffset > size) + return false; + this->index = uoffset; + break; + case SEEK_CUR: + if (offset >= 0) { + if (size - index < uoffset) + return false; + this->index = index + uoffset; + } + else { + if (index < uoffset) + return false; + this->index = index - uoffset; + } + break; + case SEEK_END: + if (uoffset > size) + return false; + this->index = size - uoffset; + break; + } + return true; +} + + +#include "fmidi/fmidi.h" +#include + +struct fmidi_raw_track { + std::unique_ptr data; + uint32_t length; }; -class memstream { -public: - memstream(const uint8_t *data, size_t length); - - size_t endpos() const; - size_t getpos() const; - memstream_status setpos(size_t off); - - memstream_status skip(size_t count); - memstream_status skipbyte(unsigned byte); - - const uint8_t *peek(size_t length); - const uint8_t *read(size_t length); - - memstream_status peekbyte(unsigned *retp); - memstream_status readbyte(unsigned *retp); - - memstream_status readintLE(uint32_t *retp, unsigned length); - memstream_status readintBE(uint32_t *retp, unsigned length); - memstream_status readvlq(uint32_t *retp); - memstream_status peekvlq(uint32_t *retp); - -private: - const uint8_t *base_ = nullptr; - size_t length_ = 0; - size_t offset_ = 0; - typedef std::tuple vlq_result; - vlq_result doreadvlq(); +struct fmidi_smf { + fmidi_smf_info_t info; + std::unique_ptr track; }; //------------------------------------------------------------------------------ -inline memstream::memstream(const uint8_t *data, size_t length) - : base_(data), length_(length) { -} +uintptr_t fmidi_event_pad(uintptr_t size); +fmidi_event_t *fmidi_event_alloc(std::vector &buf, uint32_t datalen); +unsigned fmidi_message_sizeof(uint8_t id); -inline size_t memstream::endpos() const { - return length_; -} - -inline size_t memstream::getpos() const { - return offset_; +//------------------------------------------------------------------------------ +inline uintptr_t fmidi_event_pad(uintptr_t size) +{ + uintptr_t nb = size % alignof(fmidi_event_t); + return nb ? (size + alignof(fmidi_event_t) - nb) : size; } #if !defined(FMIDI_DISABLE_DESCRIBE_API) #include @@ -730,298 +1153,61 @@ std::ostream &operator<<(std::ostream &out, const printfmt_bytes &b) #endif // !defined(FMIDI_DISABLE_DESCRIBE_API) -thread_local fmidi_error_info_t fmidi_last_error; - -fmidi_status_t fmidi_errno() -{ - return fmidi_last_error.code; -} - -const fmidi_error_info_t *fmidi_errinfo() -{ - return &fmidi_last_error; -} - -const char *fmidi_strerror(fmidi_status_t status) -{ - switch (status) { - case fmidi_ok: return "success"; - case fmidi_err_format: return "invalid format"; - case fmidi_err_eof: return "premature end of file"; - case fmidi_err_input: return "input error"; - case fmidi_err_largefile: return "file too large"; - case fmidi_err_output: return "output error"; - } - return nullptr; -} - -//------------------------------------------------------------------------------ -void Memory_Writer::put(uint8_t byte) -{ - size_t size = mem.size(); - size_t index = this->index; - - if (index < size) - mem[index] = byte; - else - { - assert(index == size); - mem.push_back(byte); - } - - this->index = index + 1; -} - -void Memory_Writer::write(const void *data, size_t size) -{ - size_t memsize = mem.size(); - size_t index = this->index; - - const uint8_t *bytes = (const uint8_t *)data; - size_t ncopy = std::min(size, memsize - index); - std::copy(bytes, bytes + ncopy, &mem[index]); - - mem.insert(mem.end(), bytes + ncopy, bytes + size); - this->index = index + size; -} - -bool Memory_Writer::seek(off_t offset, int whence) -{ - std::make_unsigned::type uoffset(offset); - size_t size = mem.size(); - size_t index = this->index; - - switch (whence) { - case SEEK_SET: - if (uoffset > size) - return false; - this->index = uoffset; - break; - case SEEK_CUR: - if (offset >= 0) { - if (size - index < uoffset) - return false; - this->index = index + uoffset; - } - else { - if (index < uoffset) - return false; - this->index = index - uoffset; - } - break; - case SEEK_END: - if (uoffset > size) - return false; - this->index = size - uoffset; - break; - } - return true; -} - -#include "fmidi/fmidi.h" #include -#include -#include +#include -struct fmidi_player_context { - fmidi_player_t *plr; - fmidi_seq_u seq; - double timepos; - double speed; - bool have_event; - fmidi_seq_event_t sqevt; - void (*cbfn)(const fmidi_event_t *, void *); - void *cbdata; - void (*finifn)(void *); - void *finidata; +//////////////////////// +// FILE PATH ENCODING // +//////////////////////// + +FILE *fmidi_fopen(const char *path, const char *mode); + +/////////////// +// FILE RAII // +/////////////// + +struct FILE_deleter; +typedef std::unique_ptr unique_FILE; + +struct FILE_deleter { + void operator()(FILE *stream) const + { fclose(stream); } }; +#if defined(_WIN32) +# include +# include +# include +#endif -struct fmidi_player { - bool running; - fmidi_player_context ctx; -}; - -fmidi_player_t *fmidi_player_new(fmidi_smf_t *smf) +FILE *fmidi_fopen(const char *path, const char *mode) { - fmidi_player_u plr(new fmidi_player_t); - plr->running = false; - - fmidi_player_context &ctx = plr->ctx; - ctx.plr = plr.get(); - ctx.seq.reset(fmidi_seq_new(smf)); - ctx.timepos = 0; - ctx.speed = 1; - ctx.have_event = false; - ctx.cbfn = nullptr; - ctx.cbdata = nullptr; - ctx.finifn = nullptr; - ctx.finidata = nullptr; - - return plr.release(); -} - -void fmidi_player_tick(fmidi_player_t *plr, double delta) -{ - fmidi_player_context &ctx = plr->ctx; - fmidi_seq_t &seq = *ctx.seq; - void (*cbfn)(const fmidi_event_t *, void *) = ctx.cbfn; - void *cbdata = ctx.cbdata; - - double timepos = ctx.timepos; - bool have_event = ctx.have_event; - fmidi_seq_event_t &sqevt = ctx.sqevt; - - timepos += ctx.speed * delta; - - bool more = have_event || fmidi_seq_next_event(&seq, &sqevt); - if (more) { - have_event = true; - while (more && timepos > sqevt.time) { - const fmidi_event_t &event = *sqevt.event; - if (cbfn) - cbfn(&event, cbdata); - have_event = more = fmidi_seq_next_event(&seq, &sqevt); +#if !defined(_WIN32) + return fopen(path, mode); +#else + auto toWideString = [](const char *utf8) -> wchar_t * { + unsigned wsize = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, nullptr, 0); + if (wsize == 0) + return nullptr; + wchar_t *wide = new wchar_t[wsize]; + wsize = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, wsize); + if (wsize == 0) { + delete[] wide; + return nullptr; } + return wide; + }; + std::unique_ptr wpath(toWideString(path)); + if (!wpath) { + errno = EINVAL; + return nullptr; } - - ctx.have_event = have_event; - ctx.timepos = timepos; - - if (!more) { - plr->running = false; - if (ctx.finifn) - ctx.finifn(ctx.finidata); + std::unique_ptr wmode(toWideString(mode)); + if (!wmode) { + errno = EINVAL; + return nullptr; } -} - -void fmidi_player_free(fmidi_player_t *plr) -{ - delete plr; -} - -void fmidi_player_start(fmidi_player_t *plr) -{ - plr->running = true; -} - -void fmidi_player_stop(fmidi_player_t *plr) -{ - plr->running = false; -} - -void fmidi_player_rewind(fmidi_player_t *plr) -{ - fmidi_player_context &ctx = plr->ctx; - fmidi_seq_rewind(ctx.seq.get()); - ctx.timepos = 0; - ctx.have_event = false; -} - -bool fmidi_player_running(const fmidi_player_t *plr) -{ - return plr->running; -} - -double fmidi_player_current_time(const fmidi_player_t *plr) -{ - return plr->ctx.timepos; -} - -void fmidi_player_goto_time(fmidi_player_t *plr, double time) -{ - fmidi_player_context &ctx = plr->ctx; - fmidi_seq_t &seq = *ctx.seq; - - uint8_t programs[16]; - uint8_t controls[16 * 128]; - std::fill_n(programs, 16, 0); - std::fill_n(controls, 16 * 128, 255); - - fmidi_player_rewind(plr); - - for (fmidi_seq_event_t sqevt; - fmidi_seq_peek_event(&seq, &sqevt) && sqevt.time < time;) { - const fmidi_event_t &evt = *sqevt.event; - if (evt.type == fmidi_event_message) { - uint8_t status = evt.data[0]; - if (status >> 4 == 0b1100 && evt.datalen == 2) { // program change - uint8_t channel = status & 0xf; - programs[channel] = evt.data[1] & 127; - } - else if (status >> 4 == 0b1011 && evt.datalen == 3) { // control change - uint8_t channel = status & 0xf; - uint8_t id = evt.data[1] & 127; - controls[channel * 128 + id] = evt.data[2] & 127; - } - } - fmidi_seq_next_event(&seq, nullptr); - } - - ctx.timepos = time; - - if (ctx.cbfn) { - uint8_t evtbuf[fmidi_event_sizeof(3)]; - fmidi_event_t *evt = (fmidi_event_t *)evtbuf; - evt->type = fmidi_event_message; - evt->delta = 0; - - for (unsigned c = 0; c < 16; ++c) { - // all sound off - evt->datalen = 3; - evt->data[0] = (0b1011 << 4) | c; - evt->data[1] = 120; - evt->data[2] = 0; - ctx.cbfn(evt, ctx.cbdata); - // reset all controllers - evt->datalen = 3; - evt->data[0] = (0b1011 << 4) | c; - evt->data[1] = 121; - evt->data[2] = 0; - ctx.cbfn(evt, ctx.cbdata); - // program change - evt->datalen = 2; - evt->data[0] = (0b1100 << 4) | c; - evt->data[1] = programs[c]; - ctx.cbfn(evt, ctx.cbdata); - // control change - for (unsigned id = 0; id < 128; ++id) { - uint8_t val = controls[c * 128 + id]; - if (val < 128) { - evt->datalen = 3; - evt->data[0] = (0b1011 << 4) | c; - evt->data[1] = id; - evt->data[2] = val; - ctx.cbfn(evt, ctx.cbdata); - } - } - } - } -} - -double fmidi_player_current_speed(const fmidi_player_t *plr) -{ - return plr->ctx.speed; -} - -void fmidi_player_set_speed(fmidi_player_t *plr, double speed) -{ - plr->ctx.speed = speed; -} - -void fmidi_player_event_callback( - fmidi_player_t *plr, void (*cbfn)(const fmidi_event_t *, void *), void *cbdata) -{ - fmidi_player_context &ctx = plr->ctx; - ctx.cbfn = cbfn; - ctx.cbdata = cbdata; -} - -void fmidi_player_finish_callback( - fmidi_player_t *plr, void (*cbfn)(void *), void *cbdata) -{ - fmidi_player_context &ctx = plr->ctx; - ctx.finifn = cbfn; - ctx.finidata = cbdata; + return _wfopen(wpath.get(), wmode.get()); +#endif } #include "fmidi/fmidi.h" @@ -1261,190 +1447,913 @@ bool fmidi_seq_next_event(fmidi_seq_t *seq, fmidi_seq_event_t *sqevt) return true; } - +#include "fmidi/fmidi.h" +#include #include -#include - -//////////////////////// -// FILE PATH ENCODING // -//////////////////////// - -FILE *fmidi_fopen(const char *path, const char *mode); - -/////////////// -// FILE RAII // -/////////////// - -struct FILE_deleter; -typedef std::unique_ptr unique_FILE; - -struct FILE_deleter { - void operator()(FILE *stream) const - { fclose(stream); } -}; +#include +#include +#include #if defined(_WIN32) -# include -# include -# include +# define fileno _fileno #endif -FILE *fmidi_fopen(const char *path, const char *mode) +const fmidi_smf_info_t *fmidi_smf_get_info(const fmidi_smf_t *smf) { -#if !defined(_WIN32) - return fopen(path, mode); -#else - auto toWideString = [](const char *utf8) -> wchar_t * { - unsigned wsize = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, nullptr, 0); - if (wsize == 0) - return nullptr; - wchar_t *wide = new wchar_t[wsize]; - wsize = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, wsize); - if (wsize == 0) { - delete[] wide; - return nullptr; + return &smf->info; +} + +double fmidi_smf_compute_duration(const fmidi_smf_t *smf) +{ + double duration = 0; + fmidi_seq_u seq(fmidi_seq_new(smf)); + fmidi_seq_event_t sqevt; + while (fmidi_seq_next_event(seq.get(), &sqevt)) + duration = sqevt.time; + return duration; +} + +static fmidi_event_t *fmidi_read_meta_event( + memstream &mb, std::vector &evbuf, uint32_t delta) +{ + memstream_status ms; + unsigned id; + if ((ms = mb.readbyte(&id))) + RET_FAIL(nullptr, (fmidi_status)ms); + + uint32_t datalen; + const uint8_t *data; + if (id == 0x2f || id == 0x3f) { // end of track + if (mb.skipbyte(0)) { + // omitted final null byte in some broken files } - return wide; - }; - std::unique_ptr wpath(toWideString(path)); - if (!wpath) { - errno = EINVAL; - return nullptr; + else { + // repeated end of track events + for (bool again = true; again;) { + size_t offset = mb.getpos(); + again = !mb.readvlq(nullptr) && !mb.skipbyte(0xff) && + (!mb.skipbyte(0x2f) || !mb.skipbyte(0x3f)); + if (!again) + mb.setpos(offset); + else + again = !mb.skipbyte(0); + } + } + datalen = 0; + data = nullptr; } - std::unique_ptr wmode(toWideString(mode)); - if (!wmode) { - errno = EINVAL; - return nullptr; + else { + if ((ms = mb.readvlq(&datalen))) + RET_FAIL(nullptr, (fmidi_status)ms); + if (!(data = mb.read(datalen))) + RET_FAIL(nullptr, fmidi_err_eof); } - return _wfopen(wpath.get(), wmode.get()); + fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen + 1); + evt->type = fmidi_event_meta; + evt->delta = delta; + evt->datalen = datalen + 1; + evt->data[0] = id; + memcpy(&evt->data[1], data, datalen); + return evt; +} + +static fmidi_event_t *fmidi_read_escape_event( + memstream &mb, std::vector &evbuf, uint32_t delta) +{ + memstream_status ms; + uint32_t datalen; + const uint8_t *data; + if ((ms = mb.readvlq(&datalen))) + RET_FAIL(nullptr, (fmidi_status)ms); + if (!(data = mb.read(datalen))) + RET_FAIL(nullptr, fmidi_err_eof); + + fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen); + evt->type = fmidi_event_escape; + evt->delta = delta; + evt->datalen = datalen; + memcpy(&evt->data[0], data, datalen); + return evt; +} + +static fmidi_event_t *fmidi_read_sysex_event( + memstream &mb, std::vector &evbuf, uint32_t delta) +{ + memstream_status ms; + fmidi_event_t *evt; + + std::vector syxbuf; + syxbuf.reserve(256); + syxbuf.push_back(0xf0); + + uint32_t partlen; + const uint8_t *part; + if ((ms = mb.readvlq(&partlen))) + RET_FAIL(nullptr, (fmidi_status)ms); + if (!(part = mb.read(partlen))) + RET_FAIL(nullptr, fmidi_err_eof); + + bool term = false; + const uint8_t *endp; + + // handle files having multiple concatenated sysex events in one + while ((endp = (const uint8_t *)memchr(part, 0xf7, partlen))) { + syxbuf.insert(syxbuf.end(), part, endp + 1); + + evt = fmidi_event_alloc(evbuf, syxbuf.size()); + evt->type = fmidi_event_message; + evt->delta = delta; + evt->datalen = syxbuf.size(); + memcpy(&evt->data[0], &syxbuf[0], syxbuf.size()); + + uint32_t reallen = endp + 1 - part; + partlen -= reallen; + part += reallen; + + if (partlen == 0) + return evt; + + if (part[0] != 0xf0) { +#if 1 + // trailing garbage, ignore +#else + // sierra: incorrect length covering part of the next event. repair + mb.setpos(mb.getpos() - partlen); #endif -} + return evt; + } + ++part; + --partlen; - -memstream_status memstream::setpos(size_t off) -{ - if (off > length_) - return ms_err_eof; - offset_ = off; - return ms_ok; -} - -memstream_status memstream::skip(size_t count) -{ - if (length_ - offset_ < count) - return ms_err_eof; - offset_ += count; - return ms_ok; -} - -memstream_status memstream::skipbyte(unsigned byte) -{ - unsigned otherbyte; - memstream_status status = peekbyte(&otherbyte); - if (status) - return status; - if (byte != otherbyte) - return ms_err_format; - ++offset_; - return ms_ok; -} - -const uint8_t *memstream::peek(size_t length) -{ - if (length > length_ - offset_) - return nullptr; - return base_ + offset_; -} - -const uint8_t *memstream::read(size_t length) -{ - const uint8_t *ptr = peek(length); - if (ptr) - offset_ += length; - return ptr; -} - -memstream_status memstream::peekbyte(unsigned *retp) -{ - if (length_ <= offset_) - return ms_err_eof; - if (retp) - *retp = base_[offset_]; - return ms_ok; -} - -memstream_status memstream::readbyte(unsigned *retp) -{ - memstream_status ret = peekbyte(retp); - if (ret) - return ret; - ++offset_; - return ms_ok; -} - -memstream_status memstream::readintLE(uint32_t *retp, unsigned length) -{ - const uint8_t *ptr = read(length); - if (!ptr) - return ms_err_eof; - uint32_t ret = 0; - for (unsigned i = length; i-- > 0;) - ret = (ret << 8) | ptr[i]; - if (retp) - *retp = ret; - return ms_ok; -} - -memstream_status memstream::readintBE(uint32_t *retp, unsigned length) -{ - const uint8_t *ptr = read(length); - if (!ptr) - return ms_err_eof; - uint32_t ret = 0; - for (unsigned i = 0; i < length; ++i) - ret = (ret << 8) | ptr[i]; - if (retp) - *retp = ret; - return ms_ok; -} - -memstream_status memstream::readvlq(uint32_t *retp) -{ - memstream_status ret; - uint32_t value; - unsigned length; - std::tie(ret, value, length) = doreadvlq(); - offset_ += length; - if (retp) - *retp = value; - return ret; -} - -memstream_status memstream::peekvlq(uint32_t *retp) -{ - memstream_status ret; - uint32_t value; - unsigned length; - std::tie(ret, value, length) = doreadvlq(); - if (retp) - *retp = value; - return ret; -} - -memstream::vlq_result memstream::doreadvlq() -{ - uint32_t ret = 0; - unsigned length; - bool cont = true; - for (length = 0; cont && length < 4; ++length) { - if (offset_ + length >= length_) - return vlq_result{ms_err_eof, 0, 0}; - uint8_t byte = base_[offset_ + length]; - ret = (ret << 7) | (byte & ((1u << 7) - 1)); - cont = byte & (1u << 7); + syxbuf.clear(); + syxbuf.push_back(0xf0); } - if (cont) - return vlq_result{ms_err_format, 0, 0}; - return vlq_result{ms_ok, ret, length}; + + // handle the rest in multiple parts (Casio MIDI) + while (!term) { + term = endp; + if (term && endp + 1 != part + partlen) { + // ensure no excess bytes + RET_FAIL(nullptr, fmidi_err_format); + } + syxbuf.insert(syxbuf.end(), part, part + partlen); + + if (!term) { + size_t offset = mb.getpos(); + bool havecont = false; + + uint32_t contdelta; + unsigned id; + if (!mb.readvlq(&contdelta) && !mb.readbyte(&id)) { + // raw sequence incoming? use it as next sysex part + havecont = id == 0xf7; + } + if (havecont) { + if ((ms = mb.readvlq(&partlen))) + RET_FAIL(nullptr, (fmidi_status)ms); + if (!(part = mb.read(partlen))) + RET_FAIL(nullptr, fmidi_err_eof); + endp = (const uint8_t *)memchr(part, 0xf7, partlen); + } + else { + // no next part? assume unfinished message and repair + mb.setpos(offset); + syxbuf.push_back(0xf7); + term = true; + } + } + } + + evt = fmidi_event_alloc(evbuf, syxbuf.size()); + evt->type = fmidi_event_message; + evt->delta = delta; + evt->datalen = syxbuf.size(); + memcpy(&evt->data[0], &syxbuf[0], syxbuf.size()); + return evt; +} + +static fmidi_event_t *fmidi_read_message_event( + memstream &mb, std::vector &evbuf, unsigned id, uint32_t delta) +{ + uint32_t datalen = fmidi_message_sizeof(id); + const uint8_t *data; + if (datalen <= 0) + RET_FAIL(nullptr, fmidi_err_format); + if (!(data = mb.read(datalen - 1))) + RET_FAIL(nullptr, fmidi_err_eof); + + fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen); + evt->type = fmidi_event_message; + evt->delta = delta; + evt->datalen = datalen; + evt->data[0] = id; + memcpy(&evt->data[1], data, datalen - 1); + return evt; +} + +static fmidi_event_t *fmidi_read_event( + memstream &mb, std::vector &evbuf, uint8_t *runstatus) +{ + memstream_status ms; + uint32_t delta; + unsigned id; + if ((ms = mb.readvlq(&delta))) + RET_FAIL(nullptr, (fmidi_status)ms); + if ((ms = mb.readbyte(&id))) + RET_FAIL(nullptr, (fmidi_status)ms); + + fmidi_event_t *evt; + if (id == 0xff) { + evt = fmidi_read_meta_event(mb, evbuf, delta); + } + else if (id == 0xf7) { + evt = fmidi_read_escape_event(mb, evbuf, delta); + } + else if (id == 0xf0) { + evt = fmidi_read_sysex_event(mb, evbuf, delta); + } + else { + if (id & 128) { + *runstatus = id; + } + else { + id = *runstatus; + mb.setpos(mb.getpos() - 1); + } + evt = fmidi_read_message_event(mb, evbuf, id, delta); + } + + return evt; +} + +void fmidi_smf_track_begin(fmidi_track_iter_t *it, uint16_t track) +{ + it->track = track; + it->index = 0; +} + +const fmidi_event_t *fmidi_smf_track_next( + const fmidi_smf_t *smf, fmidi_track_iter_t *it) +{ + if (it->track >= smf->info.track_count) + return nullptr; + + const fmidi_raw_track &trk = smf->track[it->track]; + const uint8_t *trkdata = trk.data.get(); + + const fmidi_event_t *evt = (const fmidi_event_t *)&trkdata[it->index]; + if ((const uint8_t *)evt == trkdata + trk.length) + return nullptr; + + it->index += fmidi_event_pad(fmidi_event_sizeof(evt->datalen)); + return evt; +} + +static bool fmidi_smf_read_contents(fmidi_smf_t *smf, memstream &mb) +{ + uint16_t ntracks = smf->info.track_count; + smf->track.reset(new fmidi_raw_track[ntracks]); + + std::vector evbuf; + evbuf.reserve(8192); + + uint8_t runstatus = 0; // status runs from track to track + + for (unsigned itrack = 0; itrack < ntracks; ++itrack) { + fmidi_raw_track &trk = smf->track[itrack]; + size_t trkoffset = mb.getpos(); + + memstream_status ms; + const uint8_t *trackmagic; + uint32_t tracklen; + + if (!(trackmagic = mb.read(4))) { + // file has less tracks than promised, repair + smf->info.track_count = ntracks = itrack; + break; + } + + if (memcmp(trackmagic, "MTrk", 4)) { + if (mb.getpos() == mb.endpos()) { + // some kind of final junk header, ignore + smf->info.track_count = ntracks = itrack; + break; + } + RET_FAIL(false, fmidi_err_format); + } + if ((ms = mb.readintBE(&tracklen, 4))) + RET_FAIL(false, (fmidi_status)ms); + + // check track length, broken in many files. disregard if invalid + bool tracklengood = !mb.skip(tracklen) && + (mb.getpos() == mb.endpos() || + ((trackmagic = mb.peek(4)) && !memcmp(trackmagic, "MTrk", 4))); + mb.setpos(trkoffset + 8); + + fmidi_event_t *evt; + size_t evoffset = mb.getpos(); + bool endoftrack = false; + evbuf.clear(); + while (!endoftrack && (evt = fmidi_read_event(mb, evbuf, &runstatus))) { + // some files use 3F instead or 2F for end of track + endoftrack = evt->type == fmidi_event_meta && + (evt->data[0] == 0x2f || evt->data[0] == 0x3f); + // fmt::print(stderr, "T{} @{:#x} {}\n", itrack, evoffset, *evt); + evoffset = mb.getpos(); + if (tracklengood && evoffset > trkoffset + 8 + tracklen) + // next track overlap + RET_FAIL(false, fmidi_err_format); + } + + if (!endoftrack) { + switch (fmidi_last_error.code) { + case fmidi_err_eof: + // truncated track? stop reading + smf->info.track_count = ntracks = itrack + 1; + break; + case fmidi_err_format: + // event with absurdly high delta time? ignore the rest of + // the track and if possible proceed to the next + mb.setpos(evoffset); + if (mb.peekvlq(nullptr) == ms_err_format) { + if (!tracklengood) + smf->info.track_count = ntracks = itrack + 1; + break; + } + return false; + default: + return false; + } + } + + if (endoftrack) { + // permit meta events coming after end of track + const uint8_t *head; + while ((head = mb.peek(2)) && head[0] == 0x00 && head[1] == 0xff) { + if (!(evt = fmidi_read_event(mb, evbuf, &runstatus))) { + if (fmidi_last_error.code == fmidi_err_eof) + smf->info.track_count = ntracks = itrack + 1; + else + return false; + } + else if (tracklengood && mb.getpos() > trkoffset + 8 + tracklen) + // next track overlap + RET_FAIL(false, fmidi_err_format); + } + } + + uint32_t evdatalen = trk.length = evbuf.size(); + uint8_t *evdata = new uint8_t[evdatalen]; + trk.data.reset(evdata); + memcpy(evdata, evbuf.data(), evdatalen); + + if (tracklengood) + mb.setpos(trkoffset + 8 + tracklen); + } + + return true; +} + +fmidi_smf_t *fmidi_smf_mem_read(const uint8_t *data, size_t length) +{ + memstream mb(data, length); + memstream_status ms; + const uint8_t *filemagic; + uint32_t headerlen; + uint32_t format; + uint32_t ntracks; + uint32_t deltaunit; + + while ((filemagic = mb.peek(4)) && memcmp(filemagic, "MThd", 4)) + mb.skip(1); + mb.skip(4); + + if (!filemagic) + RET_FAIL(nullptr, fmidi_err_format); + + if ((ms = mb.readintBE(&headerlen, 4)) || + (ms = mb.readintBE(&format, 2)) || + (ms = mb.readintBE(&ntracks, 2)) || + (ms = mb.readintBE(&deltaunit, 2))) + RET_FAIL(nullptr, (fmidi_status)ms); + + if (ntracks < 1 || headerlen < 6) + RET_FAIL(nullptr, fmidi_err_format); + + if ((ms = mb.skip(headerlen - 6))) + RET_FAIL(nullptr, (fmidi_status)ms); + + std::unique_ptr smf(new fmidi_smf_t); + smf->info.format = format; + smf->info.track_count = ntracks; + smf->info.delta_unit = deltaunit; + + if (!fmidi_smf_read_contents(smf.get(), mb)) + return nullptr; + + return smf.release(); +} + +void fmidi_smf_free(fmidi_smf_t *smf) +{ + delete smf; +} + +fmidi_smf_t *fmidi_smf_file_read(const char *filename) +{ + unique_FILE fh(fmidi_fopen(filename, "rb")); + if (!fh) + RET_FAIL(nullptr, fmidi_err_input); + + fmidi_smf_t *smf = fmidi_smf_stream_read(fh.get()); + return smf; +} + +fmidi_smf_t *fmidi_smf_stream_read(FILE *stream) +{ + struct stat st; + size_t length; + + rewind(stream); + + if (fstat(fileno(stream), &st) != 0) + RET_FAIL(nullptr, fmidi_err_input); + + length = st.st_size; + if (length > fmidi_file_size_limit) + RET_FAIL(nullptr, fmidi_err_largefile); + + std::unique_ptr buf(new uint8_t[length]); + if (!fread(buf.get(), length, 1, stream)) + RET_FAIL(nullptr, fmidi_err_input); + + fmidi_smf_t *smf = fmidi_smf_mem_read(buf.get(), length); + return smf; +} + +#include "fmidi/fmidi.h" +#include +#include + +static void write_vlq(uint32_t value, Writer &writer) +{ + unsigned shift = 28; + unsigned mask = (1u << 7) - 1; + while (shift > 0 && ((value >> shift) & mask) == 0) + shift -= 7; + while (shift > 0) { + writer.put(((value >> shift) & mask) | (1u << 7)); + shift -= 7; + } + writer.put(value & mask); +} + +static bool fmidi_smf_write(const fmidi_smf_t *smf, Writer &writer) +{ + writer.write("MThd", 4); + + const uint32_t header_size = 6; + writer.writeBE(&header_size, 4); + + const fmidi_smf_info_t *info = fmidi_smf_get_info(smf); + const uint16_t track_count = info->track_count; + writer.writeBE(&info->format, 2); + writer.writeBE(&track_count, 2); + writer.writeBE(&info->delta_unit, 2); + + for (unsigned i = 0; i < track_count; ++i) { + writer.write("MTrk", 4); + + off_t off_track_length = writer.tell(); + + uint32_t track_length = 0; + writer.writeBE(&track_length, 4); + + int running_status = -1; + + fmidi_track_iter_t iter; + fmidi_smf_track_begin(&iter, i); + + const fmidi_event_t *event; + while ((event = fmidi_smf_track_next(smf, &iter))) { + switch (event->type) { + case fmidi_event_meta: + write_vlq(event->delta, writer); + writer.put(0xff); + writer.put(event->data[0]); + write_vlq(event->datalen - 1, writer); + writer.write(event->data + 1, event->datalen - 1); + running_status = -1; + break; + case fmidi_event_message: + { + write_vlq(event->delta, writer); + uint8_t status = event->data[0]; + if (status == 0xf0) { + writer.put(0xf0); + write_vlq(event->datalen - 1, writer); + writer.write(event->data + 1, event->datalen - 1); + running_status = -1; + } + else if ((int)status == running_status) + writer.write(event->data + 1, event->datalen - 1); + else { + writer.write(event->data, event->datalen); + running_status = status; + } + break; + } + case fmidi_event_escape: + write_vlq(event->delta, writer); + writer.put(0xf7); + write_vlq(event->datalen, writer); + writer.write(event->data, event->datalen); + running_status = -1; + break; + case fmidi_event_xmi_timbre: + case fmidi_event_xmi_branch_point: + break; + } + } + + off_t off_track_end = writer.tell(); + + track_length = + std::make_unsigned::type(off_track_end) - + std::make_unsigned::type(off_track_length) - 4; + + writer.seek(off_track_length, SEEK_SET); + writer.writeBE(&track_length, 4); + writer.seek(off_track_end, SEEK_SET); + } + + return true; +} + +bool fmidi_smf_mem_write(const fmidi_smf_t *smf, uint8_t **data, size_t *length) +{ + std::vector mem; + mem.reserve(8192); + + Memory_Writer writer(mem); + if (!fmidi_smf_write(smf, writer)) + return false; + + assert(data); + assert(length); + + if (!(*data = (uint8_t *)malloc(mem.size()))) + throw std::bad_alloc(); + + memcpy(*data, mem.data(), mem.size()); + *length = mem.size(); + + return true; +} + +bool fmidi_smf_file_write(const fmidi_smf_t *smf, const char *filename) +{ + unique_FILE fh(fmidi_fopen(filename, "wb")); + if (!fh) + RET_FAIL(false, fmidi_err_output); + + return fmidi_smf_stream_write(smf, fh.get()); +} + +bool fmidi_smf_stream_write(const fmidi_smf_t *smf, FILE *stream) +{ + Stream_Writer writer(stream); + if (!fmidi_smf_write(smf, writer)) + return false; + + if (fflush(stream) != 0) + RET_FAIL(false, fmidi_err_output); + + return true; +} + +#include "fmidi/fmidi.h" +#include + +fmidi_fileformat_t fmidi_mem_identify(const uint8_t *data, size_t length) +{ + const uint8_t smf_magic[4] = {'M', 'T', 'h', 'd'}; + + for (size_t offset : {0x00, 0x80}) { + // a few unidentified files start at 0x80 (Sound Canvas MIDI collection) + if (length + offset >= 4 && memcmp(data + offset, smf_magic, 4) == 0) + return fmidi_fileformat_smf; + } + + const uint8_t rmi_magic1[4] = {'R', 'I', 'F', 'F'}; + const uint8_t rmi_magic2[8] = {'R', 'M', 'I', 'D', 'd', 'a', 't', 'a'}; + if (length >= 16 && memcmp(data, rmi_magic1, 4) == 0 && memcmp(data + 8, rmi_magic2, 8) == 0) + return fmidi_fileformat_smf; + + const uint8_t xmi_magic[20] = { + 'F', 'O', 'R', 'M', 0, 0, 0, 14, + 'X', 'D', 'I', 'R', 'I', 'N', 'F', 'O', 0, 0, 0, 2 + }; + if (length >= 20 && memcmp(data, xmi_magic, 20) == 0) + return fmidi_fileformat_xmi; + + const uint8_t mus_magic[4] = {'M', 'U', 'S', 0x1a}; + if (length >= 4 && memcmp(data, mus_magic, 4) == 0) + return fmidi_fileformat_mus; + + RET_FAIL((fmidi_fileformat_t)-1, fmidi_err_format); +} + +fmidi_fileformat_t fmidi_stream_identify(FILE *stream) +{ + rewind(stream); + + uint8_t magic[0x100]; + size_t size = fread(magic, 1, sizeof(magic), stream); + if (ferror(stream)) + RET_FAIL((fmidi_fileformat_t)-1, fmidi_err_input); + + return fmidi_mem_identify(magic, size); +} + +fmidi_smf_t *fmidi_auto_mem_read(const uint8_t *data, size_t length) +{ + switch (fmidi_mem_identify(data, length)) { + case fmidi_fileformat_smf: + return fmidi_smf_mem_read(data, length); + case fmidi_fileformat_xmi: + return fmidi_xmi_mem_read(data, length); + case fmidi_fileformat_mus: + return fmidi_mus_mem_read(data, length); + default: + return nullptr; + } +} + +fmidi_smf_t *fmidi_auto_file_read(const char *filename) +{ + unique_FILE fh(fmidi_fopen(filename, "rb")); + if (!fh) + RET_FAIL(nullptr, fmidi_err_input); + + fmidi_smf_t *smf = fmidi_auto_stream_read(fh.get()); + return smf; +} + +fmidi_smf_t *fmidi_auto_stream_read(FILE *stream) +{ + switch (fmidi_stream_identify(stream)) { + case fmidi_fileformat_smf: + return fmidi_smf_stream_read(stream); + case fmidi_fileformat_xmi: + return fmidi_xmi_stream_read(stream); + case fmidi_fileformat_mus: + return fmidi_mus_stream_read(stream); + default: + return nullptr; + } +} + +#include "fmidi/fmidi.h" +#include + +fmidi_smf_t *fmidi_mus_mem_read(const uint8_t *data, size_t length) +{ + const uint8_t magic[] = {'M', 'U', 'S', 0x1a}; + + if (length < sizeof(magic) || memcmp(data, magic, 4)) + RET_FAIL(nullptr, fmidi_err_format); + + memstream mb(data + sizeof(magic), length - sizeof(magic)); + memstream_status ms; + + uint32_t score_len; + uint32_t score_start; + uint32_t channels; + uint32_t sec_channels; + uint32_t instr_cnt; + + if ((ms = mb.readintLE(&score_len, 2)) || + (ms = mb.readintLE(&score_start, 2)) || + (ms = mb.readintLE(&channels, 2)) || + (ms = mb.readintLE(&sec_channels, 2)) || + (ms = mb.readintLE(&instr_cnt, 2)) || + (ms = mb.skip(2))) + RET_FAIL(nullptr, fmidi_err_format); + + std::unique_ptr instrs{new uint32_t[instr_cnt]}; + for (uint32_t i = 0; i < instr_cnt; ++i) { + if ((ms = mb.readintLE(&instrs[i], 2))) + RET_FAIL(nullptr, fmidi_err_format); + } + + fmidi_smf_u smf(new fmidi_smf); + smf->info.format = 0; + smf->info.track_count = 1; + smf->info.delta_unit = 70; // DMX 140 Hz -> PPQN at 120 BPM + smf->track.reset(new fmidi_raw_track[1]); + + fmidi_raw_track &track = smf->track[0]; + std::vector evbuf; + evbuf.reserve(8192); + + uint32_t ev_delta = 0; + uint32_t note_velocity[16] = {}; + + for (unsigned channel = 0; channel < 16; ++channel) { + // initial velocity + note_velocity[channel] = 64; + // channel volume + fmidi_event_t *event = fmidi_event_alloc(evbuf, 3); + event->type = fmidi_event_message; + event->delta = ev_delta; + event->datalen = 3; + uint8_t *data = event->data; + data[0] = 0xb0 | channel; + data[1] = 7; + data[2] = 127; + } + + for (bool score_end = false; !score_end;) { + uint32_t ev_desc; + if ((ms = mb.readintLE(&ev_desc, 1))) + RET_FAIL(nullptr, fmidi_err_format); + + const uint8_t mus_channel_to_midi_channel[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 10, 11, 12, 13, 14, 15, 9 + }; + + bool ev_last = (ev_desc & 128) != 0; + uint32_t ev_type = (ev_desc >> 4) & 7; + uint32_t ev_channel = mus_channel_to_midi_channel[ev_desc & 15]; + + uint8_t midi[3] {}; + uint8_t midi_size = 0; + + switch (ev_type) { + // release note + case 0: { + uint32_t data1; + if ((ms = mb.readintLE(&data1, 1))) + RET_FAIL(nullptr, fmidi_err_format); + midi[0] = 0x80 | ev_channel; + midi[1] = data1 & 127; + midi[2] = 64; + midi_size = 3; + break; + } + // play note + case 1: { + uint32_t data1; + if ((ms = mb.readintLE(&data1, 1))) + RET_FAIL(nullptr, fmidi_err_format); + if (data1 & 128) { + uint32_t data2; + if ((ms = mb.readintLE(&data2, 1))) + RET_FAIL(nullptr, fmidi_err_format); + note_velocity[ev_channel] = data2 & 127; + } + midi[0] = 0x90 | ev_channel; + midi[1] = data1 & 127; + midi[2] = note_velocity[ev_channel]; + midi_size = 3; + break; + } + // pitch wheel + case 2: { + uint32_t data1; + if ((ms = mb.readintLE(&data1, 1))) + RET_FAIL(nullptr, fmidi_err_format); + uint32_t bend = (data1 < 128) ? (data1 << 6) : + (8192 + (data1 - 128) * 8191 / 127); + midi[0] = 0xe0 | ev_channel; + midi[1] = bend & 127; + midi[2] = bend >> 7; + midi_size = 3; + break; + } + // system event + case 3: { + uint32_t data1; + if ((ms = mb.readintLE(&data1, 1))) + RET_FAIL(nullptr, fmidi_err_format); + midi[0] = 0xb0 | ev_channel; + midi[2] = 0; + midi_size = 3; + switch (data1 & 127) { + case 10: midi[1] = 120; break; + case 11: midi[1] = 123; break; + case 12: midi[1] = 126; break; + case 13: midi[1] = 127; break; + case 14: midi[1] = 121; break; + default: midi_size = 0; break; + } + break; + } + // change controller + case 4: { + uint32_t data1; + if ((ms = mb.readintLE(&data1, 1))) + RET_FAIL(nullptr, fmidi_err_format); + uint32_t data2; + if ((ms = mb.readintLE(&data2, 1))) + RET_FAIL(nullptr, fmidi_err_format); + midi[0] = 0xb0 | ev_channel; + midi[2] = data2 & 127; + midi_size = 3; + switch (data1 & 127) { + case 0: + // program change + midi[0] = 0xc0 | ev_channel; + midi[1] = data2 & 127; + midi_size = 2; + break; + case 1: midi[1] = 0; break; + case 2: midi[1] = 1; break; + case 3: midi[1] = 7; break; + case 4: midi[1] = 10; break; + case 5: midi[1] = 11; break; + case 6: midi[1] = 91; break; + case 7: midi[1] = 93; break; + case 8: midi[1] = 64; break; + case 9: midi[1] = 67; break; + default: midi_size = 0; break; + } + break; + } + // end of measure + case 5: { + break; + } + // score end + case 6: { + score_end = true; + break; + } + // unknown purpose + case 7: { + if ((ms = mb.skip(1))) + RET_FAIL(nullptr, fmidi_err_format); + break; + } + } + + uint32_t delta_inc = 0; + if (ev_last) { + if ((ms = mb.readvlq(&delta_inc))) + RET_FAIL(nullptr, fmidi_err_format); + ev_desc += delta_inc; + } + + if (midi_size > 0) { + fmidi_event_t *event = fmidi_event_alloc(evbuf, midi_size); + event->type = fmidi_event_message; + event->delta = ev_delta; + event->datalen = midi_size; + memcpy(event->data, midi, midi_size); + ev_delta = 0; + } + + ev_delta += delta_inc; + } + + fmidi_event_t *event = fmidi_event_alloc(evbuf, 1); + event->type = fmidi_event_meta; + event->delta = ev_delta; + event->datalen = 1; + event->data[0] = 0x2f; + + uint32_t evdatalen = track.length = evbuf.size(); + uint8_t *evdata = new uint8_t[evdatalen]; + track.data.reset(evdata); + memcpy(evdata, evbuf.data(), evdatalen); + + return smf.release(); +} + +fmidi_smf_t *fmidi_mus_file_read(const char *filename) +{ + unique_FILE fh(fmidi_fopen(filename, "rb")); + if (!fh) + RET_FAIL(nullptr, fmidi_err_input); + + fmidi_smf_t *smf = fmidi_mus_stream_read(fh.get()); + return smf; +} + +fmidi_smf_t *fmidi_mus_stream_read(FILE *stream) +{ + rewind(stream); + + constexpr size_t mus_file_size_limit = 65536; + uint8_t buf[mus_file_size_limit]; + + size_t length = fread(buf, 1, mus_file_size_limit, stream); + if (ferror(stream)) + RET_FAIL(nullptr, fmidi_err_input); + + fmidi_smf_t *smf = fmidi_mus_mem_read(buf, length); + return smf; } #include "fmidi/fmidi.h" @@ -1908,657 +2817,3 @@ fmidi_smf_t *fmidi_xmi_stream_read(FILE *stream) fmidi_smf_t *smf = fmidi_xmi_mem_read(buf.get(), length + pad); return smf; } - -#include "fmidi/fmidi.h" -#include -#include -#include -#include -#include -#if defined(_WIN32) -# define fileno _fileno -#endif - -const fmidi_smf_info_t *fmidi_smf_get_info(const fmidi_smf_t *smf) -{ - return &smf->info; -} - -double fmidi_smf_compute_duration(const fmidi_smf_t *smf) -{ - double duration = 0; - fmidi_seq_u seq(fmidi_seq_new(smf)); - fmidi_seq_event_t sqevt; - while (fmidi_seq_next_event(seq.get(), &sqevt)) - duration = sqevt.time; - return duration; -} - -static fmidi_event_t *fmidi_read_meta_event( - memstream &mb, std::vector &evbuf, uint32_t delta) -{ - memstream_status ms; - unsigned id; - if ((ms = mb.readbyte(&id))) - RET_FAIL(nullptr, (fmidi_status)ms); - - uint32_t datalen; - const uint8_t *data; - if (id == 0x2f || id == 0x3f) { // end of track - if (mb.skipbyte(0)) { - // omitted final null byte in some broken files - } - else { - // repeated end of track events - for (bool again = true; again;) { - size_t offset = mb.getpos(); - again = !mb.readvlq(nullptr) && !mb.skipbyte(0xff) && - (!mb.skipbyte(0x2f) || !mb.skipbyte(0x3f)); - if (!again) - mb.setpos(offset); - else - again = !mb.skipbyte(0); - } - } - datalen = 0; - data = nullptr; - } - else { - if ((ms = mb.readvlq(&datalen))) - RET_FAIL(nullptr, (fmidi_status)ms); - if (!(data = mb.read(datalen))) - RET_FAIL(nullptr, fmidi_err_eof); - } - fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen + 1); - evt->type = fmidi_event_meta; - evt->delta = delta; - evt->datalen = datalen + 1; - evt->data[0] = id; - memcpy(&evt->data[1], data, datalen); - return evt; -} - -static fmidi_event_t *fmidi_read_escape_event( - memstream &mb, std::vector &evbuf, uint32_t delta) -{ - memstream_status ms; - uint32_t datalen; - const uint8_t *data; - if ((ms = mb.readvlq(&datalen))) - RET_FAIL(nullptr, (fmidi_status)ms); - if (!(data = mb.read(datalen))) - RET_FAIL(nullptr, fmidi_err_eof); - - fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen); - evt->type = fmidi_event_escape; - evt->delta = delta; - evt->datalen = datalen; - memcpy(&evt->data[0], data, datalen); - return evt; -} - -static fmidi_event_t *fmidi_read_sysex_event( - memstream &mb, std::vector &evbuf, uint32_t delta) -{ - memstream_status ms; - fmidi_event_t *evt; - - std::vector syxbuf; - syxbuf.reserve(256); - syxbuf.push_back(0xf0); - - uint32_t partlen; - const uint8_t *part; - if ((ms = mb.readvlq(&partlen))) - RET_FAIL(nullptr, (fmidi_status)ms); - if (!(part = mb.read(partlen))) - RET_FAIL(nullptr, fmidi_err_eof); - - bool term = false; - const uint8_t *endp; - - // handle files having multiple concatenated sysex events in one - while ((endp = (const uint8_t *)memchr(part, 0xf7, partlen))) { - syxbuf.insert(syxbuf.end(), part, endp + 1); - - evt = fmidi_event_alloc(evbuf, syxbuf.size()); - evt->type = fmidi_event_message; - evt->delta = delta; - evt->datalen = syxbuf.size(); - memcpy(&evt->data[0], &syxbuf[0], syxbuf.size()); - - uint32_t reallen = endp + 1 - part; - partlen -= reallen; - part += reallen; - - if (partlen == 0) - return evt; - - if (part[0] != 0xf0) { -#if 1 - // trailing garbage, ignore -#else - // sierra: incorrect length covering part of the next event. repair - mb.setpos(mb.getpos() - partlen); -#endif - return evt; - } - ++part; - --partlen; - - syxbuf.clear(); - syxbuf.push_back(0xf0); - } - - // handle the rest in multiple parts (Casio MIDI) - while (!term) { - term = endp; - if (term && endp + 1 != part + partlen) { - // ensure no excess bytes - RET_FAIL(nullptr, fmidi_err_format); - } - syxbuf.insert(syxbuf.end(), part, part + partlen); - - if (!term) { - size_t offset = mb.getpos(); - bool havecont = false; - - uint32_t contdelta; - unsigned id; - if (!mb.readvlq(&contdelta) && !mb.readbyte(&id)) { - // raw sequence incoming? use it as next sysex part - havecont = id == 0xf7; - } - if (havecont) { - if ((ms = mb.readvlq(&partlen))) - RET_FAIL(nullptr, (fmidi_status)ms); - if (!(part = mb.read(partlen))) - RET_FAIL(nullptr, fmidi_err_eof); - endp = (const uint8_t *)memchr(part, 0xf7, partlen); - } - else { - // no next part? assume unfinished message and repair - mb.setpos(offset); - syxbuf.push_back(0xf7); - term = true; - } - } - } - - evt = fmidi_event_alloc(evbuf, syxbuf.size()); - evt->type = fmidi_event_message; - evt->delta = delta; - evt->datalen = syxbuf.size(); - memcpy(&evt->data[0], &syxbuf[0], syxbuf.size()); - return evt; -} - -static fmidi_event_t *fmidi_read_message_event( - memstream &mb, std::vector &evbuf, unsigned id, uint32_t delta) -{ - uint32_t datalen = fmidi_message_sizeof(id); - const uint8_t *data; - if (datalen <= 0) - RET_FAIL(nullptr, fmidi_err_format); - if (!(data = mb.read(datalen - 1))) - RET_FAIL(nullptr, fmidi_err_eof); - - fmidi_event_t *evt = fmidi_event_alloc(evbuf, datalen); - evt->type = fmidi_event_message; - evt->delta = delta; - evt->datalen = datalen; - evt->data[0] = id; - memcpy(&evt->data[1], data, datalen - 1); - return evt; -} - -static fmidi_event_t *fmidi_read_event( - memstream &mb, std::vector &evbuf, uint8_t *runstatus) -{ - memstream_status ms; - uint32_t delta; - unsigned id; - if ((ms = mb.readvlq(&delta))) - RET_FAIL(nullptr, (fmidi_status)ms); - if ((ms = mb.readbyte(&id))) - RET_FAIL(nullptr, (fmidi_status)ms); - - fmidi_event_t *evt; - if (id == 0xff) { - evt = fmidi_read_meta_event(mb, evbuf, delta); - } - else if (id == 0xf7) { - evt = fmidi_read_escape_event(mb, evbuf, delta); - } - else if (id == 0xf0) { - evt = fmidi_read_sysex_event(mb, evbuf, delta); - } - else { - if (id & 128) { - *runstatus = id; - } - else { - id = *runstatus; - mb.setpos(mb.getpos() - 1); - } - evt = fmidi_read_message_event(mb, evbuf, id, delta); - } - - return evt; -} - -void fmidi_smf_track_begin(fmidi_track_iter_t *it, uint16_t track) -{ - it->track = track; - it->index = 0; -} - -const fmidi_event_t *fmidi_smf_track_next( - const fmidi_smf_t *smf, fmidi_track_iter_t *it) -{ - if (it->track >= smf->info.track_count) - return nullptr; - - const fmidi_raw_track &trk = smf->track[it->track]; - const uint8_t *trkdata = trk.data.get(); - - const fmidi_event_t *evt = (const fmidi_event_t *)&trkdata[it->index]; - if ((const uint8_t *)evt == trkdata + trk.length) - return nullptr; - - it->index += fmidi_event_pad(fmidi_event_sizeof(evt->datalen)); - return evt; -} - -static bool fmidi_smf_read_contents(fmidi_smf_t *smf, memstream &mb) -{ - uint16_t ntracks = smf->info.track_count; - smf->track.reset(new fmidi_raw_track[ntracks]); - - std::vector evbuf; - evbuf.reserve(8192); - - uint8_t runstatus = 0; // status runs from track to track - - for (unsigned itrack = 0; itrack < ntracks; ++itrack) { - fmidi_raw_track &trk = smf->track[itrack]; - size_t trkoffset = mb.getpos(); - - memstream_status ms; - const uint8_t *trackmagic; - uint32_t tracklen; - - if (!(trackmagic = mb.read(4))) { - // file has less tracks than promised, repair - smf->info.track_count = ntracks = itrack; - break; - } - - if (memcmp(trackmagic, "MTrk", 4)) { - if (mb.getpos() == mb.endpos()) { - // some kind of final junk header, ignore - smf->info.track_count = ntracks = itrack; - break; - } - RET_FAIL(false, fmidi_err_format); - } - if ((ms = mb.readintBE(&tracklen, 4))) - RET_FAIL(false, (fmidi_status)ms); - - // check track length, broken in many files. disregard if invalid - bool tracklengood = !mb.skip(tracklen) && - (mb.getpos() == mb.endpos() || - ((trackmagic = mb.peek(4)) && !memcmp(trackmagic, "MTrk", 4))); - mb.setpos(trkoffset + 8); - - fmidi_event_t *evt; - size_t evoffset = mb.getpos(); - bool endoftrack = false; - evbuf.clear(); - while (!endoftrack && (evt = fmidi_read_event(mb, evbuf, &runstatus))) { - // some files use 3F instead or 2F for end of track - endoftrack = evt->type == fmidi_event_meta && - (evt->data[0] == 0x2f || evt->data[0] == 0x3f); - // fmt::print(stderr, "T{} @{:#x} {}\n", itrack, evoffset, *evt); - evoffset = mb.getpos(); - if (tracklengood && evoffset > trkoffset + 8 + tracklen) - // next track overlap - RET_FAIL(false, fmidi_err_format); - } - - if (!endoftrack) { - switch (fmidi_last_error.code) { - case fmidi_err_eof: - // truncated track? stop reading - smf->info.track_count = ntracks = itrack + 1; - break; - case fmidi_err_format: - // event with absurdly high delta time? ignore the rest of - // the track and if possible proceed to the next - mb.setpos(evoffset); - if (mb.peekvlq(nullptr) == ms_err_format) { - if (!tracklengood) - smf->info.track_count = ntracks = itrack + 1; - break; - } - return false; - default: - return false; - } - } - - if (endoftrack) { - // permit meta events coming after end of track - const uint8_t *head; - while ((head = mb.peek(2)) && head[0] == 0x00 && head[1] == 0xff) { - if (!(evt = fmidi_read_event(mb, evbuf, &runstatus))) { - if (fmidi_last_error.code == fmidi_err_eof) - smf->info.track_count = ntracks = itrack + 1; - else - return false; - } - else if (tracklengood && mb.getpos() > trkoffset + 8 + tracklen) - // next track overlap - RET_FAIL(false, fmidi_err_format); - } - } - - uint32_t evdatalen = trk.length = evbuf.size(); - uint8_t *evdata = new uint8_t[evdatalen]; - trk.data.reset(evdata); - memcpy(evdata, evbuf.data(), evdatalen); - - if (tracklengood) - mb.setpos(trkoffset + 8 + tracklen); - } - - return true; -} - -fmidi_smf_t *fmidi_smf_mem_read(const uint8_t *data, size_t length) -{ - memstream mb(data, length); - memstream_status ms; - const uint8_t *filemagic; - uint32_t headerlen; - uint32_t format; - uint32_t ntracks; - uint32_t deltaunit; - - while ((filemagic = mb.peek(4)) && memcmp(filemagic, "MThd", 4)) - mb.skip(1); - mb.skip(4); - - if (!filemagic) - RET_FAIL(nullptr, fmidi_err_format); - - if ((ms = mb.readintBE(&headerlen, 4)) || - (ms = mb.readintBE(&format, 2)) || - (ms = mb.readintBE(&ntracks, 2)) || - (ms = mb.readintBE(&deltaunit, 2))) - RET_FAIL(nullptr, (fmidi_status)ms); - - if (ntracks < 1 || headerlen < 6) - RET_FAIL(nullptr, fmidi_err_format); - - if ((ms = mb.skip(headerlen - 6))) - RET_FAIL(nullptr, (fmidi_status)ms); - - std::unique_ptr smf(new fmidi_smf_t); - smf->info.format = format; - smf->info.track_count = ntracks; - smf->info.delta_unit = deltaunit; - - if (!fmidi_smf_read_contents(smf.get(), mb)) - return nullptr; - - return smf.release(); -} - -void fmidi_smf_free(fmidi_smf_t *smf) -{ - delete smf; -} - -fmidi_smf_t *fmidi_smf_file_read(const char *filename) -{ - unique_FILE fh(fmidi_fopen(filename, "rb")); - if (!fh) - RET_FAIL(nullptr, fmidi_err_input); - - fmidi_smf_t *smf = fmidi_smf_stream_read(fh.get()); - return smf; -} - -fmidi_smf_t *fmidi_smf_stream_read(FILE *stream) -{ - struct stat st; - size_t length; - - rewind(stream); - - if (fstat(fileno(stream), &st) != 0) - RET_FAIL(nullptr, fmidi_err_input); - - length = st.st_size; - if (length > fmidi_file_size_limit) - RET_FAIL(nullptr, fmidi_err_largefile); - - std::unique_ptr buf(new uint8_t[length]); - if (!fread(buf.get(), length, 1, stream)) - RET_FAIL(nullptr, fmidi_err_input); - - fmidi_smf_t *smf = fmidi_smf_mem_read(buf.get(), length); - return smf; -} - -#include "fmidi/fmidi.h" -#include - -fmidi_fileformat_t fmidi_mem_identify(const uint8_t *data, size_t length) -{ - const uint8_t smf_magic[4] = {'M', 'T', 'h', 'd'}; - if (length >= 4 && memcmp(data, smf_magic, 4) == 0) - return fmidi_fileformat_smf; - - const uint8_t rmi_magic1[4] = {'R', 'I', 'F', 'F'}; - const uint8_t rmi_magic2[8] = {'R', 'M', 'I', 'D', 'd', 'a', 't', 'a'}; - if (length >= 16 && memcmp(data, rmi_magic1, 4) == 0 && memcmp(data + 8, rmi_magic2, 8) == 0) - return fmidi_fileformat_smf; - - const uint8_t xmi_magic[20] = { - 'F', 'O', 'R', 'M', 0, 0, 0, 14, - 'X', 'D', 'I', 'R', 'I', 'N', 'F', 'O', 0, 0, 0, 2 - }; - if (length >= 20 && memcmp(data, xmi_magic, 20) == 0) - return fmidi_fileformat_xmi; - - RET_FAIL((fmidi_fileformat_t)-1, fmidi_err_format); -} - -fmidi_fileformat_t fmidi_stream_identify(FILE *stream) -{ - rewind(stream); - - uint8_t magic[32]; - size_t size = fread(magic, 1, sizeof(magic), stream); - if (ferror(stream)) - RET_FAIL((fmidi_fileformat_t)-1, fmidi_err_input); - - return fmidi_mem_identify(magic, size); -} - -fmidi_smf_t *fmidi_auto_mem_read(const uint8_t *data, size_t length) -{ - switch (fmidi_mem_identify(data, length)) { - case fmidi_fileformat_smf: - return fmidi_smf_mem_read(data, length); - case fmidi_fileformat_xmi: - return fmidi_xmi_mem_read(data, length); - default: - return nullptr; - } -} - -fmidi_smf_t *fmidi_auto_file_read(const char *filename) -{ - unique_FILE fh(fmidi_fopen(filename, "rb")); - if (!fh) - RET_FAIL(nullptr, fmidi_err_input); - - fmidi_smf_t *smf = fmidi_auto_stream_read(fh.get()); - return smf; -} - -fmidi_smf_t *fmidi_auto_stream_read(FILE *stream) -{ - switch (fmidi_stream_identify(stream)) { - case fmidi_fileformat_smf: - return fmidi_smf_stream_read(stream); - case fmidi_fileformat_xmi: - return fmidi_xmi_stream_read(stream); - default: - return nullptr; - } -} - -#include "fmidi/fmidi.h" -#include -#include - -static void write_vlq(uint32_t value, Writer &writer) -{ - unsigned shift = 28; - unsigned mask = (1u << 7) - 1; - while (shift > 0 && ((value >> shift) & mask) == 0) - shift -= 7; - while (shift > 0) { - writer.put(((value >> shift) & mask) | (1u << 7)); - shift -= 7; - } - writer.put(value & mask); -} - -static bool fmidi_smf_write(const fmidi_smf_t *smf, Writer &writer) -{ - writer.write("MThd", 4); - - const uint32_t header_size = 6; - writer.writeBE(&header_size, 4); - - const fmidi_smf_info_t *info = fmidi_smf_get_info(smf); - const uint16_t track_count = info->track_count; - writer.writeBE(&info->format, 2); - writer.writeBE(&track_count, 2); - writer.writeBE(&info->delta_unit, 2); - - for (unsigned i = 0; i < track_count; ++i) { - writer.write("MTrk", 4); - - off_t off_track_length = writer.tell(); - - uint32_t track_length = 0; - writer.writeBE(&track_length, 4); - - int running_status = -1; - - fmidi_track_iter_t iter; - fmidi_smf_track_begin(&iter, i); - - const fmidi_event_t *event; - while ((event = fmidi_smf_track_next(smf, &iter))) { - switch (event->type) { - case fmidi_event_meta: - write_vlq(event->delta, writer); - writer.put(0xff); - writer.put(event->data[0]); - write_vlq(event->datalen - 1, writer); - writer.write(event->data + 1, event->datalen - 1); - running_status = -1; - break; - case fmidi_event_message: - { - write_vlq(event->delta, writer); - uint8_t status = event->data[0]; - if (status == 0xf0) { - writer.put(0xf0); - write_vlq(event->datalen - 1, writer); - writer.write(event->data + 1, event->datalen - 1); - running_status = -1; - } - else if ((int)status == running_status) - writer.write(event->data + 1, event->datalen - 1); - else { - writer.write(event->data, event->datalen); - running_status = status; - } - break; - } - case fmidi_event_escape: - write_vlq(event->delta, writer); - writer.put(0xf7); - write_vlq(event->datalen, writer); - writer.write(event->data, event->datalen); - running_status = -1; - break; - case fmidi_event_xmi_timbre: - case fmidi_event_xmi_branch_point: - break; - } - } - - off_t off_track_end = writer.tell(); - - track_length = - std::make_unsigned::type(off_track_end) - - std::make_unsigned::type(off_track_length) - 4; - - writer.seek(off_track_length, SEEK_SET); - writer.writeBE(&track_length, 4); - writer.seek(off_track_end, SEEK_SET); - } - - return true; -} - -bool fmidi_smf_mem_write(const fmidi_smf_t *smf, uint8_t **data, size_t *length) -{ - std::vector mem; - mem.reserve(8192); - - Memory_Writer writer(mem); - if (!fmidi_smf_write(smf, writer)) - return false; - - assert(data); - assert(length); - - if (!(*data = (uint8_t *)malloc(mem.size()))) - throw std::bad_alloc(); - - memcpy(*data, mem.data(), mem.size()); - *length = mem.size(); - - return true; -} - -bool fmidi_smf_file_write(const fmidi_smf_t *smf, const char *filename) -{ - unique_FILE fh(fmidi_fopen(filename, "wb")); - if (!fh) - RET_FAIL(false, fmidi_err_output); - - return fmidi_smf_stream_write(smf, fh.get()); -} - -bool fmidi_smf_stream_write(const fmidi_smf_t *smf, FILE *stream) -{ - Stream_Writer writer(stream); - if (!fmidi_smf_write(smf, writer)) - return false; - - if (fflush(stream) != 0) - RET_FAIL(false, fmidi_err_output); - - return true; -} From c75e90b034ff1a72d62fc9bb1d198b9972f0aa2a Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:39:17 +0100 Subject: [PATCH 6/8] Checkout abseil 2021_11_02 --- external/abseil-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/abseil-cpp b/external/abseil-cpp index 997aaf3a..21510581 160000 --- a/external/abseil-cpp +++ b/external/abseil-cpp @@ -1 +1 @@ -Subproject commit 997aaf3a28308eba1b9156aa35ab7bca9688e9f6 +Subproject commit 215105818dfde3174fe799600bb0f3cae233d0bf From 8b258a337790dba8bcbc44bb3483e5bde58c2cb4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:41:06 +0100 Subject: [PATCH 7/8] Update stb_image to 2.27 --- plugins/editor/external/stb_image/stb_image.h | 283 +++++++++++++----- 1 file changed, 208 insertions(+), 75 deletions(-) diff --git a/plugins/editor/external/stb_image/stb_image.h b/plugins/editor/external/stb_image/stb_image.h index 9489bf00..d60371b9 100644 --- a/plugins/editor/external/stb_image/stb_image.h +++ b/plugins/editor/external/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,7 @@ LICENSE RECENT REVISION HISTORY: + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically @@ -89,7 +90,7 @@ RECENT REVISION HISTORY: Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine + Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera @@ -102,7 +103,7 @@ RECENT REVISION HISTORY: Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw @@ -110,11 +111,13 @@ RECENT REVISION HISTORY: Josh Tobin Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] + Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE + Jacko Dirks + To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. @@ -176,6 +179,32 @@ RECENT REVISION HISTORY: // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// // =========================================================================== // // UNICODE: @@ -281,11 +310,10 @@ RECENT REVISION HISTORY: // // iPhone PNG support: // -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -489,6 +517,8 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes @@ -634,7 +664,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) @@ -748,9 +778,12 @@ static int stbi__sse2_available(void) #ifdef STBI_NEON #include -// assume GCC or Clang on ARM targets +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif +#endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name @@ -924,6 +957,7 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); #endif static @@ -998,7 +1032,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && @@ -1021,7 +1055,7 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; @@ -1087,9 +1121,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif @@ -1107,6 +1140,13 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif @@ -1262,12 +1302,12 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); @@ -1277,16 +1317,16 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; -#if _MSC_VER >= 1400 +#if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else @@ -1662,7 +1702,8 @@ static int stbi__get16le(stbi__context *s) static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; } #endif @@ -2090,13 +2131,12 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) int sgn; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits @@ -2146,7 +2186,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); @@ -2203,12 +2243,12 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); + data[0] = (short) (dc * (1 << j->succ_low)); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) @@ -2245,7 +2285,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); + data[zig] = (short) ((r >> 8) * (1 << shift)); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); @@ -2263,7 +2303,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); } } } while (k <= j->spec_end); @@ -3227,6 +3267,13 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -3782,6 +3829,10 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp else decode_n = z->s->img_n; + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // resample and color-convert { int k; @@ -3924,6 +3975,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); @@ -3936,6 +3988,7 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -3960,6 +4013,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); @@ -3979,6 +4033,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -3988,8 +4043,8 @@ typedef struct stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) @@ -4120,7 +4175,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; @@ -4317,7 +4372,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) return 1; } -static const stbi_uc stbi__zdefault_length[288] = +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, @@ -4363,7 +4418,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; @@ -4759,6 +4814,7 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; @@ -4879,19 +4935,46 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int return 1; } -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { - stbi__de_iphone_flag = flag_true_if_should_convert; + stbi__de_iphone_flag_global = flag_true_if_should_convert; } +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; @@ -5272,6 +5355,32 @@ typedef struct int extra_read; } stbi__bmp_data; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; @@ -5299,6 +5408,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres @@ -5313,17 +5424,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } + stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); @@ -5338,6 +5439,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) return stbi__errpuc("bad BMP", "bad BMP"); } } else { + // V4/V5 header int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); @@ -5345,6 +5447,8 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters @@ -5394,8 +5498,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { return stbi__errpuc("bad offset", "Corrupt BMP"); } } @@ -6342,6 +6445,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { @@ -6457,6 +6561,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); @@ -6766,6 +6871,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { @@ -6777,13 +6893,15 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int stride; int out_size = 0; int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; } - (void)out_size; (void)delays_size; // avoid warnings - do { u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6796,26 +6914,29 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); delays_size = layers * sizeof(int); } } @@ -7140,9 +7261,10 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) + if (p == NULL) { + stbi__rewind( s ); return 0; + } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { @@ -7208,8 +7330,8 @@ static int stbi__psd_is16(stbi__context *s) stbi__rewind( s ); return 0; } - (void) stbi__get32be(s); - (void) stbi__get32be(s); + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); @@ -7288,7 +7410,6 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel #ifndef STBI_NO_PNM @@ -7309,7 +7430,8 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *out; STBI_NOTUSED(ri); - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); @@ -7319,12 +7441,12 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); if (req_comp && req_comp != s->img_n) { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); @@ -7400,11 +7522,19 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; else - return 1; + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif @@ -7460,6 +7590,9 @@ static int stbi__is_16_main(stbi__context *s) if (stbi__psd_is16(s)) return 1; #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif return 0; } From 2c3432ca3026b965cf1dbe63803619069660d80c Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Wed, 10 Nov 2021 09:58:29 +0100 Subject: [PATCH 8/8] Fix Makefile build with abseil changes --- common.mk | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/common.mk b/common.mk index 3526e33c..fd81147d 100644 --- a/common.mk +++ b/common.mk @@ -215,9 +215,6 @@ SFIZZ_SOURCES += \ external/abseil-cpp/absl/base/internal/sysinfo.cc \ external/abseil-cpp/absl/base/internal/thread_identity.cc \ external/abseil-cpp/absl/base/internal/unscaledcycleclock.cc -# absl::exponential_biased -SFIZZ_SOURCES += \ - external/abseil-cpp/absl/base/internal/exponential_biased.cc # absl::malloc_internal SFIZZ_SOURCES += \ external/abseil-cpp/absl/base/internal/low_level_alloc.cc @@ -302,9 +299,9 @@ SFIZZ_SOURCES += \ # absl::city SFIZZ_SOURCES += \ external/abseil-cpp/absl/hash/internal/city.cc -# absl::wyhash +# absl::low_level_hash SFIZZ_SOURCES += \ - external/abseil-cpp/absl/hash/internal/wyhash.cc + external/abseil-cpp/absl/hash/internal/low_level_hash.cc # absl::int128 SFIZZ_SOURCES += \ external/abseil-cpp/absl/numeric/int128.cc