ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

commit 13b187b846a221708d0d18e4475e230f0b639e2e
parent 4f7f08a867b7e448ce9580279134f4cf7ff82d03
Author: Olav Sørensen <olav.sorensen@live.no>
Date:   Sun, 23 May 2021 20:35:30 +0200

Pushed v1.47 code (mega-commit, unfortunately)

Diffstat:
MCMakeLists.txt | 10++++++++--
MLICENSES.txt | 3+++
Amake-linux-nomidi-noflac.sh | 10++++++++++
Dmake-linux-nomidi.sh | 10----------
Mmake-linux.sh | 2+-
Mmake-macos.sh | 2+-
Mrelease/LICENSES.txt | 35+++++++++++++++++++++++++++++++++++
Msrc/ft2_about.c | 48++++++++++++++++++++++++++----------------------
Msrc/ft2_audio.c | 414+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ft2_audio.h | 50++++++++++++++++++++++++++++++--------------------
Msrc/ft2_audioselector.c | 60++++++++++++++++++++----------------------------------------
Msrc/ft2_config.c | 58+++++++++++++++++++++++++++++++++++++---------------------
Msrc/ft2_config.h | 22+++++++++++++---------
Asrc/ft2_cpu.h | 32++++++++++++++++++++++++++++++++
Msrc/ft2_diskop.c | 18+++++++++---------
Msrc/ft2_diskop.h | 1+
Msrc/ft2_edit.c | 893+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/ft2_edit.h | 2+-
Msrc/ft2_events.c | 6+++---
Msrc/ft2_gui.c | 12++++++------
Msrc/ft2_header.h | 59++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/ft2_help.c | 74+++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/ft2_help.h | 2+-
Msrc/ft2_inst_ed.c | 1375++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/ft2_inst_ed.h | 13++++++-------
Msrc/ft2_keyboard.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/ft2_main.c | 35+++++++++++++++++++----------------
Msrc/ft2_midi.c | 35+++++++++++++++++------------------
Msrc/ft2_midi.h | 2+-
Msrc/ft2_module_loader.c | 150+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/ft2_module_loader.h | 16++++++++--------
Msrc/ft2_module_saver.c | 603+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ft2_mouse.c | 22+++++++++++-----------
Msrc/ft2_nibbles.c | 121+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/ft2_nibbles.h | 2+-
Msrc/ft2_palette.c | 135++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/ft2_palette.h | 4++--
Msrc/ft2_pattern_draw.c | 125++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/ft2_pattern_draw.h | 2+-
Msrc/ft2_pattern_ed.c | 708++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/ft2_pattern_ed.h | 20++++++++++----------
Msrc/ft2_pushbuttons.c | 14+++++++-------
Msrc/ft2_radiobuttons.c | 10++++++----
Msrc/ft2_radiobuttons.h | 5++++-
Msrc/ft2_replayer.c | 1387+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/ft2_replayer.h | 281+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ft2_sample_ed.c | 2053+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/ft2_sample_ed.h | 35++++++++++++++++++++++-------------
Msrc/ft2_sample_ed_features.c | 644++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/ft2_sample_loader.c | 45++++++++++++++++++++++++++++++---------------
Msrc/ft2_sample_loader.h | 2+-
Msrc/ft2_sample_saver.c | 151+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/ft2_sampling.c | 473++++++++++++++++++++++++++++++++++++++-----------------------------------------
Dsrc/ft2_scopedraw.c | 402-------------------------------------------------------------------------------
Dsrc/ft2_scopes.c | 641-------------------------------------------------------------------------------
Dsrc/ft2_scopes.h | 48------------------------------------------------
Msrc/ft2_structs.h | 10+++++-----
Msrc/ft2_sysreqs.c | 62+++++++++++++++++++++++++++++++-------------------------------
Msrc/ft2_sysreqs.h | 8++++----
Msrc/ft2_tables.c | 222+++++++++++++++++--------------------------------------------------------------
Msrc/ft2_tables.h | 8++++----
Msrc/ft2_textboxes.c | 4++--
Msrc/ft2_trim.c | 299++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/ft2_video.c | 51++++++++++++++++++++++++++++++---------------------
Msrc/ft2_wav_renderer.c | 83+++++++++++++++++++++++++++++++++++--------------------------------------------
Msrc/ft2_wav_renderer.h | 7++++++-
Msrc/helpdata/FT2.HLP | 49++++++++++++++++++++++++++++---------------------
Msrc/helpdata/ft2_help_data.h | 4131++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asrc/libflac/COPYING.txt | 29+++++++++++++++++++++++++++++
Asrc/libflac/FLAC/callback.h | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/FLAC/export.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/FLAC/format.h | 1025+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/FLAC/metadata.h | 2182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/FLAC/ordinals.h | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/FLAC/stream_decoder.h | 1559+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/NOTE.txt | 4++++
Asrc/libflac/bitmath.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/bitreader.c | 914+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/crc.c | 410+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/fixed.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/format.c | 566+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/lpc.c | 1233+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/md5.c | 479+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/memory.c | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/metadata_iterators.c | 3147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/metadata_object.c | 1588+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/bitmath.h | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/bitreader.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/crc.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/fixed.h | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/float.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/format.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/lpc.h | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/macros.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/md5.h | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/memory.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/metadata.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/private/window.h | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/protected/stream_decoder.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/alloc.h | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/compat.h | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/endswap.h | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/macros.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/safe_str.h | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/utf8.h | 25+++++++++++++++++++++++++
Asrc/libflac/share/win_utf8_io.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/share/windows_unicode_filenames.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/stream_decoder.c | 2951+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/window.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/libflac/windows_unicode_filenames.c | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mixer/ft2_center_mix.c | 253++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/mixer/ft2_mix.c | 265++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/mixer/ft2_mix.h | 11++++++++++-
Msrc/mixer/ft2_mix_macros.h | 336+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/mixer/ft2_silence_mix.c | 39++++++++++++++++++++-------------------
Msrc/mixer/ft2_windowed_sinc.c | 55+++++++++++++++++++++++++++----------------------------
Msrc/mixer/ft2_windowed_sinc.h | 18++++++++----------
Msrc/modloaders/ft2_load_digi.c | 160+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/modloaders/ft2_load_mod.c | 235+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/modloaders/ft2_load_s3m.c | 534+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/modloaders/ft2_load_stk.c | 224++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/modloaders/ft2_load_stm.c | 195++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/modloaders/ft2_load_xm.c | 385++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/rtmidi/rtmidi_c.cpp | 7++++++-
Asrc/scopes/ft2_scope_macros.h | 256+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scopes/ft2_scopedraw.c | 244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/ft2_scopedraw.h -> src/scopes/ft2_scopedraw.h | 0
Asrc/scopes/ft2_scopes.c | 587+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scopes/ft2_scopes.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/smploaders/ft2_load_aiff.c | 312++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Asrc/smploaders/ft2_load_flac.c | 431+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/smploaders/ft2_load_iff.c | 96+++++++++++++++++++++++++++++++++----------------------------------------------
Msrc/smploaders/ft2_load_raw.c | 17+++++++++++------
Msrc/smploaders/ft2_load_wav.c | 158++++++++++++++++++++++++++++++++++++-------------------------------------------
Avs2019_project/ft2-clone/16.wav | 0
Mvs2019_project/ft2-clone/ft2-clone.vcxproj | 43++++++++++++++++++++++++++++---------------
Mvs2019_project/ft2-clone/ft2-clone.vcxproj.filters | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
137 files changed, 30160 insertions(+), 10434 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -10,8 +10,10 @@ file(GLOB ft2-clone_SRC "${ft2-clone_SOURCE_DIR}/src/*.c" "${ft2-clone_SOURCE_DIR}/src/gfxdata/*.c" "${ft2-clone_SOURCE_DIR}/src/mixer/*.c" + "${ft2-clone_SOURCE_DIR}/src/scopes/*.c" "${ft2-clone_SOURCE_DIR}/src/modloaders/*.c" "${ft2-clone_SOURCE_DIR}/src/smploaders/*.c" + "${ft2-clone_SOURCE_DIR}/src/libflac/*.c" ) add_executable(ft2-clone ${ft2-clone_SRC}) @@ -26,7 +28,11 @@ endif() target_link_libraries(ft2-clone PRIVATE m asound pthread ${SDL2_LIBRARIES}) -target_compile_definitions(ft2-clone PRIVATE __LINUX_ALSA__) + +target_compile_definitions(ft2-clone + PRIVATE HAS_MIDI + PRIVATE __LINUX_ALSA__ + PRIVATE HAS_LIBFLAC) install(TARGETS ft2-clone - RUNTIME DESTINATION bin ) + RUNTIME DESTINATION bin) diff --git a/LICENSES.txt b/LICENSES.txt @@ -3,6 +3,9 @@ This project uses the following licenses: Source code (BSD 3-clause): src\LICENSE.txt +libFLAC: +src\libflac\COPYING.txt + rtmidi: src\rtmidi\LICENSE.txt diff --git a/make-linux-nomidi-noflac.sh b/make-linux-nomidi-noflac.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +rm release/other/ft2-clone &> /dev/null +echo Compiling \(with no MIDI and no FLAC functionality\), please wait patiently... + +gcc -DNDEBUG src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone + +rm src/gfxdata/*.o src/*.o &> /dev/null + +echo Done. The executable can be found in \'release/other\' if everything went well. diff --git a/make-linux-nomidi.sh b/make-linux-nomidi.sh @@ -1,10 +0,0 @@ -#!/bin/bash - -rm release/other/ft2-clone &> /dev/null -echo Compiling \(with no MIDI functionality\), please wait patiently... - -gcc -DNDEBUG src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone - -rm src/gfxdata/*.o src/*.o &> /dev/null - -echo Done. The executable can be found in \'release/other\' if everything went well. diff --git a/make-linux.sh b/make-linux.sh @@ -3,7 +3,7 @@ rm release/other/ft2-clone &> /dev/null echo Compiling, please wait patiently... -gcc -DNDEBUG -DHAS_MIDI -D__LINUX_ALSA__ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone +gcc -DNDEBUG -DHAS_MIDI -D__LINUX_ALSA__ -DHAS_LIBFLAC src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/libflac/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone rm src/rtmidi/*.o src/gfxdata/*.o src/*.o &> /dev/null diff --git a/make-macos.sh b/make-macos.sh @@ -36,7 +36,7 @@ fi # function compile() { rm $1 &> /dev/null - clang $VERBOSE $CFLAGS -F /Library/Frameworks -g0 -DNDEBUG -DHAS_MIDI -D__MACOSX_CORE__ -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/modloaders/*.c src/smploaders/*.c src/*.c -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default $LDFLAGS -L /Library/Frameworks -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -liconv -lpthread -lm -lstdc++ -o $1 + clang $VERBOSE $CFLAGS -F /Library/Frameworks -g0 -DNDEBUG -DHAS_MIDI -D__MACOSX_CORE__ -DHAS_LIBFLAC -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/mixer/*.c src/scopes/*.c src/modloaders/*.c src/smploaders/*.c src/libflac/*.c src/*.c -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default $LDFLAGS -L /Library/Frameworks -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -liconv -lpthread -lm -lstdc++ -o $1 return $? } diff --git a/release/LICENSES.txt b/release/LICENSES.txt @@ -29,6 +29,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- +----------------------------------- libFLAC ------------------------------------ +-------------------------------------------------------------------------------- + +Copyright (C) 2000-2009 Josh Coalson +Copyright (C) 2011-2016 Xiph.Org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- ------------------------------------ rtmidi ------------------------------------ -------------------------------------------------------------------------------- diff --git a/src/ft2_about.c b/src/ft2_about.c @@ -7,7 +7,7 @@ #include "ft2_video.h" #include "ft2_structs.h" -#define NUM_STARS 1750 +#define NUM_STARS 2048 #define ABOUT_SCREEN_W 626 #define ABOUT_SCREEN_H 167 #define ABOUT_LOGO_W 449 @@ -26,10 +26,12 @@ typedef struct } matrix_t; static char *customText1 = "Clone by Olav \"8bitbubsy\" S\025rensen"; -static char customText2[64]; +static char *customText2 = "www.16-bits.org"; +static char customText3[64]; -static int16_t customText1X, customText2X, customTextY; -static int32_t lastStarScreenPos[NUM_STARS], starfieldFade, logoFade; +static int16_t customText1Y, customText2Y, customText3Y; +static int16_t customText1X, customText2X, customText3X; +static int32_t lastStarScreenPos[NUM_STARS], fadeValue; static uint32_t randSeed, frameCounter; static float f2pi; static vector_t starPoints[NUM_STARS], rotation; @@ -139,9 +141,8 @@ static void starfield(void) d = 255; d ^= 255; - d = (d * starfieldFade) >> 8; - int32_t r = d - 66; + int32_t r = d - 68; if (r < 0) r = 0; @@ -164,20 +165,20 @@ void aboutFrame(void) rotateMatrix(); starfield(); - rotation.x += 0.00009f; - rotation.y += 0.00007f; - rotation.z -= 0.00005f; + rotation.x -= 0.00011f; + rotation.z += 0.00006f; - // fade in starfield - if (starfieldFade < 256) - starfieldFade += 4; - - // fade in logo after 1 second - if (logoFade < 256 && frameCounter++ >= VBLANK_HZ*1) + // fade in stuff after 1/3th of a second + if (fadeValue < 256 && ++frameCounter >= (int32_t)((VBLANK_HZ/3.0)+0.5)) { - blit32Fade(91, 31, bmp.ft2AboutLogo, ABOUT_LOGO_W, ABOUT_LOGO_H, logoFade); - textOutFade(customText1X, customTextY, PAL_FORGRND, customText1, logoFade); - logoFade += 4; + blit32Fade(91, 31, bmp.ft2AboutLogo, ABOUT_LOGO_W, ABOUT_LOGO_H, fadeValue); + textOutFade(customText1X, customText1Y, PAL_FORGRND, customText1, fadeValue); + textOutFade(customText2X, customText2Y, PAL_FORGRND, customText2, fadeValue); + textOutFade(customText3X, customText3Y, PAL_FORGRND, customText3, fadeValue); + + fadeValue += 3; + if (fadeValue > 256) + fadeValue = 256; } } @@ -193,14 +194,17 @@ void showAboutScreen(void) // called once when About screen is opened showPushButton(PB_EXIT_ABOUT); - sprintf(customText2, "v%s (%s)", PROG_VER_STR, __DATE__); + sprintf(customText3, "v%s (%s)", PROG_VER_STR, __DATE__); customText1X = (SCREEN_W - textWidth(customText1)) >> 1; customText2X = (SCREEN_W-8) - textWidth(customText2); - customTextY = 157; - textOut(customText2X, customTextY, PAL_FORGRND, customText2); + customText3X = (SCREEN_W-8) - textWidth(customText3); + customText1Y = 157; + customText2Y = 157-12; + customText3Y = 157; aboutInit(); - frameCounter = starfieldFade = logoFade = 0; + frameCounter = 0; + fadeValue = 0; ui.aboutScreenShown = true; } diff --git a/src/ft2_audio.c b/src/ft2_audio.c @@ -7,7 +7,7 @@ #include <stdint.h> #include "ft2_header.h" #include "ft2_config.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_video.h" #include "ft2_gui.h" #include "ft2_midi.h" @@ -29,10 +29,10 @@ static int8_t pmpCountDiv, pmpChannels = 2; static uint16_t smpBuffSize; -static int32_t randSeed = INITIAL_DITHER_SEED; -static uint32_t oldAudioFreq, tickTimeLen, tickTimeLenFrac; -static double dAudioNormalizeMul, dPrngStateL, dPrngStateR, dPanningTab[256+1]; -static voice_t voice[MAX_VOICES * 2]; +static uint32_t oldAudioFreq, tickTimeLen, tickTimeLenFrac, randSeed = INITIAL_DITHER_SEED; +static float fAudioNormalizeMul, fPanningTab[256+1]; +static double dAudioNormalizeMul, dPrngStateL, dPrngStateR; +static voice_t voice[MAX_CHANNELS * 2]; static void (*sendAudSamplesFunc)(uint8_t *, uint32_t, uint8_t); // "send mixed samples" routines // globalized @@ -45,16 +45,13 @@ volatile bool pattQueueClearing, chQueueClearing; void resetCachedMixerVars(void) { - stmTyp *ch = stm; - for (int32_t i = 0; i < MAX_VOICES; i++, ch++) + channel_t *ch = channel; + for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) ch->oldFinalPeriod = -1; voice_t *v = voice; - for (int32_t i = 0; i < MAX_VOICES*2; i++, v++) - { - v->dOldHz = 0.0; + for (int32_t i = 0; i < MAX_CHANNELS*2; i++, v++) v->oldDelta = 0; - } } void stopVoice(int32_t i) @@ -63,13 +60,13 @@ void stopVoice(int32_t i) v = &voice[i]; memset(v, 0, sizeof (voice_t)); - v->pan = 128; + v->panning = 128; // clear "fade out" voice too - v = &voice[MAX_VOICES + i]; + v = &voice[MAX_CHANNELS + i]; memset(v, 0, sizeof (voice_t)); - v->pan = 128; + v->panning = 128; } bool setNewAudioSettings(void) // only call this from the main input/video thread @@ -125,6 +122,35 @@ void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag) dAmp *= 32768.0; dAudioNormalizeMul = dAmp; + fAudioNormalizeMul = (float)dAmp; +} + +void decreaseMasterVol(void) +{ + if (config.masterVol >= 16) + config.masterVol -= 16; + else + config.masterVol = 0; + + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); + + // if Config -> I/O Devices is open, update master volume scrollbar + if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES) + drawScrollBar(SB_MASTERVOL_SCROLL); +} + +void increaseMasterVol(void) +{ + if (config.masterVol < (256-16)) + config.masterVol += 16; + else + config.masterVol = 256; + + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); + + // if Config -> I/O Devices is open, update master volume scrollbar + if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES) + drawScrollBar(SB_MASTERVOL_SCROLL); } void setNewAudioFreq(uint32_t freq) // for song-to-WAV rendering @@ -150,25 +176,22 @@ void setBackOldAudioFreq(void) // for song-to-WAV rendering calcReplayerVars(audio.freq); } -void P_SetSpeed(uint16_t bpm) +void setMixerBPM(int32_t bpm) { - if (bpm == 0) + if (bpm < MIN_BPM || bpm > MAX_BPM) return; - // non-FT2 check for security - if (bpm > MAX_BPM) - return; + int32_t i = bpm - MIN_BPM; - audio.dSamplesPerTick = audio.dSamplesPerTickTab[bpm]; - audio.samplesPerTick = (int32_t)(audio.dSamplesPerTick + 0.5); + audio.samplesPerTick64 = audio.samplesPerTick64Tab[i]; // fixed-point + audio.samplesPerTick = (audio.samplesPerTick64 + (1LL << 31)) >> 32; // rounded - // get tick time length for audio/video sync timestamp - const uint64_t tickTimeLen64 = audio.tickTimeTab[bpm]; - tickTimeLen = tickTimeLen64 >> 32; - tickTimeLenFrac = tickTimeLen64 & UINT32_MAX; + // for audio/video sync timestamp + tickTimeLen = audio.tickTimeTab[i]; + tickTimeLenFrac = audio.tickTimeFracTab[i]; - // used for calculating volume ramp length for "ticks" ramps - audio.dRampTickMul = audio.dRampTickMulTab[bpm]; + // for calculating volume ramp length for tick-length ramps + audio.fRampTickMul = audio.fRampTickMulTab[i]; } void audioSetVolRamp(bool volRamp) @@ -189,55 +212,53 @@ void calcPanningTable(void) { // same formula as FT2's panning table (with 0.0f..1.0f range) for (int32_t i = 0; i <= 256; i++) - dPanningTab[i] = sqrt(i * (1.0 / 256.0)); + fPanningTab[i] = sqrtf(i / 256.0f); } static void voiceUpdateVolumes(int32_t i, uint8_t status) { - double dDestVolL, dDestVolR; - voice_t *v = &voice[i]; - const double dVolL = v->dVol * dPanningTab[256-v->pan]; - const double dVolR = v->dVol * dPanningTab[ v->pan]; + const float fVolumeL = v->fVolume * fPanningTab[256-v->panning]; + const float fVolumeR = v->fVolume * fPanningTab[ v->panning]; if (!audio.volumeRampingFlag) { // volume ramping is disabled - v->dVolL = dVolL; - v->dVolR = dVolR; - v->volRampSamples = 0; + v->fVolumeL = fVolumeL; + v->fVolumeR = fVolumeR; + v->volumeRampLength = 0; return; } - v->dDestVolL = dVolL; - v->dDestVolR = dVolR; + v->fVolumeLTarget = fVolumeL; + v->fVolumeRTarget = fVolumeR; - if (status & IS_NyTon) + if (status & IS_Trigger) { // sample is about to start, ramp out/in at the same time // setup "fade out" voice (only if current voice volume > 0) - if (v->dVolL > 0.0 || v->dVolR > 0.0) + if (v->fVolumeL > 0.0f || v->fVolumeR > 0.0f) { - voice_t *f = &voice[MAX_VOICES+i]; + voice_t *f = &voice[MAX_CHANNELS+i]; *f = *v; // copy voice - f->volRampSamples = audio.quickVolRampSamples; + f->volumeRampLength = audio.quickVolRampSamples; - dDestVolL = -f->dVolL; - dDestVolR = -f->dVolR; + const float fVolumeLTarget = -f->fVolumeL; + const float fVolumeRTarget = -f->fVolumeR; - f->dVolDeltaL = dDestVolL * audio.dRampQuickVolMul; - f->dVolDeltaR = dDestVolR * audio.dRampQuickVolMul; + f->fVolumeLDelta = fVolumeLTarget * audio.fRampQuickVolMul; + f->fVolumeRDelta = fVolumeRTarget * audio.fRampQuickVolMul; f->isFadeOutVoice = true; } // make current voice fade in from zero when it starts - v->dVolL = 0.0; - v->dVolR = 0.0; + v->fVolumeL = 0.0f; + v->fVolumeR = 0.0f; } // ramp volume changes @@ -248,56 +269,44 @@ static void voiceUpdateVolumes(int32_t i, uint8_t status) */ // if destination volume and current volume is the same (and we have no sample trigger), don't do ramp - if (dVolL == v->dVolL && dVolR == v->dVolR && !(status & IS_NyTon)) + if (fVolumeL == v->fVolumeL && fVolumeR == v->fVolumeR && !(status & IS_Trigger)) { // there is no volume change - v->volRampSamples = 0; + v->volumeRampLength = 0; } else { - dDestVolL = dVolL - v->dVolL; - dDestVolR = dVolR - v->dVolR; + const float fVolumeLTarget = fVolumeL - v->fVolumeL; + const float fVolumeRTarget = fVolumeR - v->fVolumeR; if (status & IS_QuickVol) { - v->volRampSamples = audio.quickVolRampSamples; - v->dVolDeltaL = dDestVolL * audio.dRampQuickVolMul; - v->dVolDeltaR = dDestVolR * audio.dRampQuickVolMul; + v->fVolumeLDelta = fVolumeLTarget * audio.fRampQuickVolMul; + v->fVolumeRDelta = fVolumeRTarget * audio.fRampQuickVolMul; + v->volumeRampLength = audio.quickVolRampSamples; + } else { - v->volRampSamples = audio.samplesPerTick; - v->dVolDeltaL = dDestVolL * audio.dRampTickMul; - v->dVolDeltaR = dDestVolR * audio.dRampTickMul; + v->fVolumeLDelta = fVolumeLTarget * audio.fRampTickMul; + v->fVolumeRDelta = fVolumeRTarget * audio.fRampTickMul; + v->volumeRampLength = audio.samplesPerTick; } } } -static void voiceTrigger(int32_t ch, sampleTyp *s, int32_t position) +static void voiceTrigger(int32_t ch, sample_t *s, int32_t position) { voice_t *v = &voice[ch]; - int32_t length = s->len; - int32_t loopStart = s->repS; - int32_t loopLength = s->repL; - int32_t loopEnd = s->repS + s->repL; - uint8_t loopType = s->typ & 3; - const bool sampleIs16Bit = (s->typ >> 4) & 1; - - if (sampleIs16Bit) - { - assert(!(length & 1)); - assert(!(loopStart & 1)); - assert(!(loopLength & 1)); - assert(!(loopEnd & 1)); - - length >>= 1; - loopStart >>= 1; - loopLength >>= 1; - loopEnd >>= 1; - } + int32_t length = s->length; + int32_t loopStart = s->loopStart; + int32_t loopLength = s->loopLength; + int32_t loopEnd = s->loopStart + s->loopLength; + uint8_t loopType = GET_LOOPTYPE(s->flags); + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - if (s->pek == NULL || length < 1) + if (s->dataPtr == NULL || length < 1) { v->active = false; // shut down voice (illegal parameters) return; @@ -306,56 +315,56 @@ static void voiceTrigger(int32_t ch, sampleTyp *s, int32_t position) if (loopLength < 1) // disable loop if loopLength is below 1 loopType = 0; - if (sampleIs16Bit) + if (sample16Bit) { - v->base16 = (const int16_t *)s->pek; + v->base16 = (const int16_t *)s->dataPtr; v->revBase16 = &v->base16[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps16 = s->leftEdgeTapSamples16 + SINC_LEFT_TAPS; } else { - v->base8 = s->pek; + v->base8 = s->dataPtr; v->revBase8 = &v->base8[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps8 = s->leftEdgeTapSamples8 + SINC_LEFT_TAPS; } v->hasLooped = false; // for sinc interpolation special case - v->backwards = false; + v->samplingBackwards = false; v->loopType = loopType; - v->end = (loopType > 0) ? loopEnd : length; + v->sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd; v->loopStart = loopStart; v->loopLength = loopLength; - v->pos = position; - v->posFrac = 0; + v->position = position; + v->positionFrac = 0; // if position overflows, shut down voice (f.ex. through 9xx command) - if (v->pos >= v->end) + if (v->position >= v->sampleEnd) { v->active = false; return; } - v->mixFuncOffset = (sampleIs16Bit * 9) + (audio.interpolationType * 3) + loopType; + v->mixFuncOffset = (sample16Bit * 9) + (audio.interpolationType * 3) + loopType; v->active = true; } void resetRampVolumes(void) { voice_t *v = voice; - for (int32_t i = 0; i < song.antChn; i++, v++) + for (int32_t i = 0; i < song.numChannels; i++, v++) { - v->dVolL = v->dDestVolL; - v->dVolR = v->dDestVolR; - v->volRampSamples = 0; + v->fVolumeL = v->fVolumeLTarget; + v->fVolumeR = v->fVolumeRTarget; + v->volumeRampLength = 0; } } void updateVoices(void) { - stmTyp *ch = stm; + channel_t *ch = channel; voice_t *v = voice; - for (int32_t i = 0; i < song.antChn; i++, ch++, v++) + for (int32_t i = 0; i < song.numChannels; i++, ch++, v++) { const uint8_t status = ch->tmpStatus = ch->status; // (tmpStatus is used for audio/video sync queue) if (status == 0) @@ -364,10 +373,15 @@ void updateVoices(void) ch->status = 0; if (status & IS_Vol) - v->dVol = ch->dFinalVol; + { + v->fVolume = ch->fFinalVol; + + const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->fFinalVol) + 0.5f); // rounded + v->scopeVolume = (uint8_t)scopeVolume; + } if (status & IS_Pan) - v->pan = ch->finalPan; + v->panning = ch->finalPan; if (status & (IS_Vol + IS_Pan)) voiceUpdateVolumes(i, status); @@ -381,33 +395,33 @@ void updateVoices(void) if (ch->finalPeriod == 0) // in FT2, period 0 -> delta 0 { - v->dOldHz = 0.0; + v->scopeDelta = 0; v->oldDelta = 0; - v->dSincLUT = gKaiserSinc; + v->fSincLUT = fKaiserSinc; } else { const double dHz = dPeriod2Hz(ch->finalPeriod); - const uint64_t delta64 = (int64_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> rounded 32.32 fixed-point + const uintCPUWord_t delta = v->oldDelta = (intCPUWord_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) - v->dOldHz = dHz; - v->oldDelta = delta64; - - // decide which sinc LUT to use according to resampling ratio - if (delta64 <= 0x130000000) // 1.1875 (32.32fp) - v->dSincLUT = gKaiserSinc; - else if (delta64 <= 0x180000000) // 1.5 (32.32fp) - v->dSincLUT = gDownSample1; + // decide which polyphase sinc LUT to use according to resampling ratio + if (delta <= (uintCPUWord_t)(1.1875 * MIXER_FRAC_SCALE)) + v->fSincLUT = fKaiserSinc; + else if (delta <= (uintCPUWord_t)(1.5 * MIXER_FRAC_SCALE)) + v->fSincLUT = fDownSample1; else - v->dSincLUT = gDownSample2; + v->fSincLUT = fDownSample2; + + // set scope delta + const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ; + v->scopeDelta = (intCPUWord_t)((dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) } } v->delta = v->oldDelta; - v->dHz = v->dOldHz; } - if (status & IS_NyTon) + if (status & IS_Trigger) voiceTrigger(i, ch->smpPtr, ch->smpStartPos); } } @@ -438,7 +452,8 @@ static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLe { // left channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 - dOut = ((audio.dMixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL; + dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; + dOut = (dOut + dPrng) - dPrngStateL; dPrngStateL = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); @@ -446,11 +461,16 @@ static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLe // right channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 - dOut = ((audio.dMixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR; + dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; + dOut = (dOut + dPrng) - dPrngStateR; dPrngStateR = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; + + // clear what we read from the mixing buffer + audio.fMixBufferL[i] = 0.0f; + audio.fMixBufferR[i] = 0.0f; } (void)numAudioChannels; @@ -465,21 +485,27 @@ static void sendSamples16BitDitherMultiChan(uint8_t *stream, uint32_t sampleBloc for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel - 1-bit triangular dithering - dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5 - dOut = ((audio.dMixBufferL[i] * dAudioNormalizeMul) + dPrng) - dPrngStateL; + dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 + dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; + dOut = (dOut + dPrng) - dPrngStateL; dPrngStateL = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; // right channel - 1-bit triangular dithering - dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5 - dOut = ((audio.dMixBufferR[i] * dAudioNormalizeMul) + dPrng) - dPrngStateR; + dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 + dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; + dOut = (dOut + dPrng) - dPrngStateR; dPrngStateR = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; + // clear what we read from the mixing buffer + audio.fMixBufferL[i] = 0.0f; + audio.fMixBufferR[i] = 0.0f; + // send zeroes to the rest of the channels for (uint32_t j = 2; j < numAudioChannels; j++) *streamPointer16++ = 0; @@ -488,20 +514,22 @@ static void sendSamples16BitDitherMultiChan(uint8_t *stream, uint32_t sampleBloc static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels) { - double dOut; - - float *fStreamPointer32 = (float *)stream; + float fOut, *fStreamPointer32 = (float *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel - dOut = audio.dMixBufferL[i] * dAudioNormalizeMul; - dOut = CLAMP(dOut, -1.0, 1.0); - *fStreamPointer32++ = (float)dOut; + fOut = audio.fMixBufferL[i] * fAudioNormalizeMul; + fOut = CLAMP(fOut, -1.0f, 1.0f); + *fStreamPointer32++ = fOut; // right channel - dOut = audio.dMixBufferR[i] * dAudioNormalizeMul; - dOut = CLAMP(dOut, -1.0, 1.0); - *fStreamPointer32++ = (float)dOut; + fOut = audio.fMixBufferR[i] * fAudioNormalizeMul; + fOut = CLAMP(fOut, -1.0f, 1.0f); + *fStreamPointer32++ = fOut; + + // clear what we read from the mixing buffer + audio.fMixBufferL[i] = 0.0f; + audio.fMixBufferR[i] = 0.0f; } (void)numAudioChannels; @@ -509,20 +537,22 @@ static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength, static void sendSamples32BitMultiChan(uint8_t *stream, uint32_t sampleBlockLength, uint8_t numAudioChannels) { - double dOut; - - float *fStreamPointer32 = (float *)stream; + float fOut, *fStreamPointer32 = (float *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel - dOut = audio.dMixBufferL[i] * dAudioNormalizeMul; - dOut = CLAMP(dOut, -1.0, 1.0); - *fStreamPointer32++ = (float)dOut; + fOut = audio.fMixBufferL[i] * fAudioNormalizeMul; + fOut = CLAMP(fOut, -1.0f, 1.0f); + *fStreamPointer32++ = fOut; // right channel - dOut = audio.dMixBufferR[i] * dAudioNormalizeMul; - dOut = CLAMP(dOut, -1.0, 1.0); - *fStreamPointer32++ = (float)dOut; + fOut = audio.fMixBufferR[i] * fAudioNormalizeMul; + fOut = CLAMP(fOut, -1.0f, 1.0f); + *fStreamPointer32++ = fOut; + + // clear what we read from the mixing buffer + audio.fMixBufferL[i] = 0.0f; + audio.fMixBufferR[i] = 0.0f; // send zeroes to the rest of the channels for (uint32_t j = 2; j < numAudioChannels; j++) @@ -533,37 +563,37 @@ static void sendSamples32BitMultiChan(uint8_t *stream, uint32_t sampleBlockLengt static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix) { voice_t *v = voice; // normal voices - voice_t *r = &voice[MAX_VOICES]; // volume ramp fadeout-voices + voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices - for (int32_t i = 0; i < song.antChn; i++, v++, r++) + for (int32_t i = 0; i < song.numChannels; i++, v++, r++) { if (v->active) { bool centerMixFlag; - const bool volRampFlag = v->volRampSamples > 0; + const bool volRampFlag = (v->volumeRampLength > 0); if (volRampFlag) { - centerMixFlag = (v->dDestVolL == v->dDestVolR) && (v->dVolDeltaL == v->dVolDeltaR); + centerMixFlag = (v->fVolumeLTarget == v->fVolumeRTarget) && (v->fVolumeLDelta == v->fVolumeRDelta); } else // no volume ramping active { - if (v->dVolL == 0.0 && v->dVolR == 0.0) + if (v->fVolumeL == 0.0f && v->fVolumeR == 0.0f) { silenceMixRoutine(v, samplesToMix); continue; } - centerMixFlag = v->dVolL == v->dVolR; + centerMixFlag = (v->fVolumeL == v->fVolumeR); } - mixFuncTab[(centerMixFlag * 36) + (volRampFlag * 18) + v->mixFuncOffset](v, bufferPosition, samplesToMix); + mixFuncTab[((int32_t)centerMixFlag * 36) + ((int32_t)volRampFlag * 18) + v->mixFuncOffset](v, bufferPosition, samplesToMix); } if (r->active) // volume ramp fadeout-voice { - const bool centerMixFlag = (r->dDestVolL == r->dDestVolR) && (r->dVolDeltaL == r->dVolDeltaR); - mixFuncTab[(centerMixFlag * 36) + 18 + r->mixFuncOffset](r, bufferPosition, samplesToMix); + const bool centerMixFlag = (r->fVolumeLTarget == r->fVolumeRTarget) && (r->fVolumeLDelta == r->fVolumeRDelta); + mixFuncTab[((int32_t)centerMixFlag * 36) + 18 + r->mixFuncOffset](r, bufferPosition, samplesToMix); } } } @@ -572,9 +602,6 @@ static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix) void mixReplayerTickToBuffer(uint32_t samplesToMix, uint8_t *stream, uint8_t bitDepth) { assert(samplesToMix <= MAX_WAV_RENDER_SAMPLES_PER_TICK); - memset(audio.dMixBufferL, 0, samplesToMix * sizeof (double)); - memset(audio.dMixBufferR, 0, samplesToMix * sizeof (double)); - doChannelMixing(0, samplesToMix); // normalize mix buffer and send to audio stream @@ -857,13 +884,13 @@ static void fillVisualsSyncBuffer(void) if (songPlaying) { // push pattern variables to sync queue - pattSyncData.timer = song.curReplayerTimer; - pattSyncData.patternPos = song.curReplayerPattPos; - pattSyncData.pattern = song.curReplayerPattNr; + pattSyncData.tick = song.curReplayerTick; + pattSyncData.row = song.curReplayerRow; + pattSyncData.pattNum = song.curReplayerPattNum; pattSyncData.songPos = song.curReplayerSongPos; - pattSyncData.speed = song.speed; - pattSyncData.tempo = (uint8_t)song.tempo; - pattSyncData.globalVol = (uint8_t)song.globVol; + pattSyncData.BPM = song.BPM; + pattSyncData.speed = (uint8_t)song.speed; + pattSyncData.globalVolume = (uint8_t)song.globalVolume; pattSyncData.timestamp = audio.tickTime64; pattQueuePush(pattSyncData); } @@ -871,24 +898,24 @@ static void fillVisualsSyncBuffer(void) // push channel variables to sync queue syncedChannel_t *c = chSyncData.channels; - stmTyp *s = stm; + channel_t *s = channel; voice_t *v = voice; - for (int32_t i = 0; i < song.antChn; i++, c++, s++, v++) + for (int32_t i = 0; i < song.numChannels; i++, c++, s++, v++) { - c->vol = s->finalVol; - c->dHz = v->dHz; - c->instrNr = s->instrNr; - c->sampleNr = s->sampleNr; + c->scopeVolume = v->scopeVolume; + c->scopeDelta = v->scopeDelta; + c->instrNum = s->instrNum; + c->smpNum = s->smpNum; c->status = s->tmpStatus; - c->smpStartPos = (uint16_t)s->smpStartPos; + c->smpStartPos = s->smpStartPos; - c->pianoNoteNr = 255; // no piano key + c->pianoNoteNum = 255; // no piano key if (songPlaying && (c->status & IS_Period) && s->envSustainActive) { - const int32_t note = getPianoKey(s->finalPeriod, s->fineTune, s->relTonNr); + const int32_t note = getPianoKey(s->finalPeriod, s->finetune, s->relativeNote); if (note >= 0 && note <= 95) - c->pianoNoteNr = (uint8_t)note; + c->pianoNoteNum = (uint8_t)note; } } @@ -914,18 +941,14 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) return; assert(len <= MAX_WAV_RENDER_SAMPLES_PER_TICK); - memset(audio.dMixBufferL, 0, len * sizeof (double)); - memset(audio.dMixBufferR, 0, len * sizeof (double)); int32_t bufferPosition = 0; int32_t samplesLeft = len; while (samplesLeft > 0) { - if (audio.dTickSampleCounter <= 0.0) + if (audio.tickSampleCounter64 <= 0) // new replayer tick { - // new replayer tick - replayerBusy = true; if (audio.volumeRampingFlag) @@ -935,12 +958,12 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) updateVoices(); fillVisualsSyncBuffer(); - audio.dTickSampleCounter += audio.dSamplesPerTick; + audio.tickSampleCounter64 += audio.samplesPerTick64; replayerBusy = false; } - const int32_t remainingTick = (int32_t)ceil(audio.dTickSampleCounter); + const int32_t remainingTick = (audio.tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards) int32_t samplesToMix = samplesLeft; if (samplesToMix > remainingTick) @@ -950,7 +973,8 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) bufferPosition += samplesToMix; samplesLeft -= samplesToMix; - audio.dTickSampleCounter -= samplesToMix; + audio.tickSampleCounter64 -= (int64_t)samplesToMix << 32; + } // normalize mix buffer and send to audio stream @@ -961,37 +985,41 @@ static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len) static bool setupAudioBuffers(void) { - const uint32_t sampleSize = sizeof (double); + const uint32_t sampleSize = sizeof (float); - audio.dMixBufferLUnaligned = (double *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256); - audio.dMixBufferRUnaligned = (double *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256); + audio.fMixBufferLUnaligned = (float *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256); + audio.fMixBufferRUnaligned = (float *)MALLOC_PAD(MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize, 256); - if (audio.dMixBufferLUnaligned == NULL || audio.dMixBufferRUnaligned == NULL) + if (audio.fMixBufferLUnaligned == NULL || audio.fMixBufferRUnaligned == NULL) return false; // make aligned main pointers - audio.dMixBufferL = (double *)ALIGN_PTR(audio.dMixBufferLUnaligned, 256); - audio.dMixBufferR = (double *)ALIGN_PTR(audio.dMixBufferRUnaligned, 256); + audio.fMixBufferL = (float *)ALIGN_PTR(audio.fMixBufferLUnaligned, 256); + audio.fMixBufferR = (float *)ALIGN_PTR(audio.fMixBufferRUnaligned, 256); + + // clear buffers + memset(audio.fMixBufferL, 0, MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize); + memset(audio.fMixBufferR, 0, MAX_WAV_RENDER_SAMPLES_PER_TICK * sampleSize); return true; } static void freeAudioBuffers(void) { - if (audio.dMixBufferLUnaligned != NULL) + if (audio.fMixBufferLUnaligned != NULL) { - free(audio.dMixBufferLUnaligned); - audio.dMixBufferLUnaligned = NULL; + free(audio.fMixBufferLUnaligned); + audio.fMixBufferLUnaligned = NULL; } - if (audio.dMixBufferRUnaligned != NULL) + if (audio.fMixBufferRUnaligned != NULL) { - free(audio.dMixBufferRUnaligned); - audio.dMixBufferRUnaligned = NULL; + free(audio.fMixBufferRUnaligned); + audio.fMixBufferRUnaligned = NULL; } - audio.dMixBufferL = NULL; - audio.dMixBufferR = NULL; + audio.fMixBufferL = NULL; + audio.fMixBufferR = NULL; } void updateSendAudSamplesRoutine(bool lockMixer) @@ -1058,7 +1086,7 @@ bool setupAudio(bool showErrorMsg) closeAudio(); if (config.audioFreq < MIN_AUDIO_FREQ || config.audioFreq > MAX_AUDIO_FREQ) - config.audioFreq = 48000; // set default rate + config.audioFreq = DEFAULT_AUDIO_FREQ; // get audio buffer size from config special flags @@ -1105,10 +1133,14 @@ bool setupAudio(bool showErrorMsg) // test if the received audio rate is compatible +#if CPU_64BIT if (have.freq != 44100 && have.freq != 48000 && have.freq != 96000 && have.freq != 192000) +#else + if (have.freq != 44100 && have.freq != 48000) +#endif { if (showErrorMsg) - showErrorMsgBox("Couldn't open audio device:\nThe program doesn't support an audio output rate of %dHz. Sorry!", have.freq); + showErrorMsgBox("Couldn't open audio device:\nThis program doesn't support an audio output rate of %dHz. Sorry!", have.freq); closeAudio(); return false; @@ -1162,22 +1194,22 @@ bool setupAudio(bool showErrorMsg) showConfigScreen(); updateWavRendererSettings(); - setAudioAmp(config.boostLevel, config.masterVol, (config.specialFlags & BITDEPTH_32) ? true : false); + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); // don't call stopVoices() in this routine - for (int32_t i = 0; i < MAX_VOICES; i++) + for (int32_t i = 0; i < MAX_CHANNELS; i++) stopVoice(i); stopAllScopes(); - audio.dTickSampleCounter = 0.0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick calcReplayerVars(audio.freq); - if (song.speed == 0) - song.speed = 125; + if (song.BPM == 0) + song.BPM = 125; - P_SetSpeed(song.speed); // this is important + setMixerBPM(song.BPM); // this is important updateSendAudSamplesRoutine(false); audio.resetSyncTickTimeFlag = true; diff --git a/src/ft2_audio.h b/src/ft2_audio.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include <SDL2/SDL.h> #include "ft2_replayer.h" +#include "ft2_cpu.h" enum { @@ -11,12 +12,15 @@ enum FREQ_TABLE_AMIGA = 1, }; +#define DEFAULT_AUDIO_FREQ 48000 + #define MIN_AUDIO_FREQ 44100 -#define MAX_AUDIO_FREQ 192000 -#define MIXER_FRAC_BITS 32 -#define MIXER_FRAC_SCALE (1ULL << MIXER_FRAC_BITS) -#define MIXER_FRAC_MASK (MIXER_FRAC_SCALE-1) +#if CPU_64BIT +#define MAX_AUDIO_FREQ 192000 +#else +#define MAX_AUDIO_FREQ 48000 +#endif #define MAX_AUDIO_DEVICES 99 @@ -31,11 +35,13 @@ typedef struct audio_t bool linearPeriodsFlag, rescanAudioDevicesSupported; volatile uint8_t interpolationType; int32_t quickVolRampSamples, inputDeviceNum, outputDeviceNum, lastWorkingAudioFreq, lastWorkingAudioBits; - uint32_t freq, audLatencyPerfValInt, audLatencyPerfValFrac, samplesPerTick, musicTimeSpeedVal; - uint64_t tickTime64, tickTime64Frac, tickTimeTab[MAX_BPM+1]; - double dRampQuickVolMul, dRampTickMul, dRampTickMulTab[MAX_BPM+1]; - double *dMixBufferL, *dMixBufferR, *dMixBufferLUnaligned, *dMixBufferRUnaligned, dHz2MixDeltaMul; - double dAudioLatencyMs, dSamplesPerTick, dTickSampleCounter, dSamplesPerTickTab[MAX_BPM+1]; + uint32_t tickTimeTab[(MAX_BPM-MIN_BPM)+1], tickTimeFracTab[(MAX_BPM-MIN_BPM)+1]; + int64_t tickSampleCounter64, samplesPerTick64, samplesPerTick64Tab[(MAX_BPM-MIN_BPM)+1]; + uint32_t freq, audLatencyPerfValInt, audLatencyPerfValFrac, samplesPerTick, samplesPerTickFrac, musicTimeSpeedVal; + uint64_t tickTime64, tickTime64Frac; + float fRampQuickVolMul, fRampTickMul, fRampTickMulTab[(MAX_BPM-MIN_BPM)+1]; + float *fMixBufferL, *fMixBufferR, *fMixBufferLUnaligned, *fMixBufferRUnaligned; + double dHz2MixDeltaMul, dAudioLatencyMs; SDL_AudioDeviceID dev; uint32_t wantFreq, haveFreq, wantSamples, haveSamples, wantChannels, haveChannels; @@ -45,24 +51,25 @@ typedef struct { const int8_t *base8, *revBase8; const int16_t *base16, *revBase16; - bool active, backwards, isFadeOutVoice, hasLooped; - uint8_t mixFuncOffset, pan, loopType; - int32_t pos, end, loopStart, loopLength, oldPeriod; - uint32_t volRampSamples; - uint64_t posFrac, delta, oldDelta; + bool active, samplingBackwards, isFadeOutVoice, hasLooped; + uint8_t mixFuncOffset, panning, loopType, scopeVolume; + int32_t position, sampleEnd, loopStart, loopLength, oldPeriod; + uint32_t volumeRampLength; + uintCPUWord_t positionFrac, delta, oldDelta, scopeDelta; +# // if (loopEnabled && hasLooped && samplingPos <= loopStart+SINC_LEFT_TAPS) readFixedTapsFromThisPointer(); const int8_t *leftEdgeTaps8; const int16_t *leftEdgeTaps16; - const double *dSincLUT; - double dOldHz, dHz, dVol, dDestVolL, dDestVolR, dVolL, dVolR, dVolDeltaL, dVolDeltaR; + const float *fSincLUT; + float fVolume, fVolumeL, fVolumeR, fVolumeLDelta, fVolumeRDelta, fVolumeLTarget, fVolumeRTarget; } voice_t; typedef struct pattSyncData_t { - uint8_t pattern, globalVol, songPos, timer, tempo, patternPos; - uint16_t speed; + uint8_t pattNum, globalVolume, songPos, tick, speed, row; + uint16_t BPM; uint64_t timestamp; } pattSyncData_t; @@ -74,7 +81,7 @@ typedef struct pattSync_t typedef struct chSyncData_t { - syncedChannel_t channels[MAX_VOICES]; + syncedChannel_t channels[MAX_CHANNELS]; uint64_t timestamp; } chSyncData_t; @@ -100,11 +107,14 @@ chSyncData_t *chQueuePeek(void); uint64_t getChQueueTimestamp(void); void resetSyncQueues(void); +void decreaseMasterVol(void); +void increaseMasterVol(void); + void calcPanningTable(void); void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag); void setNewAudioFreq(uint32_t freq); void setBackOldAudioFreq(void); -void P_SetSpeed(uint16_t bpm); +void setMixerBPM(int32_t bpm); void audioSetVolRamp(bool volRamp); void audioSetInterpolationType(uint8_t interpolationType); void stopVoice(int32_t i); diff --git a/src/ft2_audioselector.c b/src/ft2_audioselector.c @@ -360,62 +360,42 @@ void freeAudioDeviceSelectorBuffers(void) void setToDefaultAudioOutputDevice(void) { - const char *devString = SDL_GetAudioDeviceName(0, false); - if (devString == NULL) - { - if (audio.currOutputDevice != NULL) - { - free(audio.currOutputDevice); - audio.currOutputDevice = NULL; - } - - return; - } - - const uint32_t stringLen = (uint32_t)strlen(devString); - if (audio.currOutputDevice != NULL) { free(audio.currOutputDevice); audio.currOutputDevice = NULL; } - audio.currOutputDevice = (char *)malloc(stringLen + 1); - if (audio.currOutputDevice == NULL) - return; - - if (stringLen > 0) - strcpy(audio.currOutputDevice, devString); + /* If we have more than one device, we can't know which one + ** is the default (and thus not get its device name). + ** SDL2 ought to have a function for this! + */ + if (SDL_GetNumAudioDevices(false) == 1) + { + const char *devName = SDL_GetAudioDeviceName(0, false); + if (devName != NULL) + audio.currOutputDevice = strdup(devName); + } } void setToDefaultAudioInputDevice(void) { - const char *devString = SDL_GetAudioDeviceName(0, true); - if (devString == NULL) - { - if (audio.currInputDevice != NULL) - { - free(audio.currInputDevice); - audio.currInputDevice = NULL; - } - - return; - } - - const uint32_t stringLen = (uint32_t)strlen(devString); - if (audio.currInputDevice != NULL) { free(audio.currInputDevice); audio.currInputDevice = NULL; } - audio.currInputDevice = (char *)malloc(stringLen + 1); - if (audio.currInputDevice == NULL) - return; - - if (stringLen > 0) - strcpy(audio.currInputDevice, devString); + /* If we have more than one device, we can't know which one + ** is the default (and thus not get its device name). + ** SDL2 ought to have a function for this! + */ + if (SDL_GetNumAudioDevices(true) == 1) + { + const char *devName = SDL_GetAudioDeviceName(0, true); + if (devName != NULL) + audio.currInputDevice = strdup(devName); + } } void rescanAudioDevices(void) diff --git a/src/ft2_config.c b/src/ft2_config.c @@ -32,6 +32,7 @@ #include "ft2_tables.h" #include "ft2_bmp.h" #include "ft2_structs.h" +#include "ft2_cpu.h" config_t config; // globalized @@ -65,12 +66,18 @@ static int32_t calcChecksum(const uint8_t *p, uint16_t len) // for Nibbles highs return checksum; } -static void loadConfigFromBuffer(void) +static void loadConfigFromBuffer(bool defaults) { lockMixerCallback(); memcpy(&config, configBuffer, CONFIG_FILE_SIZE); + if (defaults) + config.audioFreq = DEFAULT_AUDIO_FREQ; + + if (config.audioFreq > MAX_AUDIO_FREQ) + config.audioFreq = MAX_AUDIO_FREQ; + // if Nibbles highscore checksum is incorrect, load default highscores instead const int32_t newChecksum = calcChecksum((uint8_t *)&config.NI_HighScore, sizeof (config.NI_HighScore)); if (newChecksum != config.NI_HighScoreChecksum) @@ -125,9 +132,9 @@ static void loadConfigFromBuffer(void) config.ptnMaxChannels = CLAMP(config.ptnMaxChannels, 0, 3); config.ptnFont = CLAMP(config.ptnFont, 0, 3); config.mouseType = CLAMP(config.mouseType, 0, 3); - config.cfg_StdPalNr = CLAMP(config.cfg_StdPalNr, 0, 11); + config.cfg_StdPalNum = CLAMP(config.cfg_StdPalNum, 0, 11); config.cfg_SortPriority = CLAMP(config.cfg_SortPriority, 0, 1); - config.NI_AntPlayers = CLAMP(config.NI_AntPlayers, 0, 1); + config.NI_NumPlayers = CLAMP(config.NI_NumPlayers, 0, 1); config.NI_Speed = CLAMP(config.NI_Speed, 0, 3); config.recMIDIVolSens = CLAMP(config.recMIDIVolSens, 0, 200); config.recMIDIChn = CLAMP(config.recMIDIChn, 1, 16); @@ -146,8 +153,12 @@ static void loadConfigFromBuffer(void) config.recQuantRes = 16; } +#if CPU_64BIT if (config.audioFreq != 44100 && config.audioFreq != 48000 && config.audioFreq != 96000 && config.audioFreq != 192000) - config.audioFreq = 48000; +#else + if (config.audioFreq != 44100 && config.audioFreq != 48000) +#endif + config.audioFreq = DEFAULT_AUDIO_FREQ; if (config.audioInputFreq <= 1) // default value from FT2 (this was cdr_Sync) - set defaults config.audioInputFreq = INPUT_FREQ_48KHZ; @@ -174,12 +185,12 @@ static void loadConfigFromBuffer(void) audioSetInterpolationType(config.interpolation); audioSetVolRamp((config.specialFlags & NO_VOLRAMP_FLAG) ? false : true); - setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32); + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); setMouseShape(config.mouseType); changeLogoType(config.id_FastLogo); changeBadgeType(config.id_TritonProd); ui.maxVisibleChannels = (uint8_t)(2 + ((config.ptnMaxChannels + 1) * 2)); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); updatePattFontPtrs(); unlockMixerCallback(); @@ -195,7 +206,7 @@ static void configDrawAmp(void) static void setDefaultConfigSettings(void) { memcpy(configBuffer, defConfigData, CONFIG_FILE_SIZE); - loadConfigFromBuffer(); + loadConfigFromBuffer(true); } void resetConfig(void) @@ -320,7 +331,7 @@ bool loadConfig(bool showErrorFlag) return false; } - loadConfigFromBuffer(); + loadConfigFromBuffer(false); return true; } @@ -702,7 +713,7 @@ void loadConfigOrSetDefaults(void) return; } - loadConfigFromBuffer(); + loadConfigFromBuffer(false); } // GUI-related code @@ -812,8 +823,10 @@ void setConfigIORadioButtonStates(void) // accessed by other .c files { case 44100: tmpID = RB_CONFIG_AUDIO_44KHZ; break; default: case 48000: tmpID = RB_CONFIG_AUDIO_48KHZ; break; +#if CPU_64BIT case 96000: tmpID = RB_CONFIG_AUDIO_96KHZ; break; case 192000: tmpID = RB_CONFIG_AUDIO_192KHZ; break; +#endif } radioButtons[tmpID].state = RADIOBUTTON_CHECKED; @@ -850,14 +863,14 @@ static void setConfigIOCheckButtonStates(void) static void setConfigLayoutCheckButtonStates(void) { - checkBoxes[CB_CONF_PATTSTRETCH].checked = config.ptnUnpressed; + checkBoxes[CB_CONF_PATTSTRETCH].checked = config.ptnStretch; checkBoxes[CB_CONF_HEXCOUNT].checked = config.ptnHex; checkBoxes[CB_CONF_ACCIDENTAL].checked = config.ptnAcc ? true : false; checkBoxes[CB_CONF_SHOWZEROES].checked = config.ptnInstrZero; checkBoxes[CB_CONF_FRAMEWORK].checked = config.ptnFrmWrk; checkBoxes[CB_CONF_LINECOLORS].checked = config.ptnLineLight; checkBoxes[CB_CONF_CHANNUMS].checked = config.ptnChnNumbers; - checkBoxes[CB_CONF_SHOW_VOLCOL].checked = config.ptnS3M; + checkBoxes[CB_CONF_SHOW_VOLCOL].checked = config.ptnShowVolColumn; checkBoxes[CB_CONF_SOFTWARE_MOUSE].checked = (config.specialFlags2 & HARDWARE_MOUSE) ? false : true; showCheckBox(CB_CONF_PATTSTRETCH); @@ -939,12 +952,12 @@ static void setConfigLayoutRadioButtonStates(void) // PALETTE ENTRIES uncheckRadioButtonGroup(RB_GROUP_CONFIG_PAL_ENTRIES); - radioButtons[RB_CONFIG_PAL_PATTERNTEXT + cfg_ColorNr].state = RADIOBUTTON_CHECKED; + radioButtons[RB_CONFIG_PAL_PATTERNTEXT + cfg_ColorNum].state = RADIOBUTTON_CHECKED; showRadioButtonGroup(RB_GROUP_CONFIG_PAL_ENTRIES); // PALETTE PRESET uncheckRadioButtonGroup(RB_GROUP_CONFIG_PAL_PRESET); - switch (config.cfg_StdPalNr) + switch (config.cfg_StdPalNum) { default: case PAL_ARCTIC: tmpID = RB_CONFIG_PAL_ARCTIC; break; @@ -1141,9 +1154,10 @@ void showConfigScreen(void) textOutShadow(509, 3, PAL_FORGRND, PAL_DSKTOP2, "Mixing frequency:"); textOutShadow(525, 17, PAL_FORGRND, PAL_DSKTOP2, "44100Hz"); textOutShadow(525, 31, PAL_FORGRND, PAL_DSKTOP2, "48000Hz (default)"); +#if CPU_64BIT textOutShadow(525, 45, PAL_FORGRND, PAL_DSKTOP2, "96000Hz"); textOutShadow(525, 59, PAL_FORGRND, PAL_DSKTOP2, "192000Hz"); - +#endif textOutShadow(509, 76, PAL_FORGRND, PAL_DSKTOP2, "Frequency table:"); textOutShadow(525, 90, PAL_FORGRND, PAL_DSKTOP2, "Amiga freq. table"); textOutShadow(525, 104, PAL_FORGRND, PAL_DSKTOP2, "Linear freq. table"); @@ -1605,6 +1619,7 @@ void rbConfigAudio48kHz(void) setNewAudioSettings(); } +#if CPU_64BIT void rbConfigAudio96kHz(void) { config.audioFreq = 96000; @@ -1616,6 +1631,7 @@ void rbConfigAudio192kHz(void) config.audioFreq = 192000; setNewAudioSettings(); } +#endif void rbConfigAudioInput44kHz(void) { @@ -1638,14 +1654,14 @@ void rbConfigAudioInput96kHz(void) void rbConfigFreqTableAmiga(void) { lockMixerCallback(); - setFrqTab(false); + setFrequencyTable(false); unlockMixerCallback(); } void rbConfigFreqTableLinear(void) { lockMixerCallback(); - setFrqTab(true); + setFrequencyTable(true); unlockMixerCallback(); } @@ -1664,7 +1680,7 @@ void cbConfigVolRamp(void) static void redrawPatternEditor(void) // called after changing some pattern editor settings in config { // if the cursor was on the volume column while we turned volume column off, move it to effect type slot - if (!config.ptnS3M && (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)) + if (!config.ptnShowVolColumn && (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2)) cursor.object = CURSOR_EFX0; updateChanNums(); @@ -1673,7 +1689,7 @@ static void redrawPatternEditor(void) // called after changing some pattern edit void cbConfigPattStretch(void) { - config.ptnUnpressed ^= 1; + config.ptnStretch ^= 1; redrawPatternEditor(); } @@ -1716,7 +1732,7 @@ void cbConfigChanNums(void) void cbConfigShowVolCol(void) { - config.ptnS3M ^= 1; + config.ptnShowVolColumn ^= 1; redrawPatternEditor(); } @@ -2156,7 +2172,7 @@ void sbAmp(uint32_t pos) if (config.boostLevel != (int8_t)pos + 1) { config.boostLevel = (int8_t)pos + 1; - setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32); + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); configDrawAmp(); updateWavRendererSettings(); } @@ -2177,7 +2193,7 @@ void sbMasterVol(uint32_t pos) if (config.masterVol != (int16_t)pos) { config.masterVol = (int16_t)pos; - setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32); + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); } } diff --git a/src/ft2_config.h b/src/ft2_config.h @@ -4,6 +4,7 @@ #include <stdbool.h> #include "ft2_replayer.h" #include "ft2_palette.h" +#include "ft2_cpu.h" #define CFG_ID_STR "FastTracker 2.0 configuration file\x1A" #define CONFIG_FILE_SIZE 1736 @@ -110,17 +111,18 @@ __attribute__ ((packed)) #endif highScoreType; +// this has some Swedish names, those variables are not used in our clone. typedef struct config_t // exact FT2.CFG layout (with some modifications) { char cfgID[35]; - uint16_t ver; + uint16_t version; uint32_t audioFreq; // was "BIOSSum" (never used in FT2) int16_t utEnhet, masterVol, inputVol, inputDev; uint8_t interpolation, internMode, stereoMode; uint8_t specialFlags2; // was lo-byte of "sample16Bit" (was used for external audio sampling) uint8_t dontShowAgainFlags; // was hi-byte of "sample16Bit" (was used for external audio sampling) int16_t inEnhet, sbPort, sbDMA, sbHiDMA, sbInt, sbOutFilter; - uint8_t true16Bit, ptnUnpressed, ptnHex, ptnInstrZero, ptnFrmWrk, ptnLineLight, ptnS3M, ptnChnNumbers; + uint8_t true16Bit, ptnStretch, ptnHex, ptnInstrZero, ptnFrmWrk, ptnLineLight, ptnShowVolColumn, ptnChnNumbers; int16_t ptnLineLightStep, ptnFont, ptnAcc; pal16 userPal[16]; uint16_t comMacro[10], volMacro[10]; @@ -146,18 +148,18 @@ typedef struct config_t // exact FT2.CFG layout (with some modifications) uint8_t tracksPathLen; char tracksPath[79+1]; uint8_t id_FastLogo, id_TritonProd; - int16_t cfg_StdPalNr; + int16_t cfg_StdPalNum; uint8_t cfg_AutoSave; int16_t smpEd_SampleNote; highScoreType NI_HighScore[10]; - int16_t NI_AntPlayers, NI_Speed; + int16_t NI_NumPlayers, NI_Speed; uint8_t NI_Surround, NI_Grid, NI_Wrap; int32_t NI_HighScoreChecksum; - int16_t mouseType, mouseAnimType, mouseSpeed, keyLayout, boostLevel, stdEnvP[6][2][12][2]; - uint16_t stdVolEnvAnt[6], stdVolEnvSust[6], stdVolEnvRepS[6], stdVolEnvRepE[6]; - uint16_t stdPanEnvAnt[6], stdPanEnvSust[6], stdPanEnvRepS[6], stdPanEnvRepE[6]; - uint16_t stdFadeOut[6], stdVibRate[6], stdVibDepth[6], stdVibSweep[6], stdVibTyp[6]; - uint16_t stdVolEnvTyp[6], stdPanEnvTyp[6]; + int16_t mouseType, mouseAnimType, mouseSpeed, keyLayout, boostLevel, stdEnvPoints[6][2][12][2]; + uint16_t stdVolEnvLength[6], stdVolEnvSustain[6], stdVolEnvLoopStart[6], stdVolEnvLoopEnd[6]; + uint16_t stdPanEnvLength[6], stdPanEnvSustain[6], stdPanEnvLoopStart[6], stdPanEnvLoopEnd[6]; + uint16_t stdFadeout[6], stdVibRate[6], stdVibDepth[6], stdVibSweep[6], stdVibType[6]; + uint16_t stdVolEnvFlags[6], stdPanEnvFlags[6]; int16_t antStars, ptnMaxChannels; uint16_t sampleRates[16]; uint8_t cfg_OverwriteWarning; @@ -216,8 +218,10 @@ void rbConfigAudioIntrpLinear(void); void rbConfigAudioIntrpSinc(void); void rbConfigAudio44kHz(void); void rbConfigAudio48kHz(void); +#if CPU_64BIT void rbConfigAudio96kHz(void); void rbConfigAudio192kHz(void); +#endif void rbConfigAudioInput44kHz(void); void rbConfigAudioInput48kHz(void); void rbConfigAudioInput96kHz(void); diff --git a/src/ft2_cpu.h b/src/ft2_cpu.h @@ -0,0 +1,32 @@ +#pragma once + +#include <stdint.h> + +#ifdef _WIN32 + +#ifdef _WIN64 +#define CPU_64BIT 1 +#else +#define CPU_64BIT 0 +#endif + +#else +#include <limits.h> + +#if __WORDSIZE == 64 +#define CPU_64BIT 1 +#else +#define CPU_64BIT 0 +#endif + +#endif + +#if CPU_64BIT +#define CPU_BITS 64 +#define uintCPUWord_t uint64_t +#define intCPUWord_t int64_t +#else +#define CPU_BITS 32 +#define uintCPUWord_t uint32_t +#define intCPUWord_t int32_t +#endif diff --git a/src/ft2_diskop.c b/src/ft2_diskop.c @@ -848,18 +848,18 @@ void trimEntryName(char *name, bool isDir) } } -static void createOverwriteText(char *name) +void createFileOverwriteText(char *filename, char *buffer) { char nameTmp[128]; // read entry name to a small buffer - const uint32_t nameLen = (uint32_t)strlen(name); - memcpy(nameTmp, name, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1)); + const uint32_t nameLen = (uint32_t)strlen(filename); + memcpy(nameTmp, filename, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1)); nameTmp[sizeof (nameTmp) - 1] = '\0'; trimEntryName(nameTmp, false); - sprintf(FReq_SysReqText, "Overwrite file \"%s\"?", nameTmp); + sprintf(buffer, "Overwrite file \"%s\"?", nameTmp); } static void diskOpSave(bool checkOverwrite) @@ -908,7 +908,7 @@ static void diskOpSave(bool checkOverwrite) if (checkOverwrite && fileExistsAnsi(FReq_FileName)) { - createOverwriteText(FReq_FileName); + createFileOverwriteText(FReq_FileName, FReq_SysReqText); if (okBox(2, "System request", FReq_SysReqText) != 1) return; } @@ -932,7 +932,7 @@ static void diskOpSave(bool checkOverwrite) if (checkOverwrite && fileExistsAnsi(FReq_FileName)) { - createOverwriteText(FReq_FileName); + createFileOverwriteText(FReq_FileName, FReq_SysReqText); if (okBox(2, "System request", FReq_SysReqText) != 1) return; } @@ -961,7 +961,7 @@ static void diskOpSave(bool checkOverwrite) if (checkOverwrite && fileExistsAnsi(FReq_FileName)) { - createOverwriteText(FReq_FileName); + createFileOverwriteText(FReq_FileName, FReq_SysReqText); if (okBox(2, "System request", FReq_SysReqText) != 1) return; } @@ -985,7 +985,7 @@ static void diskOpSave(bool checkOverwrite) if (checkOverwrite && fileExistsAnsi(FReq_FileName)) { - createOverwriteText(FReq_FileName); + createFileOverwriteText(FReq_FileName, FReq_SysReqText); if (okBox(2, "System request", FReq_SysReqText) != 1) return; } @@ -1008,7 +1008,7 @@ static void diskOpSave(bool checkOverwrite) if (checkOverwrite && fileExistsAnsi(FReq_FileName)) { - createOverwriteText(FReq_FileName); + createFileOverwriteText(FReq_FileName, FReq_SysReqText); if (okBox(2, "System request", FReq_SysReqText) != 1) return; } diff --git a/src/ft2_diskop.h b/src/ft2_diskop.h @@ -82,4 +82,5 @@ void rbDiskOpSmpSaveWav(void); void rbDiskOpSmpSaveRaw(void); void rbDiskOpSmpSaveIff(void); void trimEntryName(char *name, bool isDir); +void createFileOverwriteText(char *filename, char *buffer); bool fileExistsAnsi(char *str); diff --git a/src/ft2_edit.c b/src/ft2_edit.c @@ -33,11 +33,11 @@ static uint16_t ptnBufLen, trkBufLen; static int8_t lastTranspVal; static uint8_t lastInsMode, lastTranspMode; static uint32_t transpDelNotes; // count of under-/overflowing notes for warning message -static tonTyp clearNote; +static note_t clearNote; -static tonTyp blkCopyBuff[MAX_PATT_LEN * MAX_VOICES]; -static tonTyp ptnCopyBuff[MAX_PATT_LEN * MAX_VOICES]; -static tonTyp trackCopyBuff[MAX_PATT_LEN]; +static note_t blkCopyBuff[MAX_PATT_LEN * MAX_CHANNELS]; +static note_t ptnCopyBuff[MAX_PATT_LEN * MAX_CHANNELS]; +static note_t trackCopyBuff[MAX_PATT_LEN]; static const int8_t tickArr[16] = { 16, 8, 0, 4, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1 }; @@ -47,7 +47,7 @@ void recordNote(uint8_t note, int8_t vol); static bool testNoteKeys(SDL_Scancode scancode) { const int8_t noteNum = scancodeKeyToNote(scancode); - if (noteNum == 97) + if (noteNum == NOTE_OFF) { // inserts "note off" if editing song if (playMode == PLAYMODE_EDIT || playMode == PLAYMODE_RECPATT || playMode == PLAYMODE_RECSONG) @@ -55,11 +55,11 @@ static bool testNoteKeys(SDL_Scancode scancode) if (!allocatePattern(editor.editPattern)) return true; // key pressed - patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch].ton = 97; + pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch].note = NOTE_OFF; - const uint16_t pattLen = pattLens[editor.editPattern]; - if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + const uint16_t numRows = patternNumRows[editor.editPattern]; + if (playMode == PLAYMODE_EDIT && numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -88,7 +88,6 @@ void testNoteKeysRelease(SDL_Scancode scancode) static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) { int8_t i; - uint8_t oldVal; if (cursor.object == CURSOR_NOTE) { @@ -153,91 +152,88 @@ static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) // insert slot data - tonTyp *ton = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch]; + note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; switch (cursor.object) { case CURSOR_INST1: { - oldVal = ton->instr; + uint8_t oldVal = p->instr; - ton->instr = (ton->instr & 0x0F) | (i << 4); - if (ton->instr > MAX_INST) - ton->instr = MAX_INST; + p->instr = (p->instr & 0x0F) | (i << 4); + if (p->instr > MAX_INST) + p->instr = MAX_INST; - if (ton->instr != oldVal) + if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_INST2: { - oldVal = ton->instr; - ton->instr = (ton->instr & 0xF0) | i; + uint8_t oldVal = p->instr; + p->instr = (p->instr & 0xF0) | i; - if (ton->instr != oldVal) + if (p->instr != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL1: { - oldVal = ton->vol; + uint8_t oldVal = p->vol; - ton->vol = (ton->vol & 0x0F) | ((i + 1) << 4); - if (ton->vol >= 0x51 && ton->vol <= 0x5F) - ton->vol = 0x50; + p->vol = (p->vol & 0x0F) | ((i + 1) << 4); + if (p->vol >= 0x51 && p->vol <= 0x5F) + p->vol = 0x50; - if (ton->vol != oldVal) + if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_VOL2: { - oldVal = ton->vol; + uint8_t oldVal = p->vol; - if (ton->vol < 0x10) - ton->vol = 0x10 + i; + if (p->vol < 0x10) + p->vol = 0x10 + i; else - ton->vol = (ton->vol & 0xF0) | i; + p->vol = (p->vol & 0xF0) | i; - if (ton->vol >= 0x51 && ton->vol <= 0x5F) - ton->vol = 0x50; + if (p->vol >= 0x51 && p->vol <= 0x5F) + p->vol = 0x50; - if (ton->vol != oldVal) + if (p->vol != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX0: { - oldVal = ton->effTyp; + uint8_t oldVal = p->efx; - ton->effTyp = i; - - if (ton->effTyp != oldVal) + p->efx = i; + if (p->efx != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX1: { - oldVal = ton->eff; - - ton->eff = (ton->eff & 0x0F) | (i << 4); + uint8_t oldVal = p->efxData; - if (ton->eff != oldVal) + p->efxData = (p->efxData & 0x0F) | (i << 4); + if (p->efxData != oldVal) setSongModifiedFlag(); } break; case CURSOR_EFX2: { - oldVal = ton->eff; - - ton->eff = (ton->eff & 0xF0) | i; + uint8_t oldVal = p->efxData; - if (ton->eff != oldVal) + p->efxData = (p->efxData & 0xF0) | i; + if (p->efxData != oldVal) setSongModifiedFlag(); } break; @@ -247,9 +243,9 @@ static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) // increase row (only in edit mode) - const int16_t pattLen = pattLens[editor.editPattern]; - if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + const int16_t numRows = patternNumRows[editor.editPattern]; + if (playMode == PLAYMODE_EDIT && numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); if (i == 0) // if we inserted a zero, check if pattern is empty, for killing killPatternIfUnused(editor.editPattern); @@ -258,94 +254,92 @@ static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) return true; } -// directly ported from the original FT2 code (fun fact: named EvulateTimeStamp() in the FT2 code) -static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNr, int16_t *pattPos, int16_t *tick) +static void evaluateTimeStamp(int16_t *songPos, int16_t *pattNum, int16_t *row, int16_t *tick) { - int16_t sp = editor.songPos; - int16_t nr = editor.editPattern; - int16_t row = editor.pattPos; - int16_t t = editor.tempo - editor.timer; + int16_t outSongPos = editor.songPos; + int16_t outPattern = editor.editPattern; + int16_t outRow = editor.row; + int16_t outTick = editor.speed - editor.tick; - t = CLAMP(t, 0, editor.tempo - 1); + outTick = CLAMP(outTick, 0, editor.speed - 1); // this is needed, but also breaks quantization on speed>15 - if (t > 15) - t = 15; + if (outTick > 15) + outTick = 15; - const int16_t pattLen = pattLens[nr]; + const int16_t numRows = patternNumRows[outPattern]; if (config.recQuant > 0) { - int16_t r; if (config.recQuantRes >= 16) { - t += (editor.tempo >> 1) + 1; + outTick += (editor.speed >> 1) + 1; } else { - r = tickArr[config.recQuantRes-1]; + int16_t r = tickArr[config.recQuantRes-1]; + int16_t p = outRow & (r - 1); - int16_t p = row & (r - 1); if (p < (r >> 1)) - row -= p; + outRow -= p; else - row = (row + r) - p; + outRow = (outRow + r) - p; - t = 0; + outTick = 0; } } - if (t > editor.tempo) + if (outTick > editor.speed) { - t -= editor.tempo; - row++; + outTick -= editor.speed; + outRow++; } - if (row >= pattLen) + if (outRow >= numRows) { + outRow = 0; + if (playMode == PLAYMODE_RECSONG) - sp++; + outSongPos++; - row = 0; - if (sp >= song.len) - sp = song.repS; + if (outSongPos >= song.songLength) + outSongPos = song.songLoopStart; - nr = song.songTab[sp]; + outPattern = song.orders[outSongPos]; } - *songPos = sp; - *pattNr = nr; - *pattPos = row; - *tick = t; + *songPos = outSongPos; + *pattNum = outPattern; + *row = outRow; + *tick = outTick; } -// directly ported from the original FT2 code - what a mess, but it works... -void recordNote(uint8_t note, int8_t vol) +void recordNote(uint8_t noteNum, int8_t vol) // directly ported from the original FT2 code - what a mess, but it works... { int8_t i; - int16_t nr, sp, pattpos, tick; + int16_t pattNum, songPos, row, tick; int32_t time; - tonTyp *noteData; + note_t *p; - const int16_t oldpattpos = editor.pattPos; + const int16_t oldRow = editor.row; if (songPlaying) { // row quantization - evaluateTimeStamp(&sp, &nr, &pattpos, &tick); + evaluateTimeStamp(&songPos, &pattNum, &row, &tick); } else { - sp = editor.songPos; - nr = editor.editPattern; - pattpos = editor.pattPos; + songPos = editor.songPos; + pattNum = editor.editPattern; + row = editor.row; tick = 0; } bool editmode = (playMode == PLAYMODE_EDIT); bool recmode = (playMode == PLAYMODE_RECSONG) || (playMode == PLAYMODE_RECPATT); - if (note == 97) + if (noteNum == NOTE_OFF) vol = 0; int8_t c = -1; @@ -358,7 +352,7 @@ void recordNote(uint8_t note, int8_t vol) if ((config.multiEdit && editmode) || (config.multiRec && recmode)) { time = 0x7FFFFFFF; - for (i = 0; i < song.antChn; i++) + for (i = 0; i < song.numChannels; i++) { if (editor.chnMode[i] && config.multiRecChn[i] && editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { @@ -372,9 +366,9 @@ void recordNote(uint8_t note, int8_t vol) c = cursor.ch; } - for (i = 0; i < song.antChn; i++) + for (i = 0; i < song.numChannels; i++) { - if (note == editor.keyOnTab[i] && config.multiRecChn[i]) + if (noteNum == editor.keyOnTab[i] && config.multiRecChn[i]) k = i; } } @@ -388,7 +382,7 @@ void recordNote(uint8_t note, int8_t vol) if (songPlaying) { - for (i = 0; i < song.antChn; i++) + for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0 && config.multiRecChn[i]) { @@ -400,7 +394,7 @@ void recordNote(uint8_t note, int8_t vol) if (time == 0x7FFFFFFF) { - for (i = 0; i < song.antChn; i++) + for (i = 0; i < song.numChannels; i++) { if (editor.keyOffTime[i] < time && editor.keyOnTab[i] == 0) { @@ -415,9 +409,9 @@ void recordNote(uint8_t note, int8_t vol) c = cursor.ch; } - for (i = 0; i < song.antChn; i++) + for (i = 0; i < song.numChannels; i++) { - if (note == editor.keyOnTab[i]) + if (noteNum == editor.keyOnTab[i]) k = i; } } @@ -429,45 +423,45 @@ void recordNote(uint8_t note, int8_t vol) // play note - editor.keyOnTab[c] = note; + editor.keyOnTab[c] = noteNum; - if (pattpos >= oldpattpos) // non-FT2 fix: only do this if we didn't quantize to next row + if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row { #ifdef HAS_MIDI - playTone(c, editor.curInstr, note, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); + playTone(c, editor.curInstr, noteNum, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); #else - playTone(c, editor.curInstr, note, vol, 0, 0); + playTone(c, editor.curInstr, noteNum, vol, 0, 0); #endif } if (editmode || recmode) { - if (allocatePattern(nr)) + if (allocatePattern(pattNum)) { - const int16_t pattLen = pattLens[nr]; - noteData = &patt[nr][(pattpos * MAX_VOICES) + c]; + const int16_t numRows = patternNumRows[pattNum]; + p = &pattern[pattNum][(row * MAX_CHANNELS) + c]; // insert data - noteData->ton = note; + p->note = noteNum; if (editor.curInstr > 0) - noteData->instr = editor.curInstr; + p->instr = editor.curInstr; if (vol >= 0) - noteData->vol = 0x10 + vol; + p->vol = 0x10 + vol; if (!recmode) { // increase row (only in edit mode) - if (pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + if (numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); } else { // apply tick delay for note if quantization is disabled if (!config.recQuant && tick > 0) { - noteData->effTyp = 0x0E; - noteData->eff = 0xD0 + (tick & 0x0F); + p->efx = 0x0E; + p->efxData = 0xD0 + (tick & 0x0F); } } @@ -486,59 +480,63 @@ void recordNote(uint8_t note, int8_t vol) if (c < 0) return; - editor.keyOnTab[c] = 0; - editor.keyOffTime[c] = ++editor.keyOffNr; + editor.keyOffNr++; + + editor.keyOnTab[c] = 0; + editor.keyOffTime[c] = editor.keyOffNr; - if (pattpos >= oldpattpos) // non-FT2 fix: only do this if we didn't quantize to next row + if (row >= oldRow) // non-FT2 fix: only do this if we didn't quantize to next row { #ifdef HAS_MIDI - playTone(c, editor.curInstr, 97, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); + playTone(c, editor.curInstr, NOTE_OFF, vol, midi.currMIDIVibDepth, midi.currMIDIPitch); #else - playTone(c, editor.curInstr, 97, vol, 0, 0); + playTone(c, editor.curInstr, NOTE_OFF, vol, 0, 0); #endif } if (config.recRelease && recmode) { - if (allocatePattern(nr)) + if (allocatePattern(pattNum)) { // insert data - int16_t pattLen = pattLens[nr]; - noteData = &patt[nr][(pattpos * MAX_VOICES) + c]; + int16_t numRows = patternNumRows[pattNum]; + p = &pattern[pattNum][(row * MAX_CHANNELS) + c]; - if (noteData->ton != 0) - pattpos++; + if (p->note != 0) + row++; - if (pattpos >= pattLen) + if (row >= numRows) { - if (songPlaying) - sp++; + row = 0; - if (sp >= song.len) - sp = song.repS; + if (songPlaying) + { + songPos++; + if (songPos >= song.songLength) + songPos = song.songLoopStart; - nr = song.songTab[sp]; - pattpos = 0; - pattLen = pattLens[nr]; + pattNum = song.orders[songPos]; + numRows = patternNumRows[pattNum]; + } } - noteData = &patt[nr][(pattpos * MAX_VOICES) + c]; - noteData->ton = 97; + p = &pattern[pattNum][(row * MAX_CHANNELS) + c]; + p->note = NOTE_OFF; if (!recmode) { // increase row (only in edit mode) - if (pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + if (numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); } else { // apply tick delay for note if quantization is disabled if (!config.recQuant && tick > 0) { - noteData->effTyp = 0x0E; - noteData->eff = 0xD0 + (tick & 0x0F); + p->efx = 0x0E; + p->efxData = 0xD0 + (tick & 0x0F); } } @@ -557,50 +555,50 @@ bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode) if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT) return false; // we're not editing, test other keys - if (patt[editor.editPattern] == NULL) + if (pattern[editor.editPattern] == NULL) return true; - tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch]; + note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; if (keyb.leftShiftPressed) { // delete all - memset(note, 0, sizeof (tonTyp)); + p->note = p->instr = p->vol = p->efx = p->efxData = 0; } else if (keyb.leftCtrlPressed) { // delete volume column + effect - note->vol = 0; - note->effTyp = 0; - note->eff = 0; + p->vol = 0; + p->efx = 0; + p->efxData = 0; } else if (keyb.leftAltPressed) { // delete effect - note->effTyp = 0; - note->eff = 0; + p->efx = 0; + p->efxData = 0; } else { if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2) { // delete volume column - note->vol = 0; + p->vol = 0; } else { // delete note + instrument - note->ton = 0; - note->instr = 0; + p->note = 0; + p->instr = 0; } } killPatternIfUnused(editor.editPattern); // increase row (only in edit mode) - const int16_t pattLen = pattLens[editor.editPattern]; - if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + const int16_t numRows = patternNumRows[editor.editPattern]; + if (playMode == PLAYMODE_EDIT && numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -621,19 +619,19 @@ bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode) void writeToMacroSlot(uint8_t slot) { uint16_t writeVol = 0; - uint16_t writeEff = 0; + uint16_t writeEfx = 0; - if (patt[editor.editPattern] != NULL) + if (pattern[editor.editPattern] != NULL) { - tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch]; - writeVol = note->vol; - writeEff = (note->effTyp << 8) | note->eff; + note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; + writeVol = p->vol; + writeEfx = (p->efx << 8) | p->efxData; } if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2) config.volMacro[slot] = writeVol; else - config.comMacro[slot] = writeEff; + config.comMacro[slot] = writeEfx; } void writeFromMacroSlot(uint8_t slot) @@ -643,32 +641,31 @@ void writeFromMacroSlot(uint8_t slot) if (!allocatePattern(editor.editPattern)) return; - - const int16_t pattLen = pattLens[editor.editPattern]; - tonTyp *note = &patt[editor.editPattern][(editor.pattPos * MAX_VOICES) + cursor.ch]; - + + note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; if (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2) { - note->vol = (uint8_t)config.volMacro[slot]; + p->vol = (uint8_t)config.volMacro[slot]; } else { - uint8_t effTyp = (uint8_t)(config.comMacro[slot] >> 8); - if (effTyp > 35) + uint8_t efx = (uint8_t)(config.comMacro[slot] >> 8); + if (efx > 35) { // illegal effect - note->effTyp = 0; - note->eff = 0; + p->efx = 0; + p->efxData = 0; } else { - note->effTyp = effTyp; - note->eff = config.comMacro[slot] & 0xFF; + p->efx = efx; + p->efxData = config.comMacro[slot] & 0xFF; } } - if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); + const int16_t numRows = patternNumRows[editor.editPattern]; + if (playMode == PLAYMODE_EDIT && numRows >= 1) + setPos(-1, (editor.row + editor.editRowSkip) % numRows, true); killPatternIfUnused(editor.editPattern); @@ -681,24 +678,22 @@ void insertPatternNote(void) if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG) return; - const int16_t nr = editor.editPattern; - - tonTyp *pattPtr = patt[nr]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; - const int16_t pattPos = editor.pattPos; - const int16_t pattLen = pattLens[nr]; + const int16_t row = editor.row; + const int16_t numRows = patternNumRows[editor.editPattern]; - if (pattLen > 1) + if (numRows > 1) { - for (int32_t i = pattLen-2; i >= pattPos; i--) - pattPtr[((i + 1) * MAX_VOICES) + cursor.ch] = pattPtr[(i * MAX_VOICES) + cursor.ch]; + for (int32_t i = numRows-2; i >= row; i--) + p[((i+1) * MAX_CHANNELS) + cursor.ch] = p[(i * MAX_CHANNELS) + cursor.ch]; } - memset(&pattPtr[(pattPos * MAX_VOICES) + cursor.ch], 0, sizeof (tonTyp)); + memset(&p[(row * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t)); - killPatternIfUnused(nr); + killPatternIfUnused(editor.editPattern); ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -709,28 +704,26 @@ void insertPatternLine(void) if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG) return; - const int16_t nr = editor.editPattern; + setPatternLen(editor.editPattern, patternNumRows[editor.editPattern] + config.recTrueInsert); // config.recTrueInsert is 0 or 1 - setPatternLen(nr, pattLens[nr] + config.recTrueInsert); // config.recTrueInsert is 0 or 1 - - tonTyp *pattPtr = patt[nr]; - if (pattPtr != NULL) + note_t *p = pattern[editor.editPattern]; + if (p != NULL) { - const int16_t pattPos = editor.pattPos; - const int16_t pattLen = pattLens[nr]; + const int16_t row = editor.row; + const int16_t numRows = patternNumRows[editor.editPattern]; - if (pattLen > 1) + if (numRows > 1) { - for (int32_t i = pattLen-2; i >= pattPos; i--) + for (int32_t i = numRows-2; i >= row; i--) { - for (int32_t j = 0; j < MAX_VOICES; j++) - pattPtr[((i + 1) * MAX_VOICES) + j] = pattPtr[(i * MAX_VOICES) + j]; + for (int32_t j = 0; j < MAX_CHANNELS; j++) + p[((i+1) * MAX_CHANNELS) + j] = p[(i * MAX_CHANNELS) + j]; } } - memset(&pattPtr[pattPos * MAX_VOICES], 0, TRACK_WIDTH); + memset(&p[row * MAX_CHANNELS], 0, TRACK_WIDTH); - killPatternIfUnused(nr); + killPatternIfUnused(editor.editPattern); } ui.updatePatternEditor = true; @@ -742,34 +735,33 @@ void deletePatternNote(void) if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG) return; - const int16_t nr = editor.editPattern; - int16_t pattPos = editor.pattPos; - const int16_t pattLen = pattLens[nr]; + int16_t row = editor.row; + const int16_t numRows = patternNumRows[editor.editPattern]; - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr != NULL) + note_t *p = pattern[editor.editPattern]; + if (p != NULL) { - if (pattPos > 0) + if (row > 0) { - pattPos--; - editor.pattPos = song.pattPos = pattPos; + row--; + editor.row = song.row = row; - for (int32_t i = pattPos; i < pattLen-1; i++) - pattPtr[(i * MAX_VOICES) + cursor.ch] = pattPtr[((i + 1) * MAX_VOICES) + cursor.ch]; + for (int32_t i = row; i < numRows-1; i++) + p[(i * MAX_CHANNELS) + cursor.ch] = p[((i+1) * MAX_CHANNELS) + cursor.ch]; - memset(&pattPtr[((pattLen - 1) * MAX_VOICES) + cursor.ch], 0, sizeof (tonTyp)); + memset(&p[((numRows-1) * MAX_CHANNELS) + cursor.ch], 0, sizeof (note_t)); } } else { - if (pattPos > 0) + if (row > 0) { - pattPos--; - editor.pattPos = song.pattPos = pattPos; + row--; + editor.row = song.row = row; } } - killPatternIfUnused(nr); + killPatternIfUnused(editor.editPattern); ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -780,40 +772,39 @@ void deletePatternLine(void) if (playMode != PLAYMODE_EDIT && playMode != PLAYMODE_RECPATT && playMode != PLAYMODE_RECSONG) return; - const int16_t nr = editor.editPattern; - int16_t pattPos = editor.pattPos; - const int16_t pattLen = pattLens[nr]; + int16_t row = editor.row; + const int16_t numRows = patternNumRows[editor.editPattern]; - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr != NULL) + note_t *p = pattern[editor.editPattern]; + if (p != NULL) { - if (pattPos > 0) + if (row > 0) { - pattPos--; - editor.pattPos = song.pattPos = pattPos; + row--; + editor.row = song.row = row; - for (int32_t i = pattPos; i < pattLen-1; i++) + for (int32_t i = row; i < numRows-1; i++) { - for (int32_t j = 0; j < MAX_VOICES; j++) - pattPtr[(i * MAX_VOICES) + j] = pattPtr[((i + 1) * MAX_VOICES) + j]; + for (int32_t j = 0; j < MAX_CHANNELS; j++) + p[(i * MAX_CHANNELS) + j] = p[((i+1) * MAX_CHANNELS) + j]; } - memset(&pattPtr[(pattLen - 1) * MAX_VOICES], 0, TRACK_WIDTH); + memset(&p[(numRows-1) * MAX_CHANNELS], 0, TRACK_WIDTH); } } else { - if (pattPos > 0) + if (row > 0) { - pattPos--; - editor.pattPos = song.pattPos = pattPos; + row--; + editor.row = song.row = row; } } - if (config.recTrueInsert && pattLen > 1) - setPatternLen(nr, pattLen - 1); + if (config.recTrueInsert && numRows > 1) + setPatternLen(editor.editPattern, numRows-1); - killPatternIfUnused(nr); + killPatternIfUnused(editor.editPattern); ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -823,86 +814,72 @@ void deletePatternLine(void) static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_t addVal) { - uint8_t ton; - uint16_t p, pattLen, ch, row; - tonTyp *pattPtr; - transpDelNotes = 0; switch (transpMode) { case TRANSP_TRACK: { - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattPtr += cursor.ch; + p += cursor.ch; - pattLen = pattLens[editor.editPattern]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[editor.editPattern]; + for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr)) + if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr)) { - if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0) + if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0) transpDelNotes++; } - - pattPtr += MAX_VOICES; } } break; case TRANSP_PATT: { - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattLen = pattLens[editor.editPattern]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[editor.editPattern]; + const int32_t pitch = MAX_CHANNELS-song.numChannels; + + for (int32_t row = 0; row < numRows; row++, p += pitch) { - for (ch = 0; ch < song.antChn; ch++) + for (int32_t ch = 0; ch < song.numChannels; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr)) + if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr)) { - if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0) + if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0) transpDelNotes++; } - - pattPtr++; } - - pattPtr += MAX_VOICES - song.antChn; } } break; case TRANSP_SONG: { - for (p = 0; p < MAX_PATTERNS; p++) + const int32_t pitch = MAX_CHANNELS-song.numChannels; + for (int32_t i = 0; i < MAX_PATTERNS; i++) { - pattPtr = patt[p]; - if (pattPtr == NULL) + note_t *p = pattern[i]; + if (p == NULL) continue; // empty pattern - pattLen = pattLens[p]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[i]; + for (int32_t row = 0; row < numRows; row++, p += pitch) { - for (ch = 0; ch < song.antChn; ch++) + for (int32_t ch = 0; ch < song.numChannels; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr)) + if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr)) { - if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0) + if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0) transpDelNotes++; } - - pattPtr++; } - - pattPtr += MAX_VOICES - song.antChn; } } } @@ -913,28 +890,23 @@ static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_ if (pattMark.markY1 == pattMark.markY2) return; // no pattern marking - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattPtr += (pattMark.markY1 * MAX_VOICES) + pattMark.markX1; + p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1; - pattLen = pattLens[editor.editPattern]; - for (row = pattMark.markY1; row < pattMark.markY2; row++) + const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1); + for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch) { - for (ch = pattMark.markX1; ch <= pattMark.markX2; ch++) + for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!currInsOnly || pattPtr->instr == editor.curInstr)) + if ((p->note >= 1 && p->note <= 96) && (!currInsOnly || p->instr == editor.curInstr)) { - if ((int8_t)ton+addVal > 96 || (int8_t)ton+addVal <= 0) + if ((int8_t)p->note+addVal > 96 || (int8_t)p->note+addVal <= 0) transpDelNotes++; } - - pattPtr++; } - - pattPtr += MAX_VOICES - ((pattMark.markX2 + 1) - pattMark.markX1); } } break; @@ -946,9 +918,6 @@ static void countOverflowingNotes(uint8_t currInsOnly, uint8_t transpMode, int8_ void doTranspose(void) { char text[48]; - uint8_t ton; - uint16_t p, pattLen, ch, row; - tonTyp *pattPtr; countOverflowingNotes(lastInsMode, lastTranspMode, lastTranspVal); if (transpDelNotes > 0) @@ -963,86 +932,79 @@ void doTranspose(void) { case TRANSP_TRACK: { - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattPtr += cursor.ch; + p += cursor.ch; - pattLen = pattLens[editor.editPattern]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[editor.editPattern]; + for (int32_t row = 0; row < numRows; row++, p += MAX_CHANNELS) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr)) + uint8_t note = p->note; + if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr)) { - ton += lastTranspVal; - if (ton > 96) - ton = 0; // also handles underflow + note += lastTranspVal; + if (note > 96) + note = 0; // also handles underflow - pattPtr->ton = ton; + p->note = note; } - - pattPtr += MAX_VOICES; } } break; case TRANSP_PATT: { - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattLen = pattLens[editor.editPattern]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[editor.editPattern]; + const int32_t pitch = MAX_CHANNELS - song.numChannels; + + for (int32_t row = 0; row < numRows; row++, p += pitch) { - for (ch = 0; ch < song.antChn; ch++) + for (int32_t ch = 0; ch < song.numChannels; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr)) + uint8_t note = p->note; + if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr)) { - ton += lastTranspVal; - if (ton > 96) - ton = 0; // also handles underflow + note += lastTranspVal; + if (note > 96) + note = 0; // also handles underflow - pattPtr->ton = ton; + p->note = note; } - - pattPtr++; } - - pattPtr += MAX_VOICES - song.antChn; } } break; case TRANSP_SONG: { - for (p = 0; p < MAX_PATTERNS; p++) + const int32_t pitch = MAX_CHANNELS - song.numChannels; + for (int32_t i = 0; i < MAX_PATTERNS; i++) { - pattPtr = patt[p]; - if (pattPtr == NULL) + note_t *p = pattern[i]; + if (p == NULL) continue; // empty pattern - pattLen = pattLens[p]; - for (row = 0; row < pattLen; row++) + const int32_t numRows = patternNumRows[i]; + for (int32_t row = 0; row < numRows; row++, p += pitch) { - for (ch = 0; ch < song.antChn; ch++) + for (int32_t ch = 0; ch < song.numChannels; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr)) + uint8_t note = p->note; + if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr)) { - ton += lastTranspVal; - if (ton > 96) - ton = 0; // also handles underflow + note += lastTranspVal; + if (note > 96) + note = 0; // also handles underflow - pattPtr->ton = ton; + p->note = note; } - - pattPtr++; } - - pattPtr += MAX_VOICES - song.antChn; } } } @@ -1053,31 +1015,27 @@ void doTranspose(void) if (pattMark.markY1 == pattMark.markY2) return; // no pattern marking - pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; // empty pattern - pattPtr += (pattMark.markY1 * MAX_VOICES) + pattMark.markX1; + p += (pattMark.markY1 * MAX_CHANNELS) + pattMark.markX1; - pattLen = pattLens[editor.editPattern]; - for (row = pattMark.markY1; row < pattMark.markY2; row++) + const int32_t pitch = MAX_CHANNELS - ((pattMark.markX2 + 1) - pattMark.markX1); + for (int32_t row = pattMark.markY1; row < pattMark.markY2; row++, p += pitch) { - for (ch = pattMark.markX1; ch <= pattMark.markX2; ch++) + for (int32_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++, p++) { - ton = pattPtr->ton; - if ((ton >= 1 && ton <= 96) && (!lastInsMode || pattPtr->instr == editor.curInstr)) + uint8_t note = p->note; + if ((note >= 1 && note <= 96) && (!lastInsMode || p->instr == editor.curInstr)) { - ton += lastTranspVal; - if (ton > 96) - ton = 0; // also handles underflow + note += lastTranspVal; + if (note > 96) + note = 0; // also handles underflow - pattPtr->ton = ton; + p->note = note; } - - pattPtr++; } - - pattPtr += MAX_VOICES - ((pattMark.markX2 + 1) - pattMark.markX1); } } break; @@ -1345,15 +1303,15 @@ void blockTranspAllIns12Dn(void) doTranspose(); } -void copyNote(tonTyp *src, tonTyp *dst) +void copyNote(note_t *src, note_t *dst) { if (editor.copyMaskEnable) { - if (editor.copyMask[0]) dst->ton = src->ton; + if (editor.copyMask[0]) dst->note = src->note; if (editor.copyMask[1]) dst->instr = src->instr; if (editor.copyMask[2]) dst->vol = src->vol; - if (editor.copyMask[3]) dst->effTyp = src->effTyp; - if (editor.copyMask[4]) dst->eff = src->eff; + if (editor.copyMask[3]) dst->efx = src->efx; + if (editor.copyMask[4]) dst->efxData = src->efxData; } else { @@ -1361,15 +1319,15 @@ void copyNote(tonTyp *src, tonTyp *dst) } } -void pasteNote(tonTyp *src, tonTyp *dst) +void pasteNote(note_t *src, note_t *dst) { if (editor.copyMaskEnable) { - if (editor.copyMask[0] && (src->ton != 0 || !editor.transpMask[0])) dst->ton = src->ton; - if (editor.copyMask[1] && (src->instr != 0 || !editor.transpMask[1])) dst->instr = src->instr; - if (editor.copyMask[2] && (src->vol != 0 || !editor.transpMask[2])) dst->vol = src->vol; - if (editor.copyMask[3] && (src->effTyp != 0 || !editor.transpMask[3])) dst->effTyp = src->effTyp; - if (editor.copyMask[4] && (src->eff != 0 || !editor.transpMask[4])) dst->eff = src->eff; + if (editor.copyMask[0] && (src->note != 0 || !editor.transpMask[0])) dst->note = src->note; + if (editor.copyMask[1] && (src->instr != 0 || !editor.transpMask[1])) dst->instr = src->instr; + if (editor.copyMask[2] && (src->vol != 0 || !editor.transpMask[2])) dst->vol = src->vol; + if (editor.copyMask[3] && (src->efx != 0 || !editor.transpMask[3])) dst->efx = src->efx; + if (editor.copyMask[4] && (src->efxData != 0 || !editor.transpMask[4])) dst->efxData = src->efxData; } else { @@ -1379,24 +1337,24 @@ void pasteNote(tonTyp *src, tonTyp *dst) void cutTrack(void) { - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; - const int16_t pattLen = pattLens[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; if (config.ptnCutToBuffer) { - memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (tonTyp)); - for (int16_t i = 0; i < pattLen; i++) - copyNote(&pattPtr[(i * MAX_VOICES) + cursor.ch], &trackCopyBuff[i]); + memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t)); + for (int16_t i = 0; i < numRows; i++) + copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]); - trkBufLen = pattLen; + trkBufLen = numRows; } pauseMusic(); - for (int16_t i = 0; i < pattLen; i++) - pasteNote(&clearNote, &pattPtr[(i * MAX_VOICES) + cursor.ch]); + for (int16_t i = 0; i < numRows; i++) + pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + cursor.ch]); resumeMusic(); killPatternIfUnused(editor.editPattern); @@ -1407,17 +1365,17 @@ void cutTrack(void) void copyTrack(void) { - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; - const int16_t pattLen = pattLens[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; - memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (tonTyp)); - for (int16_t i = 0; i < pattLen; i++) - copyNote(&pattPtr[(i * MAX_VOICES) + cursor.ch], &trackCopyBuff[i]); + memset(trackCopyBuff, 0, MAX_PATT_LEN * sizeof (note_t)); + for (int16_t i = 0; i < numRows; i++) + copyNote(&p[(i * MAX_CHANNELS) + cursor.ch], &trackCopyBuff[i]); - trkBufLen = pattLen; + trkBufLen = numRows; } void pasteTrack(void) @@ -1425,12 +1383,12 @@ void pasteTrack(void) if (trkBufLen == 0 || !allocatePattern(editor.editPattern)) return; - tonTyp *pattPtr = patt[editor.editPattern]; - const int16_t pattLen = pattLens[editor.editPattern]; + note_t *p = pattern[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; pauseMusic(); - for (int16_t i = 0; i < pattLen; i++) - pasteNote(&trackCopyBuff[i], &pattPtr[(i * MAX_VOICES) + cursor.ch]); + for (int16_t i = 0; i < numRows; i++) + pasteNote(&trackCopyBuff[i], &p[(i * MAX_CHANNELS) + cursor.ch]); resumeMusic(); killPatternIfUnused(editor.editPattern); @@ -1441,29 +1399,29 @@ void pasteTrack(void) void cutPattern(void) { - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; - const int16_t pattLen = pattLens[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; if (config.ptnCutToBuffer) { - memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_VOICES) * sizeof (tonTyp)); - for (int16_t x = 0; x < song.antChn; x++) + memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t)); + for (int16_t x = 0; x < song.numChannels; x++) { - for (int16_t i = 0; i < pattLen; i++) - copyNote(&pattPtr[(i * MAX_VOICES) + x], &ptnCopyBuff[(i * MAX_VOICES) + x]); + for (int16_t i = 0; i < numRows; i++) + copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]); } - ptnBufLen = pattLen; + ptnBufLen = numRows; } pauseMusic(); - for (int16_t x = 0; x < song.antChn; x++) + for (int16_t x = 0; x < song.numChannels; x++) { - for (int16_t i = 0; i < pattLen; i++) - pasteNote(&clearNote, &pattPtr[(i * MAX_VOICES) + x]); + for (int16_t i = 0; i < numRows; i++) + pasteNote(&clearNote, &p[(i * MAX_CHANNELS) + x]); } resumeMusic(); @@ -1475,20 +1433,20 @@ void cutPattern(void) void copyPattern(void) { - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; - const int16_t pattLen = pattLens[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; - memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_VOICES) * sizeof (tonTyp)); - for (int16_t x = 0; x < song.antChn; x++) + memset(ptnCopyBuff, 0, (MAX_PATT_LEN * MAX_CHANNELS) * sizeof (note_t)); + for (int16_t x = 0; x < song.numChannels; x++) { - for (int16_t i = 0; i < pattLen; i++) - copyNote(&pattPtr[(i * MAX_VOICES) + x], &ptnCopyBuff[(i * MAX_VOICES) + x]); + for (int16_t i = 0; i < numRows; i++) + copyNote(&p[(i * MAX_CHANNELS) + x], &ptnCopyBuff[(i * MAX_CHANNELS) + x]); } - ptnBufLen = pattLen; + ptnBufLen = numRows; ui.updatePatternEditor = true; } @@ -1498,7 +1456,7 @@ void pastePattern(void) if (ptnBufLen == 0) return; - if (pattLens[editor.editPattern] != ptnBufLen) + if (patternNumRows[editor.editPattern] != ptnBufLen) { if (okBox(1, "System request", "Change pattern length to copybuffer's length?") == 1) setPatternLen(editor.editPattern, ptnBufLen); @@ -1507,14 +1465,14 @@ void pastePattern(void) if (!allocatePattern(editor.editPattern)) return; - tonTyp *pattPtr = patt[editor.editPattern]; - const int16_t pattLen = pattLens[editor.editPattern]; + note_t *p = pattern[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; pauseMusic(); - for (int16_t x = 0; x < song.antChn; x++) + for (int16_t x = 0; x < song.numChannels; x++) { - for (int16_t i = 0; i < pattLen; i++) - pasteNote(&ptnCopyBuff[(i * MAX_VOICES) + x], &pattPtr[(i * MAX_VOICES) + x]); + for (int16_t i = 0; i < numRows; i++) + pasteNote(&ptnCopyBuff[(i * MAX_CHANNELS) + x], &p[(i * MAX_CHANNELS) + x]); } resumeMusic(); @@ -1529,8 +1487,8 @@ void cutBlock(void) if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2) return; - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; if (config.ptnCutToBuffer) @@ -1539,9 +1497,8 @@ void cutBlock(void) { for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++) { - assert(x < song.antChn && y < pattLens[editor.editPattern]); - copyNote(&pattPtr[(y * MAX_VOICES) + x], - &blkCopyBuff[((y - pattMark.markY1) * MAX_VOICES) + (x - pattMark.markX1)]); + assert(x < song.numChannels && y < patternNumRows[editor.editPattern]); + copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]); } } } @@ -1550,7 +1507,7 @@ void cutBlock(void) for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++) { for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++) - pasteNote(&clearNote, &pattPtr[(y * MAX_VOICES) + x]); + pasteNote(&clearNote, &p[(y * MAX_CHANNELS) + x]); } resumeMusic(); @@ -1569,17 +1526,16 @@ void copyBlock(void) if (pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2) return; - tonTyp *pattPtr = patt[editor.editPattern]; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) return; for (int16_t x = pattMark.markX1; x <= pattMark.markX2; x++) { for (int16_t y = pattMark.markY1; y < pattMark.markY2; y++) { - assert(x < song.antChn && y < pattLens[editor.editPattern]); - copyNote(&pattPtr[(y * MAX_VOICES) + x], - &blkCopyBuff[((y - pattMark.markY1) * MAX_VOICES) + (x - pattMark.markX1)]); + assert(x < song.numChannels && y < patternNumRows[editor.editPattern]); + copyNote(&p[(y * MAX_CHANNELS) + x], &blkCopyBuff[((y - pattMark.markY1) * MAX_CHANNELS) + (x - pattMark.markX1)]); } } @@ -1593,28 +1549,28 @@ void pasteBlock(void) if (!blockCopied || !allocatePattern(editor.editPattern)) return; - const int16_t pattLen = pattLens[editor.editPattern]; + const int16_t numRows = patternNumRows[editor.editPattern]; const int32_t xpos = cursor.ch; - const int32_t ypos = editor.pattPos; + const int32_t ypos = editor.row; int32_t j = markXSize; - if (j+xpos >= song.antChn) - j = song.antChn - xpos - 1; + if (j+xpos >= song.numChannels) + j = song.numChannels - xpos - 1; int32_t k = markYSize; - if (k+ypos >= pattLen) - k = pattLen - ypos; + if (k+ypos >= numRows) + k = numRows-ypos; - tonTyp *pattPtr = patt[editor.editPattern]; + note_t *p = pattern[editor.editPattern]; pauseMusic(); for (int32_t x = xpos; x <= xpos+j; x++) { for (int32_t y = ypos; y < ypos+k; y++) { - assert(x < song.antChn && y < pattLen); - pasteNote(&blkCopyBuff[((y - ypos) * MAX_VOICES) + (x - xpos)], &pattPtr[(y * MAX_VOICES) + x]); + assert(x < song.numChannels && y < numRows); + pasteNote(&blkCopyBuff[((y - ypos) * MAX_CHANNELS) + (x - xpos)], &p[(y * MAX_CHANNELS) + x]); } } resumeMusic(); @@ -1625,29 +1581,24 @@ void pasteBlock(void) setSongModifiedFlag(); } -static void remapInstrXY(uint16_t nr, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst) +static void remapInstrXY(uint16_t pattNum, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint8_t src, uint8_t dst) { // this routine is only used sanely, so no need to check input - tonTyp *pattPtr = patt[nr]; + note_t *pattPtr = pattern[pattNum]; if (pattPtr == NULL) return; - const int32_t noteSkipLen = MAX_VOICES - ((x2 + 1) - x1); - tonTyp *note = &pattPtr[(y1 * MAX_VOICES) + x1]; + note_t *p = &pattPtr[(y1 * MAX_CHANNELS) + x1]; - for (uint16_t y = y1; y <= y2; y++) + const int32_t pitch = MAX_CHANNELS - ((x2 + 1) - x1); + for (uint16_t y = y1; y <= y2; y++, p += pitch) { - for (uint16_t x = x1; x <= x2; x++) + for (uint16_t x = x1; x <= x2; x++, p++) { - assert(x < song.antChn && y < pattLens[nr]); - if (note->instr == src) - note->instr = dst; - - note++; + if (p->instr == src) + p->instr = dst; } - - note += noteSkipLen; } } @@ -1675,7 +1626,7 @@ void remapTrack(void) pauseMusic(); remapInstrXY(editor.editPattern, cursor.ch, 0, - cursor.ch, pattLens[editor.editPattern] - 1, + cursor.ch, patternNumRows[editor.editPattern] - 1, editor.srcInstr, editor.curInstr); resumeMusic(); @@ -1691,7 +1642,7 @@ void remapPattern(void) pauseMusic(); remapInstrXY(editor.editPattern, 0, 0, - (uint16_t)(song.antChn - 1), pattLens[editor.editPattern] - 1, + (uint16_t)(song.numChannels - 1), patternNumRows[editor.editPattern] - 1, editor.srcInstr, editor.curInstr); resumeMusic(); @@ -1707,11 +1658,11 @@ void remapSong(void) pauseMusic(); for (int32_t i = 0; i < MAX_PATTERNS; i++) { - const uint8_t pattNr = (uint8_t)i; + const uint8_t pattNum = (uint8_t)i; - remapInstrXY(pattNr, + remapInstrXY(pattNum, 0, 0, - (uint16_t)(song.antChn - 1), pattLens[pattNr] - 1, + (uint16_t)(song.numChannels - 1), patternNumRows[pattNum] - 1, editor.srcInstr, editor.curInstr); } resumeMusic(); @@ -1722,22 +1673,22 @@ void remapSong(void) // "scale-fade volume" routines -static int8_t getNoteVolume(tonTyp *note) +static int8_t getNoteVolume(note_t *p) { int8_t nv, vv, ev; - if (note->vol >= 0x10 && note->vol <= 0x50) - vv = note->vol - 0x10; + if (p->vol >= 0x10 && p->vol <= 0x50) + vv = p->vol - 0x10; else vv = -1; - if (note->effTyp == 0xC) - ev = MIN(note->eff, 64); + if (p->efx == 0xC) + ev = MIN(p->efxData, 64); else ev = -1; - if (note->instr != 0 && instr[note->instr] != NULL) - nv = (int8_t)instr[note->instr]->samp[0].vol; + if (p->instr != 0 && instr[p->instr] != NULL) + nv = (int8_t)instr[p->instr]->smp[0].volume; else nv = -1; @@ -1749,38 +1700,38 @@ static int8_t getNoteVolume(tonTyp *note) return finalv; } -static void setNoteVolume(tonTyp *note, int8_t newVol) +static void setNoteVolume(note_t *p, int8_t newVol) { if (newVol < 0) return; - const int8_t oldv = getNoteVolume(note); - if (note->vol == oldv) + const int8_t oldv = getNoteVolume(p); + if (p->vol == oldv) return; // volume is the same - if (note->effTyp == 0x0C) - note->eff = newVol; // Cxx effect + if (p->efx == 0x0C) + p->efxData = newVol; // Cxx effect else - note->vol = 0x10 + newVol; // volume column + p->vol = 0x10 + newVol; // volume column } -static void scaleNote(uint16_t ptn, int8_t ch, int16_t row, double dScale) +static void scaleNote(uint16_t pattNum, int8_t ch, int16_t row, double dScale) { - if (patt[ptn] == NULL) + if (pattern[pattNum] == NULL) return; - const int16_t pattLen = pattLens[ptn]; - if (row < 0 || row >= pattLen || ch < 0 || ch >= song.antChn) + const int16_t numRows = patternNumRows[pattNum]; + if (row < 0 || row >= numRows || ch < 0 || ch >= song.numChannels) return; - tonTyp *note = &patt[ptn][(row * MAX_VOICES) + ch]; + note_t *p = &pattern[pattNum][(row * MAX_CHANNELS) + ch]; - int32_t vol = getNoteVolume(note); + int32_t vol = getNoteVolume(p); if (vol >= 0) { vol = (int32_t)((vol * dScale) + 0.5); // rounded vol = MIN(MAX(0, vol), 64); - setNoteVolume(note, (int8_t)vol); + setNoteVolume(p, (int8_t)vol); } } @@ -1808,7 +1759,7 @@ static bool askForScaleFade(char *msg) return false; } - dVolScaleFK1 = atof(val1); + dVolScaleFK1 = atof(val1+0); dVolScaleFK2 = atof(val2+1); return true; @@ -1819,22 +1770,22 @@ void scaleFadeVolumeTrack(void) if (!askForScaleFade("Volume scale-fade track (start-, end scale)")) return; - if (patt[editor.editPattern] == NULL) + if (pattern[editor.editPattern] == NULL) return; - const int32_t pattLen = pattLens[editor.editPattern]; + const int32_t numRows = patternNumRows[editor.editPattern]; - double dIPy = 0.0; - if (pattLen > 0) - dIPy = (dVolScaleFK2 - dVolScaleFK1) / pattLen; + double dVolDelta = 0.0; + if (numRows > 0) + dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows; double dVol = dVolScaleFK1; pauseMusic(); - for (int16_t row = 0; row < pattLen; row++) + for (int16_t row = 0; row < numRows; row++) { scaleNote(editor.editPattern, cursor.ch, row, dVol); - dVol += dIPy; + dVol += dVolDelta; } resumeMusic(); } @@ -1844,24 +1795,24 @@ void scaleFadeVolumePattern(void) if (!askForScaleFade("Volume scale-fade pattern (start-, end scale)")) return; - if (patt[editor.editPattern] == NULL) + if (pattern[editor.editPattern] == NULL) return; - const int32_t pattLen = pattLens[editor.editPattern]; + const int32_t numRows = patternNumRows[editor.editPattern]; - double dIPy = 0.0; - if (pattLen > 0) - dIPy = (dVolScaleFK2 - dVolScaleFK1) / pattLen; + double dVolDelta = 0.0; + if (numRows > 0) + dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows; double dVol = dVolScaleFK1; pauseMusic(); - for (int16_t row = 0; row < pattLen; row++) + for (int16_t row = 0; row < numRows; row++) { - for (int8_t ch = 0; ch < song.antChn; ch++) + for (int8_t ch = 0; ch < song.numChannels; ch++) scaleNote(editor.editPattern, ch, row, dVol); - dVol += dIPy; + dVol += dVolDelta; } resumeMusic(); } @@ -1871,14 +1822,14 @@ void scaleFadeVolumeBlock(void) if (!askForScaleFade("Volume scale-fade block (start-, end scale)")) return; - if (patt[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2) + if (pattern[editor.editPattern] == NULL || pattMark.markY1 == pattMark.markY2 || pattMark.markY1 > pattMark.markY2) return; - const int32_t dy = pattMark.markY2 - pattMark.markY1; + const int32_t numRows = pattMark.markY2 - pattMark.markY1; - double dIPy = 0.0; - if (dy > 0) - dIPy = (dVolScaleFK2 - dVolScaleFK1) / dy; + double dVolDelta = 0.0; + if (numRows > 0) + dVolDelta = (dVolScaleFK2 - dVolScaleFK1) / numRows; double dVol = dVolScaleFK1; @@ -1888,7 +1839,7 @@ void scaleFadeVolumeBlock(void) for (int16_t ch = pattMark.markX1; ch <= pattMark.markX2; ch++) scaleNote(editor.editPattern, (uint8_t)ch, row, dVol); - dVol += dIPy; + dVol += dVolDelta; } resumeMusic(); } diff --git a/src/ft2_edit.h b/src/ft2_edit.h @@ -4,7 +4,7 @@ #include <SDL2/SDL.h> bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode); -void recordNote(uint8_t note, int8_t vol); +void recordNote(uint8_t noteNum, int8_t vol); void testNoteKeysRelease(SDL_Scancode scancode); void writeToMacroSlot(uint8_t slot); void writeFromMacroSlot(uint8_t slot); diff --git a/src/ft2_events.c b/src/ft2_events.c @@ -33,10 +33,10 @@ #include "ft2_sample_ed_features.h" #include "ft2_structs.h" -#define CRASH_TEXT "Oh no!\nThe Fasttracker II clone has crashed...\n\nA backup .xm was hopefully " \ +#define CRASH_TEXT "Oh no! The Fasttracker II clone has crashed...\nA backup .xm was hopefully " \ "saved to the current module directory.\n\nPlease report this bug if you can.\n" \ "Try to mention what you did before the crash happened.\n" \ - "My email can be found at the bottom of 16-bits.org." + "My email can be found at the bottom of www.16-bits.org." static bool backupMadeAfterCrash; @@ -100,7 +100,7 @@ void handleThreadEvents(void) { if (okBoxData.active) { - okBoxData.returnData = okBox(okBoxData.typ, okBoxData.headline, okBoxData.text); + okBoxData.returnData = okBox(okBoxData.type, okBoxData.headline, okBoxData.text); okBoxData.active = false; } } diff --git a/src/ft2_gui.c b/src/ft2_gui.c @@ -12,7 +12,7 @@ #include "ft2_nibbles.h" #include "ft2_gui.h" #include "ft2_pattern_ed.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_help.h" #include "ft2_sample_ed.h" #include "ft2_inst_ed.h" @@ -203,7 +203,7 @@ bool setupGUI(void) s->thumbH = 0; } - setPal16(palTable[config.cfg_StdPalNr], false); + setPal16(palTable[config.cfg_StdPalNum], false); seedAboutScreenRandom((uint32_t)time(NULL)); setupInitialTextBoxPointers(); setInitialTrimFlags(); @@ -1141,7 +1141,7 @@ void showTopLeftMainScreen(bool restoreScreens) textOutShadow(4, 64, PAL_FORGRND, PAL_DSKTOP2, "Repstart"); drawPosEdNums(song.songPos); drawSongLength(); - drawSongRepS(); + drawSongLoopStart(); // logo button showPushButton(PB_LOGO); @@ -1181,8 +1181,8 @@ void showTopLeftMainScreen(bool restoreScreens) textOutShadow(116, 64, PAL_FORGRND, PAL_DSKTOP2, "Add."); textOutShadow(210, 36, PAL_FORGRND, PAL_DSKTOP2, "Ptn."); textOutShadow(210, 50, PAL_FORGRND, PAL_DSKTOP2, "Ln."); - drawSongBPM(song.speed); - drawSongSpeed(song.tempo); + drawSongBPM(song.BPM); + drawSongSpeed(song.speed); drawEditPattern(editor.editPattern); drawPatternLength(editor.editPattern); drawIDAdd(); @@ -1190,7 +1190,7 @@ void showTopLeftMainScreen(bool restoreScreens) // status bar drawFramework(0, 77, 291, 15, FRAMEWORK_TYPE1); textOutShadow(4, 80, PAL_FORGRND, PAL_DSKTOP2, "Global volume"); - drawGlobalVol(song.globVol); + drawGlobalVol(song.globalVolume); ui.updatePosSections = true; diff --git a/src/ft2_header.h b/src/ft2_header.h @@ -8,33 +8,37 @@ #define WIN32_MEAN_AND_LEAN #include <windows.h> #else -#include <limits.h> // PATH_MAX +#include <limits.h> // also has PATH_MAX #endif #include "ft2_replayer.h" -#define PROG_VER_STR "1.46" +#define PROG_VER_STR "1.47" // do NOT change these! It will only mess things up... +#define FT2_VBLANK_HZ 70 +#define SCREEN_W 632 +#define SCREEN_H 400 + /* "60Hz" ranges everywhere from 59..61Hz depending on the monitor, so with ** no vsync we will get stuttering because the rate is not perfect... */ #define VBLANK_HZ 60 +// 70Hz (FT2 vblank) delta -> 60Hz vblank delta (rounded) +#define SCALE_VBLANK_DELTA(x) (int32_t)(((x) * ((double)VBLANK_HZ / FT2_VBLANK_HZ)) + 0.5) + // scopes must be clocked slightly higher than the nominal vblank rate #define SCOPE_HZ 64 -#define FT2_VBLANK_HZ 70 -#define SCREEN_W 632 -#define SCREEN_H 400 /* Amount of extra bytes to allocate for every instrument sample, ** this is used for a hack for resampling interpolation to be ** branchless in the inner channel mixer loop. ** Warning: Do not change this! */ -#define LOOP_FIX_LEN 32 -#define SMP_DAT_OFFSET 8 +#define SMP_DAT_OFFSET 32 +#define SAMPLE_PAD_LENGTH (SMP_DAT_OFFSET+32) #ifndef _WIN32 #define _stricmp strcasecmp @@ -51,6 +55,14 @@ #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#define DROUND(x) \ + if (x < 0.0) x -= 0.5; \ + else if (x > 0.0) x += 0.5 + +#define FROUND(x) \ + if (x < 0.0f) x -= 0.5f; \ + else if (x > 0.0f) x += 0.5f + // fast 32-bit -> 8-bit clamp #define CLAMP8(i) if ((int8_t)(i) != i) i = 0x7F ^ (i >> 31) @@ -60,16 +72,33 @@ #define ALIGN_PTR(p, x) (((uintptr_t)(p) + ((x)-1)) & ~((x)-1)) #define MALLOC_PAD(size, pad) (malloc((size) + (pad))) -#define SWAP16(value) \ +#define SWAP16(x) \ ( \ - (((uint16_t)((value) & 0x00FF)) << 8) | \ - (((uint16_t)((value) & 0xFF00)) >> 8) \ + (((uint16_t)((x) & 0x00FF)) << 8) | \ + (((uint16_t)((x) & 0xFF00)) >> 8) \ ) -#define SWAP32(value) \ +#define SWAP32(x) \ ( \ - (((uint32_t)((value) & 0x000000FF)) << 24) | \ - (((uint32_t)((value) & 0x0000FF00)) << 8) | \ - (((uint32_t)((value) & 0x00FF0000)) >> 8) | \ - (((uint32_t)((value) & 0xFF000000)) >> 24) \ + (((uint32_t)((x) & 0x000000FF)) << 24) | \ + (((uint32_t)((x) & 0x0000FF00)) << 8) | \ + (((uint32_t)((x) & 0x00FF0000)) >> 8) | \ + (((uint32_t)((x) & 0xFF000000)) >> 24) \ ) + +#define SWAP64(x) \ +( \ + (((x) << 56) & 0xFF00000000000000ULL) | \ + (((x) << 40) & 0x00FF000000000000ULL) | \ + (((x) << 24) & 0x0000FF0000000000ULL) | \ + (((x) << 8) & 0x000000FF00000000ULL) | \ + (((x) >> 8) & 0x00000000FF000000ULL) | \ + (((x) >> 24) & 0x0000000000FF0000ULL) | \ + (((x) >> 40) & 0x000000000000FF00ULL) | \ + (((x) >> 56) & 0x00000000000000FFULL) \ +) + +typedef struct smpPtr_t +{ + int8_t *origPtr, *ptr; +} smpPtr_t; diff --git a/src/ft2_help.c b/src/ft2_help.c @@ -26,10 +26,10 @@ typedef struct #define MAX_HELP_LINES 768 #define HELP_SIZE sizeof (helpRec) #define MAX_SUBJ 10 -#define HELP_KOL 135 -#define HELP_WIDTH (596 - HELP_KOL) +#define HELP_COLUMN 135 +#define HELP_WIDTH (596 - HELP_COLUMN) -static uint8_t fHlp_Nr; +static uint8_t fHlp_Num; static int16_t textLine, fHlp_Line, subjLen[MAX_SUBJ]; static int32_t helpBufferPos; static helpRec *subjPtrArr[MAX_SUBJ]; @@ -101,7 +101,7 @@ static char *rtrim(char *s) return s; } -static void readHelp(void) // this is really messy, directly ported from Pascal code... +static void readHelp(void) // this is a bit messy... { char text[256], text2[256], *s, *sEnd, *s3; int16_t a, b, i, k; @@ -122,7 +122,7 @@ static void readHelp(void) // this is really messy, directly ported from Pascal for (int16_t subj = 0; subj < MAX_SUBJ; subj++) { textLine = 0; - int16_t currKol = 0; + int16_t currColumn = 0; uint8_t currColor = PAL_FORGRND; getLine(text); s = text; @@ -139,12 +139,12 @@ static void readHelp(void) // this is really messy, directly ported from Pascal if (*(uint16_t *)s == 0x4C40) // @L - "big font" { - addText(&tempText[textLine], currKol, currColor, s2); + addText(&tempText[textLine], currColumn, currColor, s2); s += 2; if (*(uint16_t *)s == 0x5840) // @X - "change X position" { - currKol = controlCodeToNum(&s[2]); + currColumn = controlCodeToNum(&s[2]); s += 5; } @@ -156,7 +156,7 @@ static void readHelp(void) // this is really messy, directly ported from Pascal } helpRec *t = &tempText[textLine]; - t->xPos = currKol; + t->xPos = currColumn; t->color = currColor; t->bigFont = true; t->noLine = false; @@ -171,13 +171,13 @@ static void readHelp(void) // this is really messy, directly ported from Pascal { if (*s == '>') { - addText(&tempText[textLine], currKol, currColor, s2); + addText(&tempText[textLine], currColumn, currColor, s2); s++; } if (*(uint16_t *)s == 0x5840) // @X - "set X position (relative to help X start)" { - currKol = controlCodeToNum(&s[2]); + currColumn = controlCodeToNum(&s[2]); s += 5; } @@ -191,9 +191,9 @@ static void readHelp(void) // this is really messy, directly ported from Pascal s = ltrim(rtrim(s)); if (*s == '\0') { - addText(&tempText[textLine], currKol, currColor, s2); + addText(&tempText[textLine], currColumn, currColor, s2); strcpy(s2, " "); - addText(&tempText[textLine], currKol, currColor, s2); + addText(&tempText[textLine], currColumn, currColor, s2); } int16_t sLen = (int16_t)strlen(s); @@ -214,7 +214,7 @@ static void readHelp(void) // this is really messy, directly ported from Pascal s += 5; sLen -= 5; s3 = &s2[strlen(s2)]; - while (textWidth(s2) + charWidth(' ') + 1 < k-currKol) + while (textWidth(s2) + charWidth(' ') + 1 < k-currColumn) { s3[0] = ' '; s3[1] = '\0'; @@ -222,17 +222,17 @@ static void readHelp(void) // this is really messy, directly ported from Pascal } b = textWidth(s2) + 1; - if (b < (k - currKol)) + if (b < k-currColumn) { s3 = &s2[strlen(s2)]; - for (a = 0; a < k-b-currKol; a++) + for (a = 0; a < k-b-currColumn; a++) s3[a] = 127; // one-pixel spacer glyph s3[a] = '\0'; } } - if (textWidth(s2)+textNWidth(s,i)+2 > HELP_WIDTH-currKol) - addText(&tempText[textLine], currKol, currColor, s2); + if (textWidth(s2)+textNWidth(s,i)+2 > HELP_WIDTH-currColumn) + addText(&tempText[textLine], currColumn, currColor, s2); strncat(s2, s, i); @@ -301,42 +301,42 @@ static void bigTextOutHalf(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, b static void writeHelp(void) { - helpRec *pek = subjPtrArr[fHlp_Nr]; - if (pek == NULL) + helpRec *ptr = subjPtrArr[fHlp_Num]; + if (ptr == NULL) return; for (int16_t i = 0; i < HELP_LINES; i++) { const int16_t k = i + fHlp_Line; - if (k >= subjLen[fHlp_Nr]) + if (k >= subjLen[fHlp_Num]) break; - clearRect(HELP_KOL, 5 + (i * 11), HELP_WIDTH, 11); + clearRect(HELP_COLUMN, 5 + (i * 11), HELP_WIDTH, 11); - if (pek[k].noLine) + if (ptr[k].noLine) { if (i == 0) - bigTextOutHalf(HELP_KOL + pek[k-1].xPos, 5 + (i * 11), PAL_FORGRND, false, pek[k-1].text); + bigTextOutHalf(HELP_COLUMN + ptr[k-1].xPos, 5 + (i * 11), PAL_FORGRND, false, ptr[k-1].text); } else { - if (pek[k].bigFont) + if (ptr[k].bigFont) { - if (i == (HELP_LINES - 1)) + if (i == HELP_LINES-1) { - bigTextOutHalf(HELP_KOL + pek[k].xPos, 5 + (i * 11), PAL_FORGRND, true, pek[k].text); + bigTextOutHalf(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, true, ptr[k].text); return; } else { - clearRect(HELP_KOL, 5 + ((i + 1) * 11), HELP_WIDTH, 11); - bigTextOut(HELP_KOL + pek[k].xPos, 5 + (i * 11), PAL_FORGRND, pek[k].text); + clearRect(HELP_COLUMN, 5 + ((i + 1) * 11), HELP_WIDTH, 11); + bigTextOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), PAL_FORGRND, ptr[k].text); i++; } } else { - textOut(HELP_KOL + pek[k].xPos, 5 + (i * 11), pek[k].color, pek[k].text); + textOut(HELP_COLUMN + ptr[k].xPos, 5 + (i * 11), ptr[k].color, ptr[k].text); } } } @@ -353,7 +353,7 @@ void helpScrollUp(void) void helpScrollDown(void) { - if (fHlp_Line < subjLen[fHlp_Nr]-1) + if (fHlp_Line < subjLen[fHlp_Num]-1) { scrollBarScrollDown(SB_HELP_SCROLL, 1); writeHelp(); @@ -388,12 +388,12 @@ void showHelpScreen(void) showPushButton(PB_HELP_SCROLL_DOWN); uncheckRadioButtonGroup(RB_GROUP_HELP); - switch (fHlp_Nr) + switch (fHlp_Num) { default: case 0: tmpID = RB_HELP_FEATURES; break; case 1: tmpID = RB_HELP_EFFECTS; break; - case 2: tmpID = RB_HELP_KEYBOARD; break; + case 2: tmpID = RB_HELP_KEYBINDINGS; break; case 3: tmpID = RB_HELP_HOW_TO_USE_FT2; break; case 4: tmpID = RB_HELP_FAQ; break; case 5: tmpID = RB_HELP_KNOWN_BUGS; break; @@ -407,7 +407,7 @@ void showHelpScreen(void) textOutShadow(4, 4, PAL_FORGRND, PAL_DSKTOP2, "Subjects:"); textOutShadow(21, 19, PAL_FORGRND, PAL_DSKTOP2, "Features"); textOutShadow(21, 35, PAL_FORGRND, PAL_DSKTOP2, "Effects"); - textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keyboard"); + textOutShadow(21, 51, PAL_FORGRND, PAL_DSKTOP2, "Keybindings"); textOutShadow(21, 67, PAL_FORGRND, PAL_DSKTOP2, "How to use FT2"); textOutShadow(21, 83, PAL_FORGRND, PAL_DSKTOP2, "Problems/FAQ"); textOutShadow(21, 99, PAL_FORGRND, PAL_DSKTOP2, "Known bugs"); @@ -435,10 +435,10 @@ void exitHelpScreen(void) static void setHelpSubject(uint8_t Nr) { - fHlp_Nr = Nr; + fHlp_Num = Nr; fHlp_Line = 0; - setScrollBarEnd(SB_HELP_SCROLL, subjLen[fHlp_Nr]); + setScrollBarEnd(SB_HELP_SCROLL, subjLen[fHlp_Num]); setScrollBarPos(SB_HELP_SCROLL, 0, false); } @@ -456,9 +456,9 @@ void rbHelpEffects(void) writeHelp(); } -void rbHelpKeyboard(void) +void rbHelpKeybindings(void) { - checkRadioButton(RB_HELP_KEYBOARD); + checkRadioButton(RB_HELP_KEYBINDINGS); setHelpSubject(2); writeHelp(); } diff --git a/src/ft2_help.h b/src/ft2_help.h @@ -16,7 +16,7 @@ void initFTHelp(void); void windUpFTHelp(void); void rbHelpFeatures(void); void rbHelpEffects(void); -void rbHelpKeyboard(void); +void rbHelpKeybindings(void); void rbHelpHowToUseFT2(void); void rbHelpFAQ(void); void rbHelpKnownBugs(void); diff --git a/src/ft2_inst_ed.c b/src/ft2_inst_ed.c @@ -12,7 +12,7 @@ #include "ft2_audio.h" #include "ft2_pattern_ed.h" #include "ft2_gui.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_sample_ed.h" #include "ft2_mouse.h" #include "ft2_video.h" @@ -27,66 +27,66 @@ #pragma pack(push) #pragma pack(1) #endif -typedef struct instrPATHeaderTyp_t +typedef struct patHdr_t { - char id[22], copyright[60]; - uint8_t antInstr, activeVoices, antChannels; - int16_t waveForms, masterVol; + char ID[22], junk1[60]; + uint8_t numInstrs, junk2, numChannels; + int16_t waveforms, masterVol; int32_t dataSize; - char reserved1[36]; - int16_t instrNr; + char junk4[36]; + int16_t junk5; char instrName[16]; int32_t instrSize; uint8_t layers; - char reserved2[40]; - uint8_t layerDuplicate, layerByte; - int32_t layerSize; - uint8_t antSamp; - char reserved3[40]; + char junk6[40]; + uint8_t junk7, junk8; + int32_t junk9; + uint8_t numSamples; + char junk10[40]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -instrPATHeaderTyp; +patHdr_t; -typedef struct instrPATWaveHeaderTyp_t +typedef struct patWaveHdr_t { char name[7]; uint8_t fractions; - int32_t waveSize, repS, repE; + int32_t sampleLength, loopStart, loopEnd; uint16_t sampleRate; int32_t lowFrq, highFreq, rootFrq; - int16_t fineTune; - uint8_t pan, envRate[6], envOfs[6], tremSweep, tremRate; - uint8_t tremDepth, vibSweep, vibRate, vibDepth, mode; - int16_t scaleFrq; - uint16_t scaleFactor; - char reserved[36]; + int16_t finetune; + uint8_t panning, envRate[6], envOfs[6], tremSweep, tremRate; + uint8_t tremDepth, vibSweep, vibRate, vibDepth, flags; + int16_t junk1; + uint16_t junk2; + char junk3[36]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -instrPATWaveHeaderTyp; +patWaveHdr_t; -typedef struct instrXIHeaderTyp_t +typedef struct xiHdr_t { - char sig[21], name[23], progName[20]; - uint16_t ver; - uint8_t ta[96]; - int16_t envVP[12][2], envPP[12][2]; - uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS; - uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate; - uint16_t fadeOut; + char ID[21], name[23], progName[20]; + uint16_t version; + uint8_t note2SampleLUT[96]; + int16_t volEnvPoints[12][2], panEnvPoints[12][2]; + uint8_t volEnvLength, panEnvLength, volEnvSustain, volEnvLoopStart, volEnvLoopEnd, panEnvSustain, panEnvLoopStart; + uint8_t panEnvLoopEnd, volEnvFlags, panEnvFlags, vibType, vibSweep, vibDepth, vibRate; + uint16_t fadeout; uint8_t midiOn, midiChannel; int16_t midiProgram, midiBend; uint8_t mute, reserved[15]; - int16_t antSamp; - sampleHeaderTyp samp[16]; + int16_t numSamples; + xmSmpHdr_t smp[16]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -instrXIHeaderTyp; +xiHdr_t; #define PIANOKEY_WHITE_W 10 #define PIANOKEY_WHITE_H 46 @@ -114,7 +114,7 @@ static const uint8_t mx2PianoKey[77] = }; // thread data -static uint16_t saveInstrNr; +static uint16_t saveInstrNum; static SDL_Thread *thread; extern const uint16_t *note2Period; // ft2_replayer.c @@ -122,7 +122,44 @@ extern const uint16_t *note2Period; // ft2_replayer.c void updateInstEditor(void); void updateNewInstrument(void); -static instrTyp *getCurDispInstr(void) +void sanitizeInstrument(instr_t *ins) +{ + if (ins == NULL) + return; + + ins->midiProgram = CLAMP(ins->midiProgram, 0, 127); + ins->midiBend = CLAMP(ins->midiBend, 0, 36); + + if (ins->midiChannel > 15) ins->midiChannel = 15; + if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F; + if (ins->vibRate > 0x3F) ins->vibRate = 0x3F; + if (ins->vibType > 3) ins->vibType = 0; + + for (int32_t i = 0; i < 96; i++) + { + if (ins->note2SampleLUT[i] >= MAX_SMP_PER_INST) + ins->note2SampleLUT[i] = MAX_SMP_PER_INST-1; + } + + if (ins->volEnvLength > 12) ins->volEnvLength = 12; + if (ins->volEnvLoopStart > 11) ins->volEnvLoopStart = 11; + if (ins->volEnvLoopEnd > 11) ins->volEnvLoopEnd = 11; + if (ins->volEnvSustain > 11) ins->volEnvSustain = 11; + if (ins->panEnvLength > 12) ins->panEnvLength = 12; + if (ins->panEnvLoopStart > 11) ins->panEnvLoopStart = 11; + if (ins->panEnvLoopEnd > 11) ins->panEnvLoopEnd = 11; + if (ins->panEnvSustain > 11) ins->panEnvSustain = 11; + + for (int32_t i = 0; i < 12; i++) + { + if ((uint16_t)ins->volEnvPoints[i][0] > 32767) ins->volEnvPoints[i][0] = 32767; + if ((uint16_t)ins->panEnvPoints[i][0] > 32767) ins->panEnvPoints[i][0] = 32767; + if ((uint16_t)ins->volEnvPoints[i][1] > 64) ins->volEnvPoints[i][1] = 64; + if ((uint16_t)ins->panEnvPoints[i][1] > 63) ins->panEnvPoints[i][1] = 63; + } +} + +static instr_t *getCurDispInstr(void) { if (instr[editor.curInstr] == NULL) return instr[131]; @@ -132,44 +169,28 @@ static instrTyp *getCurDispInstr(void) static int32_t SDLCALL copyInstrThread(void *ptr) { - bool error = false; - - const int16_t destIns = editor.curInstr; - const int16_t sourceIns = editor.srcInstr; + const int16_t dstIns = editor.curInstr; + const int16_t srcIns = editor.srcInstr; pauseAudio(); - - freeInstr(destIns); + freeInstr(dstIns); - if (instr[sourceIns] != NULL) + bool error = true; + if (instr[srcIns] != NULL) { - if (allocateInstr(destIns)) + if (allocateInstr(dstIns)) { - memcpy(instr[destIns], instr[sourceIns], sizeof (instrTyp)); + memcpy(instr[dstIns], instr[srcIns], sizeof (instr_t)); - sampleTyp *src = instr[sourceIns]->samp; - sampleTyp *dst = instr[destIns]->samp; + sample_t *srcSmp = instr[srcIns]->smp; + sample_t *dstSmp = instr[dstIns]->smp; - for (int16_t i = 0; i < MAX_SMP_PER_INST; i++, src++, dst++) + for (int16_t i = 0; i < MAX_SMP_PER_INST; i++, srcSmp++, dstSmp++) { - dst->origPek = NULL; - dst->pek = NULL; - - if (src->origPek != NULL) - { - int8_t *p = (int8_t *)malloc(src->len + LOOP_FIX_LEN); - if (p != NULL) - { - dst->origPek = p; - dst->pek = dst->origPek + SMP_DAT_OFFSET; - - memcpy(dst->origPek, src->origPek, src->len + LOOP_FIX_LEN); - } - else error = true; - } + if (!cloneSample(srcSmp, dstSmp)) + error = false; } } - else error = true; } resumeAudio(); @@ -179,12 +200,15 @@ static int32_t SDLCALL copyInstrThread(void *ptr) // do not change instrument names! - editor.updateCurInstr = true; - setSongModifiedFlag(); + if (!error) + { + editor.updateCurInstr = true; + setSongModifiedFlag(); + } + setMouseBusy(false); return false; - (void)ptr; } @@ -211,11 +235,11 @@ void xchgInstr(void) // dstInstr <-> srcInstr lockMixerCallback(); - instrTyp *src = instr[editor.srcInstr]; - instrTyp *dst = instr[editor.curInstr]; + instr_t *src = instr[editor.srcInstr]; + instr_t *dst = instr[editor.curInstr]; // swap instruments - instrTyp dstTmp = *dst; + instr_t dstTmp = *dst; *dst = *src; *src = dstTmp; @@ -229,7 +253,7 @@ void xchgInstr(void) // dstInstr <-> srcInstr static void drawMIDICh(void) { - instrTyp *ins = getCurDispInstr(); + instr_t *ins = getCurDispInstr(); assert(ins->midiChannel <= 15); const uint8_t val = ins->midiChannel + 1; textOutFixed(156, 132, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[val]); @@ -237,14 +261,14 @@ static void drawMIDICh(void) static void drawMIDIPrg(void) { - instrTyp *ins = getCurDispInstr(); + instr_t *ins = getCurDispInstr(); assert(ins->midiProgram <= 127); textOutFixed(149, 146, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[ins->midiProgram]); } static void drawMIDIBend(void) { - instrTyp *ins = getCurDispInstr(); + instr_t *ins = getCurDispInstr(); assert(ins->midiBend <= 36); textOutFixed(156, 160, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->midiBend]); } @@ -287,7 +311,7 @@ void midiBendUp(void) void sbMidiChPos(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_EXT_MIDI_CH, 0, false); @@ -304,7 +328,7 @@ void sbMidiChPos(uint32_t pos) void sbMidiPrgPos(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_EXT_MIDI_PRG, 0, false); @@ -321,7 +345,7 @@ void sbMidiPrgPos(uint32_t pos) void sbMidiBendPos(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_EXT_MIDI_BEND, 0, false); @@ -374,66 +398,66 @@ void updateNewInstrument(void) static void drawVolEnvSus(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envVSust < 100); - textOutFixed(382, 206, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVSust]); + instr_t *ins = getCurDispInstr(); + assert(ins->volEnvSustain < 100); + textOutFixed(382, 206, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvSustain]); } static void drawVolEnvRepS(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envVRepS < 100); - textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVRepS]); + instr_t *ins = getCurDispInstr(); + assert(ins->volEnvLoopStart < 100); + textOutFixed(382, 233, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopStart]); } static void drawVolEnvRepE(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envVRepE < 100); - textOutFixed(382, 247, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envVRepE]); + instr_t *ins = getCurDispInstr(); + assert(ins->volEnvLoopEnd < 100); + textOutFixed(382, 247, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->volEnvLoopEnd]); } static void drawPanEnvSus(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envPSust < 100); - textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPSust]); + instr_t *ins = getCurDispInstr(); + assert(ins->panEnvSustain < 100); + textOutFixed(382, 293, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvSustain]); } static void drawPanEnvRepS(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envPRepS < 100); - textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPRepS]); + instr_t *ins = getCurDispInstr(); + assert(ins->panEnvLoopStart < 100); + textOutFixed(382, 320, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopStart]); } static void drawPanEnvRepE(void) { - instrTyp *ins = getCurDispInstr(); - assert(ins->envPRepE < 100); - textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->envPRepE]); + instr_t *ins = getCurDispInstr(); + assert(ins->panEnvLoopEnd < 100); + textOutFixed(382, 334, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[ins->panEnvLoopEnd]); } static void drawVolume(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL) - s = &instr[0]->samp[0]; + s = &instr[0]->smp[0]; else - s = &instr[editor.curInstr]->samp[editor.curSmp]; + s = &instr[editor.curInstr]->smp[editor.curSmp]; - hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->vol, 2); + hexOutBg(505, 177, PAL_FORGRND, PAL_DESKTOP, s->volume, 2); } static void drawPanning(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL) - s = &instr[0]->samp[0]; + s = &instr[0]->smp[0]; else - s = &instr[editor.curInstr]->samp[editor.curSmp]; + s = &instr[editor.curInstr]->smp[editor.curSmp]; - hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->pan, 2); + hexOutBg(505, 191, PAL_FORGRND, PAL_DESKTOP, s->panning, 2); } void drawC4Rate(void) @@ -443,9 +467,9 @@ void drawC4Rate(void) int32_t C4Hz = 0; if (editor.curInstr != 0) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins != NULL) - C4Hz = (int32_t)(getSampleC4Rate(&ins->samp[editor.curSmp]) + 0.5); // rounded + C4Hz = (int32_t)(getSampleC4Rate(&ins->smp[editor.curSmp]) + 0.5); // rounded } char str[64]; @@ -455,15 +479,15 @@ void drawC4Rate(void) static void drawFineTune(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL) - s = &instr[0]->samp[0]; + s = &instr[0]->smp[0]; else - s = &instr[editor.curInstr]->samp[editor.curSmp]; + s = &instr[editor.curInstr]->smp[editor.curSmp]; fillRect(491, 205, 27, 8, PAL_DESKTOP); - int16_t ftune = s->fine; + int16_t ftune = s->finetune; if (ftune == 0) { charOut(512, 205, PAL_FORGRND, '0'); @@ -495,7 +519,7 @@ static void drawFineTune(void) static void drawFadeout(void) { - hexOutBg(498, 222, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->fadeOut, 3); + hexOutBg(498, 222, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->fadeout, 3); } static void drawVibSpeed(void) @@ -513,7 +537,7 @@ static void drawVibSweep(void) hexOutBg(505, 264, PAL_FORGRND, PAL_DESKTOP, getCurDispInstr()->vibSweep, 2); } -static void drawRelTone(void) +static void drawRelativeNote(void) { char noteChar1, noteChar2; int8_t note2; @@ -527,7 +551,7 @@ static void drawRelTone(void) if (editor.curInstr == 0) note2 = 48; else - note2 = 48 + instr[editor.curInstr]->samp[editor.curSmp].relTon; + note2 = 48 + instr[editor.curInstr]->smp[editor.curSmp].relativeNote; const int8_t note = note2 % 12; if (config.ptnAcc == 0) @@ -548,68 +572,68 @@ static void drawRelTone(void) charOutBg(616, 299, PAL_FORGRND, PAL_BCKGRND, octaChar); } -static void setStdVolEnvelope(instrTyp *ins, uint8_t num) +static void setStdVolEnvelope(instr_t *ins, uint8_t num) { if (editor.curInstr == 0 || ins == NULL) return; pauseMusic(); - ins->fadeOut = config.stdFadeOut[num]; - ins->envVSust = (uint8_t)config.stdVolEnvSust[num]; - ins->envVRepS = (uint8_t)config.stdVolEnvRepS[num]; - ins->envVRepE = (uint8_t)config.stdVolEnvRepE[num]; - ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[num]; - ins->envVTyp = (uint8_t)config.stdVolEnvTyp[num]; + ins->fadeout = config.stdFadeout[num]; + ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[num]; + ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[num]; + ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[num]; + ins->volEnvLength = (uint8_t)config.stdVolEnvLength[num]; + ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[num]; ins->vibRate = (uint8_t)config.stdVibRate[num]; ins->vibDepth = (uint8_t)config.stdVibDepth[num]; ins->vibSweep = (uint8_t)config.stdVibSweep[num]; - ins->vibTyp = (uint8_t)config.stdVibTyp[num]; + ins->vibType = (uint8_t)config.stdVibType[num]; - memcpy(ins->envVP, config.stdEnvP[num][0], sizeof (int16_t) * 12 * 2); + memcpy(ins->volEnvPoints, config.stdEnvPoints[num][0], sizeof (int16_t) * 12 * 2); resumeMusic(); } -static void setStdPanEnvelope(instrTyp *ins, uint8_t num) +static void setStdPanEnvelope(instr_t *ins, uint8_t num) { if (editor.curInstr == 0 || ins == NULL) return; pauseMusic(); - ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[num]; - ins->envPSust = (uint8_t)config.stdPanEnvSust[num]; - ins->envPRepS = (uint8_t)config.stdPanEnvRepS[num]; - ins->envPRepE = (uint8_t)config.stdPanEnvRepE[num]; - ins->envPTyp = (uint8_t)config.stdPanEnvTyp[num]; + ins->panEnvLength = (uint8_t)config.stdPanEnvLength[num]; + ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[num]; + ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[num]; + ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[num]; + ins->panEnvFlags = (uint8_t)config.stdPanEnvFlags[num]; - memcpy(ins->envPP, config.stdEnvP[num][1], sizeof (int16_t) * 12 * 2); + memcpy(ins->panEnvPoints, config.stdEnvPoints[num][1], sizeof (int16_t) * 12 * 2); resumeMusic(); } static void setOrStoreVolEnvPreset(uint8_t num) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; if (mouse.rightButtonReleased) { // store preset - config.stdFadeOut[num] = ins->fadeOut; - config.stdVolEnvSust[num] = ins->envVSust; - config.stdVolEnvRepS[num] = ins->envVRepS; - config.stdVolEnvRepE[num] = ins->envVRepE; - config.stdVolEnvAnt[num] = ins->envVPAnt; - config.stdVolEnvTyp[num] = ins->envVTyp; + config.stdFadeout[num] = ins->fadeout; + config.stdVolEnvSustain[num] = ins->volEnvSustain; + config.stdVolEnvLoopStart[num] = ins->volEnvLoopStart; + config.stdVolEnvLoopEnd[num] = ins->volEnvLoopEnd; + config.stdVolEnvLength[num] = ins->volEnvLength; + config.stdVolEnvFlags[num] = ins->volEnvFlags; config.stdVibRate[num] = ins->vibRate; config.stdVibDepth[num] = ins->vibDepth; config.stdVibSweep[num] = ins->vibSweep; - config.stdVibTyp[num] = ins->vibTyp; + config.stdVibType[num] = ins->vibType; - memcpy(config.stdEnvP[num][0], ins->envVP, sizeof (int16_t) * 12 * 2); + memcpy(config.stdEnvPoints[num][0], ins->volEnvPoints, sizeof (int16_t) * 12 * 2); } else if (mouse.leftButtonReleased) { @@ -623,25 +647,25 @@ static void setOrStoreVolEnvPreset(uint8_t num) static void setOrStorePanEnvPreset(uint8_t num) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; if (mouse.rightButtonReleased) { // store preset - config.stdFadeOut[num] = ins->fadeOut; - config.stdPanEnvSust[num] = ins->envPSust; - config.stdPanEnvRepS[num] = ins->envPRepS; - config.stdPanEnvRepE[num] = ins->envPRepE; - config.stdPanEnvAnt[num] = ins->envPPAnt; - config.stdPanEnvTyp[num] = ins->envPTyp; + config.stdFadeout[num] = ins->fadeout; + config.stdPanEnvSustain[num] = ins->panEnvSustain; + config.stdPanEnvLoopStart[num] = ins->panEnvLoopStart; + config.stdPanEnvLoopEnd[num] = ins->panEnvLoopEnd; + config.stdPanEnvLength[num] = ins->panEnvLength; + config.stdPanEnvFlags[num] = ins->panEnvFlags; config.stdVibRate[num] = ins->vibRate; config.stdVibDepth[num] = ins->vibDepth; config.stdVibSweep[num] = ins->vibSweep; - config.stdVibTyp[num] = ins->vibTyp; + config.stdVibType[num] = ins->vibType; - memcpy(config.stdEnvP[num][1], ins->envPP, sizeof (int16_t) * 12 * 2); + memcpy(config.stdEnvPoints[num][1], ins->panEnvPoints, sizeof (int16_t) * 12 * 2); } else if (mouse.leftButtonReleased) { @@ -725,67 +749,67 @@ void panPreDef6(void) setOrStorePanEnvPreset(6 - 1); } -void relToneOctUp(void) +void relativeNoteOctUp(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->relTon <= 71-12) - s->relTon += 12; + s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->relativeNote <= 71-12) + s->relativeNote += 12; else - s->relTon = 71; + s->relativeNote = 71; - drawRelTone(); + drawRelativeNote(); drawC4Rate(); setSongModifiedFlag(); } -void relToneOctDown(void) +void relativeNoteOctDown(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->relTon >= -48+12) - s->relTon -= 12; + s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->relativeNote >= -48+12) + s->relativeNote -= 12; else - s->relTon = -48; + s->relativeNote = -48; - drawRelTone(); + drawRelativeNote(); drawC4Rate(); setSongModifiedFlag(); } -void relToneUp(void) +void relativeNoteUp(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->relTon < 71) + s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->relativeNote < 71) { - s->relTon++; - drawRelTone(); + s->relativeNote++; + drawRelativeNote(); drawC4Rate(); setSongModifiedFlag(); } } -void relToneDown(void) +void relativeNoteDown(void) { - sampleTyp *s; + sample_t *s; if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->relTon > -48) + s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->relativeNote > -48) { - s->relTon--; - drawRelTone(); + s->relativeNote--; + drawRelativeNote(); drawC4Rate(); setSongModifiedFlag(); } @@ -793,11 +817,11 @@ void relToneDown(void) void volEnvAdd(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (editor.curInstr == 0 || ins == NULL) return; - const int16_t ant = ins->envVPAnt; + const int16_t ant = ins->volEnvLength; if (ant >= 12) return; @@ -809,37 +833,37 @@ void volEnvAdd(void) i = 0; } - if (i < ant-1 && ins->envVP[i+1][0]-ins->envVP[i][0] < 2) + if (i < ant-1 && ins->volEnvPoints[i+1][0]-ins->volEnvPoints[i][0] < 2) return; - if (ins->envVP[i][0] >= 323) + if (ins->volEnvPoints[i][0] >= 323) return; for (int16_t j = ant; j > i; j--) { - ins->envVP[j][0] = ins->envVP[j-1][0]; - ins->envVP[j][1] = ins->envVP[j-1][1]; + ins->volEnvPoints[j][0] = ins->volEnvPoints[j-1][0]; + ins->volEnvPoints[j][1] = ins->volEnvPoints[j-1][1]; } - if (ins->envVSust > i) { ins->envVSust++; drawVolEnvSus(); } - if (ins->envVRepS > i) { ins->envVRepS++; drawVolEnvRepS(); } - if (ins->envVRepE > i) { ins->envVRepE++; drawVolEnvRepE(); } + if (ins->volEnvSustain > i) { ins->volEnvSustain++; drawVolEnvSus(); } + if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart++; drawVolEnvRepS(); } + if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd++; drawVolEnvRepE(); } if (i < ant-1) { - ins->envVP[i+1][0] = (ins->envVP[i][0] + ins->envVP[i+2][0]) / 2; - ins->envVP[i+1][1] = (ins->envVP[i][1] + ins->envVP[i+2][1]) / 2; + ins->volEnvPoints[i+1][0] = (ins->volEnvPoints[i][0] + ins->volEnvPoints[i+2][0]) / 2; + ins->volEnvPoints[i+1][1] = (ins->volEnvPoints[i][1] + ins->volEnvPoints[i+2][1]) / 2; } else { - ins->envVP[i+1][0] = ins->envVP[i][0] + 10; - ins->envVP[i+1][1] = ins->envVP[i][1]; + ins->volEnvPoints[i+1][0] = ins->volEnvPoints[i][0] + 10; + ins->volEnvPoints[i+1][1] = ins->volEnvPoints[i][1]; } - if (ins->envVP[i+1][0] > 324) - ins->envVP[i+1][0] = 324; + if (ins->volEnvPoints[i+1][0] > 324) + ins->volEnvPoints[i+1][0] = 324; - ins->envVPAnt++; + ins->volEnvLength++; updateVolEnv = true; setSongModifiedFlag(); @@ -847,43 +871,43 @@ void volEnvAdd(void) void volEnvDel(void) { - instrTyp *ins = instr[editor.curInstr]; - if (ins == NULL || editor.curInstr == 0 || ins->envVPAnt <= 2) + instr_t *ins = instr[editor.curInstr]; + if (ins == NULL || editor.curInstr == 0 || ins->volEnvLength <= 2) return; int16_t i = (int16_t)editor.currVolEnvPoint; - if (i < 0 || i >= ins->envVPAnt) + if (i < 0 || i >= ins->volEnvLength) return; - for (int16_t j = i; j < ins->envVPAnt; j++) + for (int16_t j = i; j < ins->volEnvLength; j++) { - ins->envVP[j][0] = ins->envVP[j+1][0]; - ins->envVP[j][1] = ins->envVP[j+1][1]; + ins->volEnvPoints[j][0] = ins->volEnvPoints[j+1][0]; + ins->volEnvPoints[j][1] = ins->volEnvPoints[j+1][1]; } bool drawSust = false; bool drawRepS = false; bool drawRepE = false; - if (ins->envVSust > i) { ins->envVSust--; drawSust = true; } - if (ins->envVRepS > i) { ins->envVRepS--; drawRepS = true; } - if (ins->envVRepE > i) { ins->envVRepE--; drawRepE = true; } + if (ins->volEnvSustain > i) { ins->volEnvSustain--; drawSust = true; } + if (ins->volEnvLoopStart > i) { ins->volEnvLoopStart--; drawRepS = true; } + if (ins->volEnvLoopEnd > i) { ins->volEnvLoopEnd--; drawRepE = true; } - ins->envVP[0][0] = 0; - ins->envVPAnt--; + ins->volEnvPoints[0][0] = 0; + ins->volEnvLength--; - if (ins->envVSust >= ins->envVPAnt) { ins->envVSust = ins->envVPAnt - 1; drawSust = true; } - if (ins->envVRepS >= ins->envVPAnt) { ins->envVRepS = ins->envVPAnt - 1; drawRepS = true; } - if (ins->envVRepE >= ins->envVPAnt) { ins->envVRepE = ins->envVPAnt - 1; drawRepE = true; } + if (ins->volEnvSustain >= ins->volEnvLength) { ins->volEnvSustain = ins->volEnvLength - 1; drawSust = true; } + if (ins->volEnvLoopStart >= ins->volEnvLength) { ins->volEnvLoopStart = ins->volEnvLength - 1; drawRepS = true; } + if (ins->volEnvLoopEnd >= ins->volEnvLength) { ins->volEnvLoopEnd = ins->volEnvLength - 1; drawRepE = true; } if (drawSust) drawVolEnvSus(); if (drawRepS) drawVolEnvRepS(); if (drawRepE) drawVolEnvRepE(); - if (ins->envVPAnt == 0) + if (ins->volEnvLength == 0) editor.currVolEnvPoint = 0; - else if (editor.currVolEnvPoint >= ins->envVPAnt) - editor.currVolEnvPoint = ins->envVPAnt-1; + else if (editor.currVolEnvPoint >= ins->volEnvLength) + editor.currVolEnvPoint = ins->volEnvLength-1; updateVolEnv = true; setSongModifiedFlag(); @@ -891,13 +915,13 @@ void volEnvDel(void) void volEnvSusUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVSust < ins->envVPAnt-1) + if (ins->volEnvSustain < ins->volEnvLength-1) { - ins->envVSust++; + ins->volEnvSustain++; drawVolEnvSus(); updateVolEnv = true; setSongModifiedFlag(); @@ -906,13 +930,13 @@ void volEnvSusUp(void) void volEnvSusDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVSust > 0) + if (ins->volEnvSustain > 0) { - ins->envVSust--; + ins->volEnvSustain--; drawVolEnvSus(); updateVolEnv = true; setSongModifiedFlag(); @@ -921,13 +945,13 @@ void volEnvSusDown(void) void volEnvRepSUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVRepS < ins->envVRepE) + if (ins->volEnvLoopStart < ins->volEnvLoopEnd) { - ins->envVRepS++; + ins->volEnvLoopStart++; drawVolEnvRepS(); updateVolEnv = true; setSongModifiedFlag(); @@ -936,13 +960,13 @@ void volEnvRepSUp(void) void volEnvRepSDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVRepS > 0) + if (ins->volEnvLoopStart > 0) { - ins->envVRepS--; + ins->volEnvLoopStart--; drawVolEnvRepS(); updateVolEnv = true; setSongModifiedFlag(); @@ -951,13 +975,13 @@ void volEnvRepSDown(void) void volEnvRepEUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVRepE < ins->envVPAnt-1) + if (ins->volEnvLoopEnd < ins->volEnvLength-1) { - ins->envVRepE++; + ins->volEnvLoopEnd++; drawVolEnvRepE(); updateVolEnv = true; setSongModifiedFlag(); @@ -966,13 +990,13 @@ void volEnvRepEUp(void) void volEnvRepEDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envVRepE > ins->envVRepS) + if (ins->volEnvLoopEnd > ins->volEnvLoopStart) { - ins->envVRepE--; + ins->volEnvLoopEnd--; drawVolEnvRepE(); updateVolEnv = true; setSongModifiedFlag(); @@ -981,11 +1005,11 @@ void volEnvRepEDown(void) void panEnvAdd(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - const int16_t ant = ins->envPPAnt; + const int16_t ant = ins->panEnvLength; if (ant >= 12) return; @@ -997,37 +1021,37 @@ void panEnvAdd(void) i = 0; } - if (i < ant-1 && ins->envPP[i+1][0]-ins->envPP[i][0] < 2) + if (i < ant-1 && ins->panEnvPoints[i+1][0]-ins->panEnvPoints[i][0] < 2) return; - if (ins->envPP[i][0] >= 323) + if (ins->panEnvPoints[i][0] >= 323) return; for (int16_t j = ant; j > i; j--) { - ins->envPP[j][0] = ins->envPP[j-1][0]; - ins->envPP[j][1] = ins->envPP[j-1][1]; + ins->panEnvPoints[j][0] = ins->panEnvPoints[j-1][0]; + ins->panEnvPoints[j][1] = ins->panEnvPoints[j-1][1]; } - if (ins->envPSust > i) { ins->envPSust++; drawPanEnvSus(); } - if (ins->envPRepS > i) { ins->envPRepS++; drawPanEnvRepS(); } - if (ins->envPRepE > i) { ins->envPRepE++; drawPanEnvRepE(); } + if (ins->panEnvSustain > i) { ins->panEnvSustain++; drawPanEnvSus(); } + if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart++; drawPanEnvRepS(); } + if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd++; drawPanEnvRepE(); } if (i < ant-1) { - ins->envPP[i+1][0] = (ins->envPP[i][0] + ins->envPP[i+2][0]) / 2; - ins->envPP[i+1][1] = (ins->envPP[i][1] + ins->envPP[i+2][1]) / 2; + ins->panEnvPoints[i+1][0] = (ins->panEnvPoints[i][0] + ins->panEnvPoints[i+2][0]) / 2; + ins->panEnvPoints[i+1][1] = (ins->panEnvPoints[i][1] + ins->panEnvPoints[i+2][1]) / 2; } else { - ins->envPP[i+1][0] = ins->envPP[i][0] + 10; - ins->envPP[i+1][1] = ins->envPP[i][1]; + ins->panEnvPoints[i+1][0] = ins->panEnvPoints[i][0] + 10; + ins->panEnvPoints[i+1][1] = ins->panEnvPoints[i][1]; } - if (ins->envPP[i+1][0] > 324) - ins->envPP[i+1][0] = 324; + if (ins->panEnvPoints[i+1][0] > 324) + ins->panEnvPoints[i+1][0] = 324; - ins->envPPAnt++; + ins->panEnvLength++; updatePanEnv = true; setSongModifiedFlag(); @@ -1035,43 +1059,43 @@ void panEnvAdd(void) void panEnvDel(void) { - instrTyp *ins = instr[editor.curInstr]; - if (ins == NULL || editor.curInstr == 0 || ins->envPPAnt <= 2) + instr_t *ins = instr[editor.curInstr]; + if (ins == NULL || editor.curInstr == 0 || ins->panEnvLength <= 2) return; int16_t i = (int16_t)editor.currPanEnvPoint; - if (i < 0 || i >= ins->envPPAnt) + if (i < 0 || i >= ins->panEnvLength) return; - for (int16_t j = i; j < ins->envPPAnt; j++) + for (int16_t j = i; j < ins->panEnvLength; j++) { - ins->envPP[j][0] = ins->envPP[j+1][0]; - ins->envPP[j][1] = ins->envPP[j+1][1]; + ins->panEnvPoints[j][0] = ins->panEnvPoints[j+1][0]; + ins->panEnvPoints[j][1] = ins->panEnvPoints[j+1][1]; } bool drawSust = false; bool drawRepS = false; bool drawRepE = false; - if (ins->envPSust > i) { ins->envPSust--; drawSust = true; } - if (ins->envPRepS > i) { ins->envPRepS--; drawRepS = true; } - if (ins->envPRepE > i) { ins->envPRepE--; drawRepE = true; } + if (ins->panEnvSustain > i) { ins->panEnvSustain--; drawSust = true; } + if (ins->panEnvLoopStart > i) { ins->panEnvLoopStart--; drawRepS = true; } + if (ins->panEnvLoopEnd > i) { ins->panEnvLoopEnd--; drawRepE = true; } - ins->envPP[0][0] = 0; - ins->envPPAnt--; + ins->panEnvPoints[0][0] = 0; + ins->panEnvLength--; - if (ins->envPSust >= ins->envPPAnt) { ins->envPSust = ins->envPPAnt - 1; drawSust = true; } - if (ins->envPRepS >= ins->envPPAnt) { ins->envPRepS = ins->envPPAnt - 1; drawRepS = true; } - if (ins->envPRepE >= ins->envPPAnt) { ins->envPRepE = ins->envPPAnt - 1; drawRepE = true; } + if (ins->panEnvSustain >= ins->panEnvLength) { ins->panEnvSustain = ins->panEnvLength - 1; drawSust = true; } + if (ins->panEnvLoopStart >= ins->panEnvLength) { ins->panEnvLoopStart = ins->panEnvLength - 1; drawRepS = true; } + if (ins->panEnvLoopEnd >= ins->panEnvLength) { ins->panEnvLoopEnd = ins->panEnvLength - 1; drawRepE = true; } if (drawSust) drawPanEnvSus(); if (drawRepS) drawPanEnvRepS(); if (drawRepE) drawPanEnvRepE(); - if (ins->envPPAnt == 0) + if (ins->panEnvLength == 0) editor.currPanEnvPoint = 0; - else if (editor.currPanEnvPoint >= ins->envPPAnt) - editor.currPanEnvPoint = ins->envPPAnt-1; + else if (editor.currPanEnvPoint >= ins->panEnvLength) + editor.currPanEnvPoint = ins->panEnvLength-1; updatePanEnv = true; setSongModifiedFlag(); @@ -1079,13 +1103,13 @@ void panEnvDel(void) void panEnvSusUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPSust < ins->envPPAnt-1) + if (ins->panEnvSustain < ins->panEnvLength-1) { - ins->envPSust++; + ins->panEnvSustain++; drawPanEnvSus(); updatePanEnv = true; setSongModifiedFlag(); @@ -1094,13 +1118,13 @@ void panEnvSusUp(void) void panEnvSusDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPSust > 0) + if (ins->panEnvSustain > 0) { - ins->envPSust--; + ins->panEnvSustain--; drawPanEnvSus(); updatePanEnv = true; setSongModifiedFlag(); @@ -1109,13 +1133,13 @@ void panEnvSusDown(void) void panEnvRepSUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPRepS < ins->envPRepE) + if (ins->panEnvLoopStart < ins->panEnvLoopEnd) { - ins->envPRepS++; + ins->panEnvLoopStart++; drawPanEnvRepS(); updatePanEnv = true; setSongModifiedFlag(); @@ -1124,13 +1148,13 @@ void panEnvRepSUp(void) void panEnvRepSDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPRepS > 0) + if (ins->panEnvLoopStart > 0) { - ins->envPRepS--; + ins->panEnvLoopStart--; drawPanEnvRepS(); updatePanEnv = true; setSongModifiedFlag(); @@ -1139,13 +1163,13 @@ void panEnvRepSDown(void) void panEnvRepEUp(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPRepE < ins->envPPAnt-1) + if (ins->panEnvLoopEnd < ins->panEnvLength-1) { - ins->envPRepE++; + ins->panEnvLoopEnd++; drawPanEnvRepE(); updatePanEnv = true; setSongModifiedFlag(); @@ -1154,13 +1178,13 @@ void panEnvRepEUp(void) void panEnvRepEDown(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) return; - if (ins->envPRepE > ins->envPRepS) + if (ins->panEnvLoopEnd > ins->panEnvLoopStart) { - ins->envPRepE--; + ins->panEnvLoopEnd--; drawPanEnvRepE(); updatePanEnv = true; setSongModifiedFlag(); @@ -1263,10 +1287,10 @@ void setVolumeScroll(uint32_t pos) return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->vol != (uint8_t)pos) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->volume != (uint8_t)pos) { - s->vol = (uint8_t)pos; + s->volume = (uint8_t)pos; drawVolume(); setSongModifiedFlag(); } @@ -1280,10 +1304,10 @@ void setPanningScroll(uint32_t pos) return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->pan != (uint8_t)pos) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->panning != (uint8_t)pos) { - s->pan = (uint8_t)pos; + s->panning = (uint8_t)pos; drawPanning(); setSongModifiedFlag(); } @@ -1297,10 +1321,10 @@ void setFinetuneScroll(uint32_t pos) return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->fine != (int8_t)(pos - 128)) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->finetune != (int8_t)(pos - 128)) { - s->fine = (int8_t)(pos - 128); + s->finetune = (int8_t)(pos - 128); drawFineTune(); drawC4Rate(); setSongModifiedFlag(); @@ -1309,7 +1333,7 @@ void setFinetuneScroll(uint32_t pos) void setFadeoutScroll(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL) { setScrollBarPos(SB_INST_FADEOUT, 0, false); @@ -1322,9 +1346,9 @@ void setFadeoutScroll(uint32_t pos) return; } - if (ins->fadeOut != (uint16_t)pos) + if (ins->fadeout != (uint16_t)pos) { - ins->fadeOut = (uint16_t)pos; + ins->fadeout = (uint16_t)pos; drawFadeout(); setSongModifiedFlag(); } @@ -1332,7 +1356,7 @@ void setFadeoutScroll(uint32_t pos) void setVibSpeedScroll(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_VIBSPEED, 0, false); @@ -1349,7 +1373,7 @@ void setVibSpeedScroll(uint32_t pos) void setVibDepthScroll(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_VIBDEPTH, 0, false); @@ -1366,7 +1390,7 @@ void setVibDepthScroll(uint32_t pos) void setVibSweepScroll(uint32_t pos) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (ins == NULL || editor.curInstr == 0) { setScrollBarPos(SB_INST_VIBSWEEP, 0, false); @@ -1386,7 +1410,7 @@ void rbVibWaveSine(void) if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - instr[editor.curInstr]->vibTyp = 0; + instr[editor.curInstr]->vibType = 0; uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM); radioButtons[RB_INST_WAVE_SINE].state = RADIOBUTTON_CHECKED; @@ -1399,7 +1423,7 @@ void rbVibWaveSquare(void) if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - instr[editor.curInstr]->vibTyp = 1; + instr[editor.curInstr]->vibType = 1; uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM); radioButtons[RB_INST_WAVE_SQUARE].state = RADIOBUTTON_CHECKED; @@ -1412,7 +1436,7 @@ void rbVibWaveRampDown(void) if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - instr[editor.curInstr]->vibTyp = 2; + instr[editor.curInstr]->vibType = 2; uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM); radioButtons[RB_INST_WAVE_RAMP_DOWN].state = RADIOBUTTON_CHECKED; @@ -1425,7 +1449,7 @@ void rbVibWaveRampUp(void) if (instr[editor.curInstr] == NULL || editor.curInstr == 0) return; - instr[editor.curInstr]->vibTyp = 3; + instr[editor.curInstr]->vibType = 3; uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM); radioButtons[RB_INST_WAVE_RAMP_UP].state = RADIOBUTTON_CHECKED; @@ -1442,7 +1466,7 @@ void cbVEnv(void) return; } - instr[editor.curInstr]->envVTyp ^= 1; + instr[editor.curInstr]->volEnvFlags ^= 1; updateVolEnv = true; setSongModifiedFlag(); @@ -1457,7 +1481,7 @@ void cbVEnvSus(void) return; } - instr[editor.curInstr]->envVTyp ^= 2; + instr[editor.curInstr]->volEnvFlags ^= 2; updateVolEnv = true; setSongModifiedFlag(); @@ -1472,7 +1496,7 @@ void cbVEnvLoop(void) return; } - instr[editor.curInstr]->envVTyp ^= 4; + instr[editor.curInstr]->volEnvFlags ^= 4; updateVolEnv = true; setSongModifiedFlag(); @@ -1487,7 +1511,7 @@ void cbPEnv(void) return; } - instr[editor.curInstr]->envPTyp ^= 1; + instr[editor.curInstr]->panEnvFlags ^= 1; updatePanEnv = true; setSongModifiedFlag(); @@ -1502,7 +1526,7 @@ void cbPEnvSus(void) return; } - instr[editor.curInstr]->envPTyp ^= 2; + instr[editor.curInstr]->panEnvFlags ^= 2; updatePanEnv = true; setSongModifiedFlag(); @@ -1517,7 +1541,7 @@ void cbPEnvLoop(void) return; } - instr[editor.curInstr]->envPTyp ^= 4; + instr[editor.curInstr]->panEnvFlags ^= 4; updatePanEnv = true; setSongModifiedFlag(); @@ -1546,7 +1570,7 @@ static void writePianoNumber(uint8_t note, uint8_t key, uint8_t octave) { uint8_t number = 0; if (instr[editor.curInstr] != NULL && editor.curInstr != 0) - number = instr[editor.curInstr]->ta[note]; + number = instr[editor.curInstr]->note2SampleLUT[note]; const uint16_t x = keyDigitXPos[key] + (octave * 77); @@ -1633,9 +1657,9 @@ bool testPianoKeysMouseDown(bool mouseButtonDown) } const uint8_t note = (octave * 12) + key; - if (instr[editor.curInstr]->ta[note] != editor.curSmp) + if (instr[editor.curInstr]->note2SampleLUT[note] != editor.curSmp) { - instr[editor.curInstr]->ta[note] = editor.curSmp; + instr[editor.curInstr]->note2SampleLUT[note] = editor.curSmp; writePianoNumber(note, key, octave); setSongModifiedFlag(); } @@ -1654,20 +1678,20 @@ void drawPiano(chSyncData_t *chSyncData) if (chSyncData != NULL) // song is playing, use replayer channel state { syncedChannel_t *c = chSyncData->channels; - for (int32_t i = 0; i < song.antChn; i++, c++) + for (int32_t i = 0; i < song.numChannels; i++, c++) { - if (c->instrNr == editor.curInstr && c->pianoNoteNr <= 95) - newStatus[c->pianoNoteNr] = true; + if (c->instrNum == editor.curInstr && c->pianoNoteNum <= 95) + newStatus[c->pianoNoteNum] = true; } } else // song is not playing (jamming from keyboard/MIDI) { - stmTyp *c = stm; - for (int32_t i = 0; i < song.antChn; i++, c++) + channel_t *c = channel; + for (int32_t i = 0; i < song.numChannels; i++, c++) { - if (c->instrNr == editor.curInstr && c->envSustainActive) + if (c->instrNum == editor.curInstr && c->envSustainActive) { - const int32_t note = getPianoKey(c->finalPeriod, c->fineTune, c->relTonNr); + const int32_t note = getPianoKey(c->finalPeriod, c->finetune, c->relativeNote); if (note >= 0 && note <= 95) newStatus[note] = true; } @@ -1694,19 +1718,19 @@ void drawPiano(chSyncData_t *chSyncData) } } -static void envelopeLine(int32_t nr, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t col) +static void envelopeLine(int32_t envNum, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t pal) { y1 = CLAMP(y1, 0, 66); y2 = CLAMP(y2, 0, 66); x1 = CLAMP(x1, 0, 335); x2 = CLAMP(x2, 0, 335); - if (nr == 0) + if (envNum == 0) // volume envelope { y1 += 189; y2 += 189; } - else + else // panning envelope { y1 += 276; y2 += 276; @@ -1723,7 +1747,7 @@ static void envelopeLine(int32_t nr, int16_t x1, int16_t y1, int16_t x2, int16_t const uint32_t pal1 = video.palette[PAL_BLCKMRK]; const uint32_t pal2 = video.palette[PAL_BLCKTXT]; - const uint32_t pixVal = video.palette[col]; + const uint32_t pixVal = video.palette[pal]; const int32_t pitch = sy * SCREEN_W; uint32_t *dst32 = &video.frameBuffer[(y * SCREEN_W) + x]; @@ -1787,15 +1811,15 @@ static void envelopeLine(int32_t nr, int16_t x1, int16_t y1, int16_t x2, int16_t } } -static void envelopePixel(int32_t nr, int16_t x, int16_t y, uint8_t col) +static void envelopePixel(int32_t envNum, int16_t x, int16_t y, uint8_t pal) { - y += (nr == 0) ? 189 : 276; - video.frameBuffer[(y * SCREEN_W) + x] = video.palette[col]; + y += (envNum == 0) ? 189 : 276; + video.frameBuffer[(y * SCREEN_W) + x] = video.palette[pal]; } -static void envelopeDot(int32_t nr, int16_t x, int16_t y) +static void envelopeDot(int32_t envNum, int16_t x, int16_t y) { - y += (nr == 0) ? 189 : 276; + y += (envNum == 0) ? 189 : 276; const uint32_t pixVal = video.palette[PAL_BLCKTXT]; uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x]; @@ -1810,11 +1834,11 @@ static void envelopeDot(int32_t nr, int16_t x, int16_t y) } } -static void envelopeVertLine(int32_t nr, int16_t x, int16_t y, uint8_t col) +static void envelopeVertLine(int32_t envNum, int16_t x, int16_t y, uint8_t pal) { - y += (nr == 0) ? 189 : 276; + y += (envNum == 0) ? 189 : 276; - const uint32_t pixVal1 = video.palette[col]; + const uint32_t pixVal1 = video.palette[pal]; const uint32_t pixVal2 = video.palette[PAL_BLCKTXT]; uint32_t *dstPtr = &video.frameBuffer[(y * SCREEN_W) + x]; @@ -1827,45 +1851,46 @@ static void envelopeVertLine(int32_t nr, int16_t x, int16_t y, uint8_t col) } } -static void writeEnvelope(int32_t nr) +static void writeEnvelope(int32_t envNum) { uint8_t selected; int16_t i, nd, sp, ls, le, (*curEnvP)[2]; - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; // clear envelope area - if (nr == 0) + if (envNum == 0) clearRect(5, 189, 333, 67); else clearRect(5, 276, 333, 67); // draw dotted x/y lines - for (i = 0; i <= 32; i++) envelopePixel(nr, 5, 1 + i * 2, PAL_PATTEXT); - for (i = 0; i <= 8; i++) envelopePixel(nr, 4, 1 + i * 8, PAL_PATTEXT); - for (i = 0; i <= 162; i++) envelopePixel(nr, 8 + i * 2, 65, PAL_PATTEXT); - for (i = 0; i <= 6; i++) envelopePixel(nr, 8 + i * 50, 66, PAL_PATTEXT); + for (i = 0; i <= 32; i++) envelopePixel(envNum, 5, 1 + i * 2, PAL_PATTEXT); + for (i = 0; i <= 8; i++) envelopePixel(envNum, 4, 1 + i * 8, PAL_PATTEXT); + for (i = 0; i <= 162; i++) envelopePixel(envNum, 8 + i * 2, 65, PAL_PATTEXT); + for (i = 0; i <= 6; i++) envelopePixel(envNum, 8 + i * 50, 66, PAL_PATTEXT); // draw center line on pan envelope - if (nr == 1) - envelopeLine(nr, 8, 33, 332, 33, PAL_BLCKMRK); + if (envNum == 1) + envelopeLine(envNum, 8, 33, 332, 33, PAL_BLCKMRK); if (ins == NULL) return; // collect variables - if (nr == 0) + + if (envNum == 0) // volume envelope { - nd = ins->envVPAnt; - if (ins->envVTyp & 2) - sp = ins->envVSust; + nd = ins->volEnvLength; + if (ins->volEnvFlags & ENV_SUSTAIN) + sp = ins->volEnvSustain; else sp = -1; - if (ins->envVTyp & 4) + if (ins->volEnvFlags & ENV_LOOP) { - ls = ins->envVRepS; - le = ins->envVRepE; + ls = ins->volEnvLoopStart; + le = ins->volEnvLoopEnd; } else { @@ -1873,21 +1898,21 @@ static void writeEnvelope(int32_t nr) le = -1; } - curEnvP = ins->envVP; + curEnvP = ins->volEnvPoints; selected = editor.currVolEnvPoint; } - else + else // panning envelope { - nd = ins->envPPAnt; - if (ins->envPTyp & 2) - sp = ins->envPSust; + nd = ins->panEnvLength; + if (ins->panEnvFlags & ENV_SUSTAIN) + sp = ins->panEnvSustain; else sp = -1; - if (ins->envPTyp & 4) + if (ins->panEnvFlags & ENV_LOOP) { - ls = ins->envPRepS; - le = ins->envPRepE; + ls = ins->panEnvLoopStart; + le = ins->panEnvLoopEnd; } else { @@ -1895,7 +1920,7 @@ static void writeEnvelope(int32_t nr) le = -1; } - curEnvP = ins->envPP; + curEnvP = ins->panEnvPoints; selected = editor.currPanEnvPoint; } @@ -1913,48 +1938,48 @@ static void writeEnvelope(int32_t nr) x = CLAMP(x, 0, 324); - if (nr == 0) + if (envNum == 0) // volume envelope y = CLAMP(y, 0, 64); - else + else // panning envelope y = CLAMP(y, 0, 63); if ((uint16_t)curEnvP[i][0] <= 324) { - envelopeDot(nr, 7 + x, 64 - y); + envelopeDot(envNum, 7 + x, 64 - y); // draw "envelope selected" data if (i == selected) { - envelopeLine(nr, 5 + x, 64 - y, 5 + x, 66 - y, PAL_BLCKTXT); - envelopeLine(nr, 11 + x, 64 - y, 11 + x, 66 - y, PAL_BLCKTXT); - envelopePixel(nr, 5, 65 - y, PAL_BLCKTXT); - envelopePixel(nr, 8 + x, 65, PAL_BLCKTXT); + envelopeLine(envNum, 5 + x, 64 - y, 5 + x, 66 - y, PAL_BLCKTXT); + envelopeLine(envNum, 11 + x, 64 - y, 11 + x, 66 - y, PAL_BLCKTXT); + envelopePixel(envNum, 5, 65 - y, PAL_BLCKTXT); + envelopePixel(envNum, 8 + x, 65, PAL_BLCKTXT); } // draw loop start marker if (i == ls) { - envelopeLine(nr, x + 6, 1, x + 10, 1, PAL_PATTEXT); - envelopeLine(nr, x + 7, 2, x + 9, 2, PAL_PATTEXT); - envelopeVertLine(nr, x + 8, 1, PAL_PATTEXT); + envelopeLine(envNum, x + 6, 1, x + 10, 1, PAL_PATTEXT); + envelopeLine(envNum, x + 7, 2, x + 9, 2, PAL_PATTEXT); + envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT); } // draw sustain marker if (i == sp) - envelopeVertLine(nr, x + 8, 1, PAL_BLCKTXT); + envelopeVertLine(envNum, x + 8, 1, PAL_BLCKTXT); // draw loop end marker if (i == le) { - envelopeLine(nr, x + 6, 65, x + 10, 65, PAL_PATTEXT); - envelopeLine(nr, x + 7, 64, x + 9, 64, PAL_PATTEXT); - envelopeVertLine(nr, x + 8, 1, PAL_PATTEXT); + envelopeLine(envNum, x + 6, 65, x + 10, 65, PAL_PATTEXT); + envelopeLine(envNum, x + 7, 64, x + 9, 64, PAL_PATTEXT); + envelopeVertLine(envNum, x + 8, 1, PAL_PATTEXT); } } // draw envelope line if (i > 0 && lx < x) - envelopeLine(nr, lx + 8, 65 - ly, x + 8, 65 - y, PAL_PATTEXT); + envelopeLine(envNum, lx + 8, 65 - ly, x + 8, 65 - y, PAL_PATTEXT); lx = x; ly = y; @@ -2010,7 +2035,7 @@ void handleInstEditorRedrawing(void) { int16_t tick, val; - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; if (updateVolEnv) { @@ -2020,10 +2045,10 @@ void handleInstEditorRedrawing(void) tick = 0; val = 0; - if (ins != NULL && ins->envVPAnt > 0) + if (ins != NULL && ins->volEnvLength > 0) { - tick = ins->envVP[editor.currVolEnvPoint][0]; - val = ins->envVP[editor.currVolEnvPoint][1]; + tick = ins->volEnvPoints[editor.currVolEnvPoint][0]; + val = ins->volEnvPoints[editor.currVolEnvPoint][1]; } drawVolEnvCoords(tick, val); @@ -2037,10 +2062,10 @@ void handleInstEditorRedrawing(void) tick = 0; val = 32; - if (ins != NULL && ins->envPPAnt > 0) + if (ins != NULL && ins->panEnvLength > 0) { - tick = ins->envPP[editor.currPanEnvPoint][0]; - val = ins->envPP[editor.currPanEnvPoint][1]; + tick = ins->panEnvPoints[editor.currPanEnvPoint][0]; + val = ins->panEnvPoints[editor.currPanEnvPoint][1]; } drawPanEnvCoords(tick, val); @@ -2126,13 +2151,13 @@ void exitInstEditor(void) void updateInstEditor(void) { uint16_t tmpID; - sampleTyp *s; - instrTyp *ins = getCurDispInstr(); + sample_t *s; + instr_t *ins = getCurDispInstr(); if (instr[editor.curInstr] == NULL) - s = &ins->samp[0]; + s = &ins->smp[0]; else - s = &ins->samp[editor.curSmp]; + s = &ins->smp[editor.curSmp]; // update instrument editor extension if (ui.instEditorExtShown) @@ -2169,13 +2194,13 @@ void updateInstEditor(void) drawVibDepth(); drawVibSweep(); drawC4Rate(); - drawRelTone(); + drawRelativeNote(); // set scroll bars - setScrollBarPos(SB_INST_VOL, s->vol, false); - setScrollBarPos(SB_INST_PAN, s->pan, false); - setScrollBarPos(SB_INST_FTUNE, 128 + s->fine, false); - setScrollBarPos(SB_INST_FADEOUT, ins->fadeOut, false); + setScrollBarPos(SB_INST_VOL, s->volume, false); + setScrollBarPos(SB_INST_PAN, s->panning, false); + setScrollBarPos(SB_INST_FTUNE, 128 + s->finetune, false); + setScrollBarPos(SB_INST_FADEOUT, ins->fadeout, false); setScrollBarPos(SB_INST_VIBSPEED, ins->vibRate, false); setScrollBarPos(SB_INST_VIBDEPTH, ins->vibDepth, false); setScrollBarPos(SB_INST_VIBSWEEP, ins->vibSweep, false); @@ -2183,7 +2208,7 @@ void updateInstEditor(void) // set radio buttons uncheckRadioButtonGroup(RB_GROUP_INST_WAVEFORM); - switch (ins->vibTyp) + switch (ins->vibType) { default: case 0: tmpID = RB_INST_WAVE_SINE; break; @@ -2194,17 +2219,18 @@ void updateInstEditor(void) radioButtons[tmpID].state = RADIOBUTTON_CHECKED; - // set check boxes + // set checkboxes + + checkBoxes[CB_INST_VENV].checked = (ins->volEnvFlags & ENV_ENABLED) ? true : false; + checkBoxes[CB_INST_VENV_SUS].checked = (ins->volEnvFlags & ENV_SUSTAIN) ? true : false; + checkBoxes[CB_INST_VENV_LOOP].checked = (ins->volEnvFlags & ENV_LOOP) ? true : false; - checkBoxes[CB_INST_VENV].checked = (ins->envVTyp & 1) ? true : false; - checkBoxes[CB_INST_VENV_SUS].checked = (ins->envVTyp & 2) ? true : false; - checkBoxes[CB_INST_VENV_LOOP].checked = (ins->envVTyp & 4) ? true : false; - checkBoxes[CB_INST_PENV].checked = (ins->envPTyp & 1) ? true : false; - checkBoxes[CB_INST_PENV_SUS].checked = (ins->envPTyp & 2) ? true : false; - checkBoxes[CB_INST_PENV_LOOP].checked = (ins->envPTyp & 4) ? true : false; + checkBoxes[CB_INST_PENV].checked = (ins->panEnvFlags & ENV_ENABLED) ? true : false; + checkBoxes[CB_INST_PENV_SUS].checked = (ins->panEnvFlags & ENV_SUSTAIN) ? true : false; + checkBoxes[CB_INST_PENV_LOOP].checked = (ins->panEnvFlags & ENV_LOOP) ? true : false; - if (editor.currVolEnvPoint >= ins->envVPAnt) editor.currVolEnvPoint = 0; - if (editor.currPanEnvPoint >= ins->envPPAnt) editor.currPanEnvPoint = 0; + if (editor.currVolEnvPoint >= ins->volEnvLength) editor.currVolEnvPoint = 0; + if (editor.currPanEnvPoint >= ins->panEnvLength) editor.currPanEnvPoint = 0; showRadioButtonGroup(RB_GROUP_INST_WAVEFORM); @@ -2257,7 +2283,7 @@ void showInstEditor(void) textOutShadow(342, 334, PAL_FORGRND, PAL_DSKTOP2, "End"); textOutShadow(443, 177, PAL_FORGRND, PAL_DSKTOP2, "Volume"); textOutShadow(443, 191, PAL_FORGRND, PAL_DSKTOP2, "Panning"); - textOutShadow(443, 205, PAL_FORGRND, PAL_DSKTOP2, "Tune"); + textOutShadow(443, 205, PAL_FORGRND, PAL_DSKTOP2, "F.tune"); textOutShadow(442, 222, PAL_FORGRND, PAL_DSKTOP2, "Fadeout"); textOutShadow(442, 236, PAL_FORGRND, PAL_DSKTOP2, "Vib.speed"); textOutShadow(442, 250, PAL_FORGRND, PAL_DSKTOP2, "Vib.depth"); @@ -2363,9 +2389,9 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL) return false; - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; - uint8_t ant = ins->envVPAnt; + uint8_t ant = ins->volEnvLength; if (ant > 12) ant = 12; @@ -2377,7 +2403,7 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) if (my < 189 || my > 256 || mx < 7 || mx > 334) return false; - if (ins->envVPAnt == 0) + if (ins->volEnvLength == 0) return true; lastMouseX = mx; @@ -2385,8 +2411,8 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) for (uint8_t i = 0; i < ant; i++) { - const int32_t x = 8 + ins->envVP[i][0]; - const int32_t y = 190 + (64 - ins->envVP[i][1]); + const int32_t x = 8 + ins->volEnvPoints[i][0]; + const int32_t y = 190 + (64 - ins->volEnvPoints[i][1]); if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2) { @@ -2404,7 +2430,7 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) return true; } - if (ins->envVPAnt == 0) + if (ins->volEnvLength == 0) return true; if (mx != lastMouseX) @@ -2418,19 +2444,19 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) if (editor.currVolEnvPoint == ant-1) { - minX = ins->envVP[editor.currVolEnvPoint-1][0] + 1; + minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1; maxX = 324; } else { - minX = ins->envVP[editor.currVolEnvPoint-1][0] + 1; - maxX = ins->envVP[editor.currVolEnvPoint+1][0] - 1; + minX = ins->volEnvPoints[editor.currVolEnvPoint-1][0] + 1; + maxX = ins->volEnvPoints[editor.currVolEnvPoint+1][0] - 1; } minX = CLAMP(minX, 0, 324); maxX = CLAMP(maxX, 0, 324); - ins->envVP[editor.currVolEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX)); + ins->volEnvPoints[editor.currVolEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX)); updateVolEnv = true; setSongModifiedFlag(); @@ -2444,7 +2470,7 @@ bool testInstrVolEnvMouseDown(bool mouseButtonDown) my -= saveMouseY; my = 64 - CLAMP(my, 0, 64); - ins->envVP[editor.currVolEnvPoint][1] = (int16_t)my; + ins->volEnvPoints[editor.currVolEnvPoint][1] = (int16_t)my; updateVolEnv = true; setSongModifiedFlag(); @@ -2460,9 +2486,9 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) if (!ui.instEditorShown || editor.curInstr == 0 || instr[editor.curInstr] == NULL) return false; - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; - uint8_t ant = ins->envPPAnt; + uint8_t ant = ins->panEnvLength; if (ant > 12) ant = 12; @@ -2474,7 +2500,7 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) if (my < 277 || my > 343 || mx < 7 || mx > 334) return false; - if (ins->envPPAnt == 0) + if (ins->panEnvLength == 0) return true; lastMouseX = mx; @@ -2482,8 +2508,8 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) for (uint8_t i = 0; i < ant; i++) { - const int32_t x = 8 + ins->envPP[i][0]; - const int32_t y = 277 + (63 - ins->envPP[i][1]); + const int32_t x = 8 + ins->panEnvPoints[i][0]; + const int32_t y = 277 + (63 - ins->panEnvPoints[i][1]); if (mx >= x-2 && mx <= x+2 && my >= y-2 && my <= y+2) { @@ -2501,7 +2527,7 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) return true; } - if (ins->envPPAnt == 0) + if (ins->panEnvLength == 0) return true; if (mx != lastMouseX) @@ -2515,19 +2541,19 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) if (editor.currPanEnvPoint == ant-1) { - minX = ins->envPP[editor.currPanEnvPoint-1][0] + 1; + minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1; maxX = 324; } else { - minX = ins->envPP[editor.currPanEnvPoint-1][0] + 1; - maxX = ins->envPP[editor.currPanEnvPoint+1][0] - 1; + minX = ins->panEnvPoints[editor.currPanEnvPoint-1][0] + 1; + maxX = ins->panEnvPoints[editor.currPanEnvPoint+1][0] - 1; } minX = CLAMP(minX, 0, 324); maxX = CLAMP(maxX, 0, 324); - ins->envPP[editor.currPanEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX)); + ins->panEnvPoints[editor.currPanEnvPoint][0] = (int16_t)(CLAMP(mx, minX, maxX)); updatePanEnv = true; setSongModifiedFlag(); @@ -2541,7 +2567,7 @@ bool testInstrPanEnvMouseDown(bool mouseButtonDown) my -= saveMouseY; my = 63 - CLAMP(my, 0, 63); - ins->envPP[editor.currPanEnvPoint][1] = (int16_t)my; + ins->panEnvPoints[editor.currPanEnvPoint][1] = (int16_t)my; updatePanEnv = true; setSongModifiedFlag(); @@ -2578,7 +2604,7 @@ void cbInstMuteComputer(void) void drawInstEditorExt(void) { - instrTyp *ins = instr[editor.curInstr]; + instr_t *ins = instr[editor.curInstr]; drawFramework(0, 92, 291, 17, FRAMEWORK_TYPE1); drawFramework(0, 109, 291, 19, FRAMEWORK_TYPE1); @@ -2870,8 +2896,8 @@ bool testInstrSwitcherMouseDown(void) static int32_t SDLCALL saveInstrThread(void *ptr) { - instrXIHeaderTyp ih; - sampleTyp *s; + xiHdr_t ih; + sample_t *s; if (editor.tmpFilenameU == NULL) { @@ -2879,8 +2905,8 @@ static int32_t SDLCALL saveInstrThread(void *ptr) return false; } - const int16_t n = getUsedSamples(saveInstrNr); - if (n == 0 || instr[saveInstrNr] == NULL) + const int32_t numSamples = getUsedSamples(saveInstrNum); + if (numSamples == 0 || instr[saveInstrNum] == NULL) { okBoxThreadSafe(0, "System message", "Instrument is empty!"); return false; @@ -2895,63 +2921,94 @@ static int32_t SDLCALL saveInstrThread(void *ptr) memset(&ih, 0, sizeof (ih)); // important, also clears reserved stuff - memcpy(ih.sig, "Extended Instrument: ", 21); - memset(ih.name, ' ', 22); - memcpy(ih.name, song.instrName[saveInstrNr], strlen(song.instrName[saveInstrNr])); + memcpy(ih.ID, "Extended Instrument: ", 21); + ih.version = 0x0102; + + // song name + int32_t nameLength = (int32_t)strlen(song.instrName[saveInstrNum]); + if (nameLength > 22) + nameLength = 22; + + memset(ih.name, ' ', 22); // yes, FT2 pads the name with spaces + if (nameLength > 0) + memcpy(ih.name, song.instrName[saveInstrNum], nameLength); + ih.name[22] = 0x1A; - memcpy(ih.progName, PROG_NAME_STR, 20); - ih.ver = 0x0102; + + // program/tracker name + nameLength = (int32_t)strlen(PROG_NAME_STR); + if (nameLength > 20) + nameLength = 20; + + memset(ih.progName, ' ', 20); // yes, FT2 pads the name with spaces + if (nameLength > 0) + memcpy(ih.progName, PROG_NAME_STR, nameLength); // copy over instrument struct data to instrument header - instrTyp *ins = instr[saveInstrNr]; - memcpy(ih.ta, ins->ta, 96); - memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t)); - memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t)); - ih.envVPAnt = ins->envVPAnt; - ih.envPPAnt = ins->envPPAnt; - ih.envVSust = ins->envVSust; - ih.envVRepS = ins->envVRepS; - ih.envVRepE = ins->envVRepE; - ih.envPSust = ins->envPSust; - ih.envPRepS = ins->envPRepS; - ih.envPRepE = ins->envPRepE; - ih.envVTyp = ins->envVTyp; - ih.envPTyp = ins->envPTyp; - ih.vibTyp = ins->vibTyp; + instr_t *ins = instr[saveInstrNum]; + memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96); + memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t)); + memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t)); + ih.volEnvLength = ins->volEnvLength; + ih.panEnvLength = ins->panEnvLength; + ih.volEnvSustain = ins->volEnvSustain; + ih.volEnvLoopStart = ins->volEnvLoopStart; + ih.volEnvLoopEnd = ins->volEnvLoopEnd; + ih.panEnvSustain = ins->panEnvSustain; + ih.panEnvLoopStart = ins->panEnvLoopStart; + ih.panEnvLoopEnd = ins->panEnvLoopEnd; + ih.volEnvFlags = ins->volEnvFlags; + ih.panEnvFlags = ins->panEnvFlags; + ih.vibType = ins->vibType; ih.vibSweep = ins->vibSweep; ih.vibDepth = ins->vibDepth; ih.vibRate = ins->vibRate; - ih.fadeOut = ins->fadeOut; + ih.fadeout = ins->fadeout; ih.midiOn = ins->midiOn ? 1 : 0; ih.midiChannel = ins->midiChannel; ih.midiProgram = ins->midiProgram; ih.midiBend = ins->midiBend; ih.mute = ins->mute ? 1 : 0; - ih.antSamp = n; + ih.numSamples = (uint16_t)numSamples; // copy over sample struct datas to sample headers - s = instr[saveInstrNr]->samp; - for (int32_t i = 0; i < n; i++, s++) + s = instr[saveInstrNum]->smp; + for (int32_t i = 0; i < numSamples; i++, s++) { - sampleHeaderTyp *dst = &ih.samp[i]; + xmSmpHdr_t *dst = &ih.smp[i]; + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - dst->len = s->len; - dst->repS = s->repS; - dst->repL = s->repL; - dst->vol = s->vol; - dst->fine = s->fine; - dst->typ = s->typ; - dst->pan = s->pan; - dst->relTon = s->relTon; + dst->length = s->length; + dst->loopStart = s->loopStart; + dst->loopLength = s->loopLength; - dst->nameLen = (uint8_t)strlen(s->name); - memcpy(dst->name, s->name, 22); + if (sample16Bit) + { + dst->length <<= 1; + dst->loopStart <<= 1; + dst->loopLength <<= 1; + } + + dst->volume = s->volume; + dst->finetune = s->finetune; + dst->flags = s->flags; + dst->panning = s->panning; + dst->relativeNote = s->relativeNote; - if (s->pek == NULL) - dst->len = 0; + dst->nameLength = (uint8_t)strlen(s->name); + if (dst->nameLength > 22) + dst->nameLength = 22; + + memset(dst->name, 0, 22); + if (dst->nameLength > 0) + memcpy(dst->name, s->name, dst->nameLength); + + if (s->dataPtr == NULL) + dst->length = 0; } - size_t result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.antSamp * sizeof (sampleHeaderTyp)), 1, f); + size_t result = fwrite(&ih, INSTR_XI_HEADER_SIZE + (ih.numSamples * sizeof (xmSmpHdr_t)), 1, f); if (result != 1) { fclose(f); @@ -2960,20 +3017,20 @@ static int32_t SDLCALL saveInstrThread(void *ptr) } pauseAudio(); - s = instr[saveInstrNr]->samp; - for (int32_t i = 0; i < n; i++, s++) + s = instr[saveInstrNum]->smp; + for (int32_t i = 0; i < numSamples; i++, s++) { - if (s->pek != NULL && s->len > 0) + if (s->dataPtr != NULL && s->length > 0) { - restoreSample(s); - samp2Delta(s->pek, s->len, s->typ); + unfixSample(s); + samp2Delta(s->dataPtr, s->length, s->flags); - result = fwrite(s->pek, 1, s->len, f); + result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f); - delta2Samp(s->pek, s->len, s->typ); + delta2Samp(s->dataPtr, s->length, s->flags); fixSample(s); - if (result != (size_t)s->len) // write not OK + if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK { resumeAudio(); fclose(f); @@ -2987,19 +3044,19 @@ static int32_t SDLCALL saveInstrThread(void *ptr) fclose(f); editor.diskOpReadDir = true; // force diskop re-read - setMouseBusy(false); + return true; (void)ptr; } -void saveInstr(UNICHAR *filenameU, int16_t nr) +void saveInstr(UNICHAR *filenameU, int16_t insNum) { - if (nr == 0) + if (insNum == 0) return; - saveInstrNr = nr; + saveInstrNum = insNum; UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); mouseAnimOn(); @@ -3015,25 +3072,25 @@ void saveInstr(UNICHAR *filenameU, int16_t nr) static int16_t getPATNote(int32_t freq) { - const double dNote = (log2(freq * (1.0 / 440000.0)) * 12.0) + 57.0; - const int32_t note = (const int32_t)(dNote + 0.5); + const double dNote = (log2(freq / 440000.0) * 12.0) + 57.0; + const int32_t note = (const int32_t)(dNote + 0.5); // rounded return (int16_t)note; } static int32_t SDLCALL loadInstrThread(void *ptr) { - int8_t *newPtr; int16_t a, b; - int32_t i, j; - instrXIHeaderTyp ih; - instrPATHeaderTyp ih_PAT; - instrPATWaveHeaderTyp ih_PATWave; - sampleHeaderTyp *src; - sampleTyp *s; - instrTyp *ins; + int32_t i, j, numLoadedSamples; + xiHdr_t xi_h; + patHdr_t pat_h; + patWaveHdr_t patWave_h; + xmSmpHdr_t *src; + sample_t *s; + instr_t *ins; bool stereoWarning = false; + numLoadedSamples = 0; if (editor.tmpInstrFilenameU == NULL) { @@ -3048,36 +3105,41 @@ static int32_t SDLCALL loadInstrThread(void *ptr) return false; } - memset(&ih, 0, sizeof (ih)); + memset(&xi_h, 0, sizeof (xi_h)); + memset(&pat_h, 0, sizeof (pat_h)); + memset(&patWave_h, 0, sizeof (patWave_h)); - fread(&ih, INSTR_XI_HEADER_SIZE, 1, f); - if (!strncmp(ih.sig, "Extended Instrument: ", 21)) + fread(&xi_h, INSTR_XI_HEADER_SIZE, 1, f); + if (!strncmp(xi_h.ID, "Extended Instrument: ", 21)) { // XI - Extended Instrument - if (ih.ver != 0x0101 && ih.ver != 0x0102) + if (xi_h.version != 0x0101 && xi_h.version != 0x0102) { okBoxThreadSafe(0, "System message", "Incompatible format version!"); goto loadDone; } - if (ih.ver == 0x0101) // not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this. + // not even FT2.01 can save old v1.01 .XI files, so I have no way to verify this + if (xi_h.version == 0x0101) { fseek(f, -20, SEEK_CUR); - ih.antSamp = ih.midiProgram; - ih.midiProgram = 0; - ih.midiBend = 0; - ih.mute = false; + xi_h.numSamples = xi_h.midiProgram; + xi_h.midiProgram = 0; + xi_h.midiBend = 0; + xi_h.mute = false; } - memcpy(song.instrName[editor.curInstr], ih.name, 22); + numLoadedSamples = xi_h.numSamples; + + memcpy(song.instrName[editor.curInstr], xi_h.name, 22); song.instrName[editor.curInstr][22] = '\0'; pauseAudio(); freeInstr(editor.curInstr); - if (ih.antSamp > 0) + if (xi_h.numSamples > 0) { if (!allocateInstr(editor.curInstr)) { @@ -3089,69 +3151,36 @@ static int32_t SDLCALL loadInstrThread(void *ptr) // copy instrument header elements to our instrument struct ins = instr[editor.curInstr]; - memcpy(ins->ta, ih.ta, 96); - memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t)); - memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t)); - ins->envVPAnt = ih.envVPAnt; - ins->envPPAnt = ih.envPPAnt; - ins->envVSust = ih.envVSust; - ins->envVRepS = ih.envVRepS; - ins->envVRepE = ih.envVRepE; - ins->envPSust = ih.envPSust; - ins->envPRepS = ih.envPRepS; - ins->envPRepE = ih.envPRepE; - ins->envVTyp = ih.envVTyp; - ins->envPTyp = ih.envPTyp; - ins->vibTyp = ih.vibTyp; - ins->vibSweep = ih.vibSweep; - ins->vibDepth = ih.vibDepth; - ins->vibRate = ih.vibRate; - ins->fadeOut = ih.fadeOut; - ins->midiOn = (ih.midiOn == 1) ? true : false; - ins->midiChannel = ih.midiChannel; - ins->midiProgram = ih.midiProgram; - ins->midiBend = ih.midiBend; - ins->mute = (ih.mute == 1) ? true : false; // correct logic! Don't mess with this - ins->antSamp = ih.antSamp; // used in loadInstrSample() - - // sanitize stuff for broken/unsupported instruments - ins->midiProgram = CLAMP(ins->midiProgram, 0, 127); - ins->midiBend = CLAMP(ins->midiBend, 0, 36); - - if (ins->midiChannel > 15) ins->midiChannel = 15; - if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F; - if (ins->vibRate > 0x3F) ins->vibRate = 0x3F; - if (ins->vibTyp > 3) ins->vibTyp = 0; - - for (i = 0; i < 96; i++) - { - if (ins->ta[i] >= MAX_SMP_PER_INST) - ins->ta[i] = MAX_SMP_PER_INST-1; - } - - if (ins->envVPAnt > 12) ins->envVPAnt = 12; - if (ins->envVRepS > 11) ins->envVRepS = 11; - if (ins->envVRepE > 11) ins->envVRepE = 11; - if (ins->envVSust > 11) ins->envVSust = 11; - if (ins->envPPAnt > 12) ins->envPPAnt = 12; - if (ins->envPRepS > 11) ins->envPRepS = 11; - if (ins->envPRepE > 11) ins->envPRepE = 11; - if (ins->envPSust > 11) ins->envPSust = 11; - - for (i = 0; i < 12; i++) - { - if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767; - if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767; - if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64; - if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63; - - } - - int32_t sampleHeadersToRead = ih.antSamp; + memcpy(ins->note2SampleLUT, xi_h.note2SampleLUT, 96); + memcpy(ins->volEnvPoints, xi_h.volEnvPoints, 12*2*sizeof(int16_t)); + memcpy(ins->panEnvPoints, xi_h.panEnvPoints, 12*2*sizeof(int16_t)); + ins->volEnvLength = xi_h.volEnvLength; + ins->panEnvLength = xi_h.panEnvLength; + ins->volEnvSustain = xi_h.volEnvSustain; + ins->volEnvLoopStart = xi_h.volEnvLoopStart; + ins->volEnvLoopEnd = xi_h.volEnvLoopEnd; + ins->panEnvSustain = xi_h.panEnvSustain; + ins->panEnvLoopStart = xi_h.panEnvLoopStart; + ins->panEnvLoopEnd = xi_h.panEnvLoopEnd; + ins->volEnvFlags = xi_h.volEnvFlags; + ins->panEnvFlags = xi_h.panEnvFlags; + ins->vibType = xi_h.vibType; + ins->vibSweep = xi_h.vibSweep; + ins->vibDepth = xi_h.vibDepth; + ins->vibRate = xi_h.vibRate; + ins->fadeout = xi_h.fadeout; + ins->midiOn = (xi_h.midiOn == 1) ? true : false; + ins->midiChannel = xi_h.midiChannel; + ins->midiProgram = xi_h.midiProgram; + ins->midiBend = xi_h.midiBend; + ins->mute = (xi_h.mute == 1) ? true : false; // correct logic! Don't mess with this + ins->numSamples = xi_h.numSamples; // used in loadInstrSample() + + int32_t sampleHeadersToRead = xi_h.numSamples; if (sampleHeadersToRead > MAX_SMP_PER_INST) sampleHeadersToRead = MAX_SMP_PER_INST; - if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1) + if (fread(xi_h.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 1, f) != 1) { freeInstr(editor.curInstr); resumeAudio(); @@ -3159,62 +3188,62 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - if (ih.antSamp > MAX_SMP_PER_INST) + if (xi_h.numSamples > MAX_SMP_PER_INST) { - const int32_t sampleHeadersToSkip = ih.antSamp - MAX_SMP_PER_INST; - fseek(f, sampleHeadersToSkip * sizeof (sampleHeaderTyp), SEEK_CUR); + const int32_t sampleHeadersToSkip = xi_h.numSamples - MAX_SMP_PER_INST; + fseek(f, sampleHeadersToSkip * sizeof (xmSmpHdr_t), SEEK_CUR); } for (i = 0; i < sampleHeadersToRead; i++) { - s = &instr[editor.curInstr]->samp[i]; - src = &ih.samp[i]; + s = &instr[editor.curInstr]->smp[i]; + src = &xi_h.smp[i]; // copy sample header elements to our sample struct - s->len = src->len; - s->repS = src->repS; - s->repL = src->repL; - s->vol = src->vol; - s->fine = src->fine; - s->typ = src->typ; - s->pan = src->pan; - s->relTon = src->relTon; + s->length = src->length; + s->loopStart = src->loopStart; + s->loopLength = src->loopLength; + s->volume = src->volume; + s->finetune = src->finetune; + s->flags = src->flags; + s->panning = src->panning; + s->relativeNote = src->relativeNote; memcpy(s->name, src->name, 22); s->name[22] = '\0'; - // dst->pek is set up later - - // trim off spaces at end of name - for (j = 21; j >= 0; j--) - { - if (s->name[j] == ' ' || s->name[j] == 0x1A) - s->name[j] = '\0'; - else - break; - } - - // sanitize stuff broken/unsupported samples - if (s->vol > 64) - s->vol = 64; - - s->relTon = CLAMP(s->relTon, -48, 71); + // dst->dataPtr is set up later } } - for (i = 0; i < ih.antSamp; i++) + for (i = 0; i < xi_h.numSamples; i++) { - s = &instr[editor.curInstr]->samp[i]; - - // if a sample has both forward loop and pingpong loop set, make it pingpong loop only (FT2 mixer behavior) - if ((s->typ & 3) == 3) - s->typ &= 0xFE; + s = &instr[editor.curInstr]->smp[i]; - if (s->len > 0) + if (s->length <= 0) + { + s->length = 0; + s->loopStart = 0; + s->loopLength = 0; + s->flags = 0; + } + else { - s->pek = NULL; - s->origPek = (int8_t *)malloc(s->len + LOOP_FIX_LEN); - if (s->origPek == NULL) + const int32_t lengthInFile = s->length; + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + bool stereoSample = !!(s->flags & SAMPLE_STEREO); + + if (sample16Bit) // we use units of samples (not bytes like in FT2) + { + s->length >>= 1; + s->loopStart >>= 1; + s->loopLength >>= 1; + } + + if (s->length > MAX_SAMPLE_LEN) + s->length = MAX_SAMPLE_LEN; + + if (!allocateSmpData(s, s->length, sample16Bit)) { freeInstr(editor.curInstr); resumeAudio(); @@ -3222,9 +3251,8 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - s->pek = s->origPek + SMP_DAT_OFFSET; - - if (fread(s->pek, s->len, 1, f) != 1) + const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s); + if (fread(s->dataPtr, sampleLengthInBytes, 1, f) != 1) { freeInstr(editor.curInstr); resumeAudio(); @@ -3232,29 +3260,23 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - delta2Samp(s->pek, s->len, s->typ); // stereo samples are handled here + if (sampleLengthInBytes < lengthInFile) + fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR); + + delta2Samp(s->dataPtr, s->length, s->flags); // stereo samples are handled here // check if it was a stereo sample - if (s->typ & 32) + if (stereoSample) { - s->typ &= ~32; - - s->len >>= 1; - s->repL >>= 1; - s->repS >>= 1; + s->flags &= ~SAMPLE_STEREO; - newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } + s->length >>= 1; + s->loopStart >>= 1; + s->loopLength >>= 1; + reallocateSmpData(s, s->length, sample16Bit); stereoWarning = true; } - - checkSampleRepeat(s); - fixSample(s); } } @@ -3264,20 +3286,22 @@ static int32_t SDLCALL loadInstrThread(void *ptr) { rewind(f); - fread(&ih_PAT, 1, sizeof (instrPATHeaderTyp), f); - if (!memcmp(ih_PAT.id, "GF1PATCH110\0ID#000002\0", 22)) + fread(&pat_h, 1, sizeof (patHdr_t), f); + if (!memcmp(pat_h.ID, "GF1PATCH110\0ID#000002\0", 22)) { - // PAT - Gravis Ultrasound GF1 patch + // PAT - Gravis Ultrasound patch - if (ih_PAT.antSamp == 0) - ih_PAT.antSamp = 1; // to some patch makers, 0 means 1 + if (pat_h.numSamples == 0) + pat_h.numSamples = 1; // to some patch makers, 0 means 1 - if (ih_PAT.layers > 1 || ih_PAT.antSamp > MAX_SMP_PER_INST) + if (pat_h.layers > 1 || pat_h.numSamples > MAX_SMP_PER_INST) { okBoxThreadSafe(0, "System message", "Incompatible instrument!"); goto loadDone; } + numLoadedSamples = pat_h.numSamples; + pauseAudio(); freeInstr(editor.curInstr); @@ -3287,15 +3311,15 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - memset(song.instrName[editor.curInstr], 0, 22 + 1); - memcpy(song.instrName[editor.curInstr], ih_PAT.instrName, 16); + memcpy(song.instrName[editor.curInstr], pat_h.instrName, 16); + song.instrName[editor.curInstr][16] = '\0'; - for (i = 0; i < ih_PAT.antSamp; i++) + for (i = 0; i < pat_h.numSamples; i++) { - s = &instr[editor.curInstr]->samp[i]; + s = &instr[editor.curInstr]->smp[i]; ins = instr[editor.curInstr]; - if (fread(&ih_PATWave, 1, sizeof (ih_PATWave), f) != sizeof (ih_PATWave)) + if (fread(&patWave_h, 1, sizeof (patWave_h), f) != sizeof (patWave_h)) { freeInstr(editor.curInstr); resumeAudio(); @@ -3303,9 +3327,21 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - s->pek = NULL; - s->origPek = (int8_t *)malloc(ih_PATWave.waveSize + LOOP_FIX_LEN); - if (s->origPek == NULL) + const int32_t lengthInFile = patWave_h.sampleLength; + + bool sample16Bit = !!(patWave_h.flags & 1); + + s->length = lengthInFile; + if (sample16Bit) + { + s->flags |= SAMPLE_16BIT; + s->length >>= 1; + } + + if (s->length > MAX_SAMPLE_LEN) + s->length = MAX_SAMPLE_LEN; + + if (!allocateSmpData(s, s->length, sample16Bit)) { freeInstr(editor.curInstr); resumeAudio(); @@ -3313,75 +3349,54 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - s->pek = s->origPek + SMP_DAT_OFFSET; - if (i == 0) { - ins->vibSweep = ih_PATWave.vibSweep; - - ins->vibRate = (ih_PATWave.vibRate + 2) >> 2; - if (ins->vibRate > 0x3F) - ins->vibRate = 0x3F; - - ins->vibDepth = (ih_PATWave.vibDepth + 1) >> 1; - if (ins->vibDepth > 0x0F) - ins->vibDepth = 0x0F; + ins->vibSweep = patWave_h.vibSweep; + ins->vibRate = (patWave_h.vibRate + 2) >> 2; // rounded + ins->vibDepth = (patWave_h.vibDepth + 1) >> 1; // rounded } - s = &instr[editor.curInstr]->samp[i]; + s = &instr[editor.curInstr]->smp[i]; - memcpy(s->name, ih_PATWave.name, 7); + memcpy(s->name, patWave_h.name, 7); + s->name[7] = '\0'; - s->typ = (ih_PATWave.mode & 1) << 4; // 16-bit flag - if (ih_PATWave.mode & 4) // loop enabled? + if (patWave_h.flags & 4) // loop enabled? { - if (ih_PATWave.mode & 8) - s->typ |= 2; // pingpong loop + if (patWave_h.flags & 8) + s->flags |= LOOP_BIDI; else - s->typ |= 1; // forward loop + s->flags |= LOOP_FWD; } - s->pan = ((ih_PATWave.pan << 4) & 0xF0) | (ih_PATWave.pan & 0xF); + s->panning = ((patWave_h.panning << 4) & 0xF0) | (patWave_h.panning & 0xF); + s->loopStart = patWave_h.loopStart; + s->loopLength = patWave_h.loopEnd - patWave_h.loopStart; - if (s->typ & 16) + if (sample16Bit) { - ih_PATWave.waveSize &= 0xFFFFFFFE; - ih_PATWave.repS &= 0xFFFFFFFE; - ih_PATWave.repE &= 0xFFFFFFFE; + s->loopStart >>= 1; + s->loopLength >>= 1; } - s->len = ih_PATWave.waveSize; - if (s->len > MAX_SAMPLE_LEN) - s->len = MAX_SAMPLE_LEN; - - s->repS = ih_PATWave.repS; - if (s->repS > s->len) - s->repS = 0; - - s->repL = ih_PATWave.repE - ih_PATWave.repS; - if (s->repL < 0) - s->repL = 0; - - if (s->repS+s->repL > s->len) - s->repL = s->len - s->repS; - - const double dFreq = (1.0 + (ih_PATWave.fineTune / 512.0)) * ih_PATWave.sampleRate; - int32_t freq = (const int32_t)(dFreq + 0.5); - tuneSample(s, freq, audio.linearPeriodsFlag); + const double dFreq = (1.0 + (patWave_h.finetune / 512.0)) * patWave_h.sampleRate; + tuneSample(s, (int32_t)(dFreq + 0.5), audio.linearPeriodsFlag); - a = s->relTon - (getPATNote(ih_PATWave.rootFrq) - (12 * 3)); - s->relTon = (uint8_t)CLAMP(a, -48, 71); + a = getPATNote(patWave_h.rootFrq) - (12 * 3); + s->relativeNote -= (uint8_t)a; - a = getPATNote(ih_PATWave.lowFrq); - b = getPATNote(ih_PATWave.highFreq); + a = getPATNote(patWave_h.lowFrq); + b = getPATNote(patWave_h.highFreq); a = CLAMP(a, 0, 95); b = CLAMP(b, 0, 95); for (j = a; j <= b; j++) - ins->ta[j] = (uint8_t)i; + ins->note2SampleLUT[j] = (uint8_t)i; - if (fread(s->pek, ih_PATWave.waveSize, 1, f) != 1) + const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s); + size_t bytesRead = fread(s->dataPtr, sampleLengthInBytes, 1, f); + if (bytesRead != 1) { freeInstr(editor.curInstr); resumeAudio(); @@ -3389,15 +3404,16 @@ static int32_t SDLCALL loadInstrThread(void *ptr) goto loadDone; } - if (ih_PATWave.mode & 2) + if (sampleLengthInBytes < lengthInFile) + fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR); + + if (patWave_h.flags & 2) // unsigned samples? { - if (s->typ & 16) - conv16BitSample(s->pek, s->len, false); + if (sample16Bit) + conv16BitSample(s->dataPtr, s->length, false); else - conv8BitSample(s->pek, s->len, false); + conv8BitSample(s->dataPtr, s->length, false); } - - fixSample(s); } resumeAudio(); @@ -3407,17 +3423,32 @@ static int32_t SDLCALL loadInstrThread(void *ptr) loadDone: fclose(f); - fixInstrAndSampleNames(editor.curInstr); + numLoadedSamples = CLAMP(numLoadedSamples, 1, MAX_SMP_PER_INST); + + ins = instr[editor.curInstr]; + if (ins != NULL) + { + sanitizeInstrument(ins); + for (i = 0; i < numLoadedSamples; i++) + { + s = &ins->smp[i]; + sanitizeSample(s); + + if (s->dataPtr != NULL) + fixSample(s); + } + + fixInstrAndSampleNames(editor.curInstr); + } editor.updateCurInstr = true; // setMouseBusy(false) is called in the input/video thread when done - if (ih.antSamp > MAX_SMP_PER_INST) + if (numLoadedSamples > MAX_SMP_PER_INST) okBoxThreadSafe(0, "System message", "Warning: The instrument contained >16 samples. The extra samples were discarded!"); if (stereoWarning) okBoxThreadSafe(0, "System message", "Warning: The instrument contained stereo sample(s). They were mixed to mono!"); return true; - (void)ptr; } diff --git a/src/ft2_inst_ed.h b/src/ft2_inst_ed.h @@ -7,10 +7,9 @@ #include "ft2_unicode.h" void drawC4Rate(void); - +void sanitizeInstrument(instr_t *ins); bool fileIsInstr(UNICHAR *filenameU); - -void saveInstr(UNICHAR *filenameU, int16_t nr); +void saveInstr(UNICHAR *filenameU, int16_t insNum); void loadInstr(UNICHAR *filenameU); void copyInstr(void); // dstInstr = srcInstr void xchgInstr(void); // dstInstr <-> srcInstr @@ -43,10 +42,10 @@ void panPreDef3(void); void panPreDef4(void); void panPreDef5(void); void panPreDef6(void); -void relToneOctUp(void); -void relToneOctDown(void); -void relToneUp(void); -void relToneDown(void); +void relativeNoteOctUp(void); +void relativeNoteOctDown(void); +void relativeNoteUp(void); +void relativeNoteDown(void); void volEnvAdd(void); void volEnvDel(void); void volEnvSusUp(void); diff --git a/src/ft2_keyboard.c b/src/ft2_keyboard.c @@ -4,6 +4,8 @@ #endif #include <stdint.h> +#include <stdbool.h> +#include <string.h> #include "ft2_header.h" #include "ft2_keyboard.h" #include "ft2_gui.h" @@ -43,7 +45,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode); int8_t scancodeKeyToNote(SDL_Scancode scancode) { if (scancode == SDL_SCANCODE_CAPSLOCK || scancode == SDL_SCANCODE_NONUSBACKSLASH) - return 97; // key off + return NOTE_OFF; // translate key to note int8_t note = 0; @@ -161,7 +163,7 @@ void keyDownHandler(SDL_Scancode scancode, SDL_Keycode keycode, bool keyWasRepea static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) { // if we're holding numpad plus but not pressing bank keys, don't check any other key - if (keyb.numPadPlusPressed) + if (keyb.numPadPlusPressed && !keyb.leftCtrlPressed) { if (scanKey != SDL_SCANCODE_NUMLOCKCLEAR && scanKey != SDL_SCANCODE_KP_DIVIDE && scanKey != SDL_SCANCODE_KP_MULTIPLY && scanKey != SDL_SCANCODE_KP_MINUS) @@ -234,19 +236,49 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) case SDL_SCANCODE_KP_MINUS: { - if (keyb.numPadPlusPressed) + if (keyb.leftCtrlPressed) // non-FT2 feature: Decrease master volume by 16 { - if (editor.instrBankSwapped) - pbSetInstrBank16(); + if (config.masterVol >= 16) + config.masterVol -= 16; else - pbSetInstrBank8(); + config.masterVol = 0; + + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); + if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES) + showConfigScreen(); } else { - if (editor.instrBankSwapped) - pbSetInstrBank12(); + if (keyb.numPadPlusPressed) + { + if (editor.instrBankSwapped) + pbSetInstrBank16(); + else + pbSetInstrBank8(); + } + else + { + if (editor.instrBankSwapped) + pbSetInstrBank12(); + else + pbSetInstrBank4(); + } + } + } + return; + + case SDL_SCANCODE_KP_PLUS: + { + if (keyb.leftCtrlPressed) // non-FT2 feature: Increase master volume by 16 + { + if (config.masterVol <= 256-16) + config.masterVol += 16; else - pbSetInstrBank4(); + config.masterVol = 256; + + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); + if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_IO_DEVICES) + showConfigScreen(); } } return; @@ -267,6 +299,7 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (okBox(1, "System request", "Clear instrument?") == 1) { freeInstr(editor.curInstr); + memset(song.instrName[editor.curInstr], 0, sizeof(song.instrName[editor.curInstr])); updateNewInstrument(); setSongModifiedFlag(); } @@ -290,18 +323,18 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (keyb.leftShiftPressed) { // decrease edit skip - if (editor.ID_Add == 0) - editor.ID_Add = 16; + if (editor.editRowSkip == 0) + editor.editRowSkip = 16; else - editor.ID_Add--; + editor.editRowSkip--; } else { // increase edit skip - if (editor.ID_Add == 16) - editor.ID_Add = 0; + if (editor.editRowSkip == 16) + editor.editRowSkip = 0; else - editor.ID_Add++; + editor.editRowSkip++; } if (!ui.nibblesShown && !ui.configScreenShown && @@ -459,13 +492,13 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) { lockAudio(); - song.pattPos = editor.ptnJumpPos[0]; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; + song.row = editor.ptnJumpPos[0]; + if (song.row >= song.currNumRows) + song.row = song.currNumRows - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -477,13 +510,13 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) { lockAudio(); - song.pattPos = editor.ptnJumpPos[1]; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; + song.row = editor.ptnJumpPos[1]; + if (song.row >= song.currNumRows) + song.row = song.currNumRows - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -495,13 +528,13 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) { lockAudio(); - song.pattPos = editor.ptnJumpPos[2]; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; + song.row = editor.ptnJumpPos[2]; + if (song.row >= song.currNumRows) + song.row = song.currNumRows - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -513,13 +546,13 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) { lockAudio(); - song.pattPos = editor.ptnJumpPos[3]; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; + song.row = editor.ptnJumpPos[3]; + if (song.row >= song.currNumRows) + song.row = song.currNumRows - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -585,14 +618,14 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (audioWasntLocked) lockAudio(); - if (song.pattPos >= 16) - song.pattPos -= 16; + if (song.row >= 16) + song.row -= 16; else - song.pattPos = 0; + song.row = 0; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -607,14 +640,14 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (audioWasntLocked) lockAudio(); - if (song.pattPos < (song.pattLen - 16)) - song.pattPos += 16; + if (song.row < song.currNumRows-16) + song.row += 16; else - song.pattPos = song.pattLen - 1; + song.row = song.currNumRows-1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -629,10 +662,10 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (audioWasntLocked) lockAudio(); - song.pattPos = 0; + song.row = 0; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -647,10 +680,10 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) if (audioWasntLocked) lockAudio(); - song.pattPos = pattLens[song.pattNr] - 1; + song.row = patternNumRows[song.pattNum] - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -693,7 +726,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode) } else if (keyb.leftShiftPressed) { - editor.ptnJumpPos[0] = (uint8_t)editor.pattPos; + editor.ptnJumpPos[0] = (uint8_t)editor.row; return true; } } @@ -708,7 +741,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode) } else if (keyb.leftShiftPressed) { - editor.ptnJumpPos[1] = (uint8_t)editor.pattPos; + editor.ptnJumpPos[1] = (uint8_t)editor.row; return true; } } @@ -723,7 +756,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode) } else if (keyb.leftShiftPressed) { - editor.ptnJumpPos[2] = (uint8_t)editor.pattPos; + editor.ptnJumpPos[2] = (uint8_t)editor.row; return true; } } @@ -738,7 +771,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode) } else if (keyb.leftShiftPressed) { - editor.ptnJumpPos[3] = (uint8_t)editor.pattPos; + editor.ptnJumpPos[3] = (uint8_t)editor.row; return true; } } @@ -793,7 +826,7 @@ static bool checkModifiedKeys(SDL_Keycode keycode) pattMark.markX1 = cursor.ch; pattMark.markX2 = pattMark.markX1; pattMark.markY1 = 0; - pattMark.markY2 = pattLens[editor.editPattern]; + pattMark.markY2 = patternNumRows[editor.editPattern]; ui.updatePatternEditor = true; } diff --git a/src/ft2_main.c b/src/ft2_main.c @@ -22,7 +22,7 @@ #include "ft2_config.h" #include "ft2_sample_ed.h" #include "ft2_diskop.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_about.h" #include "ft2_pattern_ed.h" #include "ft2_module_loader.h" @@ -180,20 +180,23 @@ int main(int argc, char *argv[]) setupPerfFreq(); - if (!setupAudio(CONFIG_HIDE_ERRORS)) + if (!setupAudio(CONFIG_HIDE_ERRORS)) // can we open the audio device? { - // one LAST attempt (with default audio device and settings) - config.audioFreq = 48000; - - // try 48kHz 16-bit audio at 1024 samples - config.specialFlags &= ~(BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_2048); - config.specialFlags |= (BITDEPTH_16 + BUFFSIZE_1024); - + // nope, try with the default audio device setToDefaultAudioOutputDevice(); - if (!setupAudio(CONFIG_SHOW_ERRORS)) + + if (!setupAudio(CONFIG_HIDE_ERRORS)) // does it work this time? { - cleanUpAndExit(); // still no go... - return 1; + // nope, try safe values (44.1kHz 16-bit @ 1024 samples) + config.audioFreq = 44100; + config.specialFlags &= ~(BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_2048); + config.specialFlags |= (BITDEPTH_16 + BUFFSIZE_1024); + + if (!setupAudio(CONFIG_SHOW_ERRORS)) // this time it surely must work?! + { + cleanUpAndExit(); // well, nope! + return 1; + } } } @@ -269,10 +272,10 @@ static void initializeVars(void) memset(&song, 0, sizeof (song)); // used for scopes and sampling position line (sampler screen) - for (int32_t i = 0; i < MAX_VOICES; i++) + for (int32_t i = 0; i < MAX_CHANNELS; i++) { - lastChInstr[i].instrNr = 255; - lastChInstr[i].sampleNr = 255; + lastChInstr[i].instrNum = 255; + lastChInstr[i].smpNum = 255; } // now set data that must be initialized to non-zero values... @@ -289,7 +292,7 @@ static void initializeVars(void) mouse.lastUsedObjectID = OBJECT_ID_NONE; - editor.ID_Add = 1; + editor.editRowSkip = 1; editor.srcInstr = 1; editor.curInstr = 1; editor.curOctave = 4; diff --git a/src/ft2_midi.c b/src/ft2_midi.c @@ -68,10 +68,10 @@ static inline void midiInControlChange(uint8_t data1, uint8_t data2) if (recMIDIValidChn) // real FT2 forgot to check this here.. { - for (uint8_t i = 0; i < song.antChn; i++) + for (uint8_t i = 0; i < song.numChannels; i++) { - if (stm[i].midiVibDepth != 0 || editor.keyOnTab[i] != 0) - stm[i].midiVibDepth = midi.currMIDIVibDepth; + if (channel[i].midiVibDepth != 0 || editor.keyOnTab[i] != 0) + channel[i].midiVibDepth = midi.currMIDIVibDepth; } } @@ -88,8 +88,8 @@ static inline void midiInPitchBendChange(uint8_t data1, uint8_t data2) midi.currMIDIPitch = pitch; if (recMIDIValidChn) { - stmTyp *ch = stm; - for (uint8_t i = 0; i < song.antChn; i++, ch++) + channel_t *ch = channel; + for (uint8_t i = 0; i < song.numChannels; i++, ch++) { if (ch->midiPitch != 0 || editor.keyOnTab[i] != 0) ch->midiPitch = midi.currMIDIPitch; @@ -214,27 +214,26 @@ bool openMidiInDevice(uint32_t deviceID) return true; } -void recordMIDIEffect(uint8_t effTyp, uint8_t effData) +void recordMIDIEffect(uint8_t efx, uint8_t efxData) { // only handle this in record mode if (!midi.enable || (playMode != PLAYMODE_RECSONG && playMode != PLAYMODE_RECPATT)) return; - const int16_t nr = editor.editPattern; if (config.multiRec) { - tonTyp *note = &patt[nr][editor.pattPos * MAX_VOICES]; - for (int32_t i = 0; i < song.antChn; i++, note++) + note_t *p = &pattern[editor.editPattern][editor.row * MAX_CHANNELS]; + for (int32_t i = 0; i < song.numChannels; i++, p++) { if (config.multiRecChn[i] && editor.chnMode[i]) { - if (!allocatePattern(nr)) + if (!allocatePattern(editor.editPattern)) return; - if (note->effTyp == 0) + if (p->efx == 0) { - note->effTyp = effTyp; - note->eff = effData; + p->efx = efx; + p->efxData = efxData; setSongModifiedFlag(); } } @@ -242,15 +241,15 @@ void recordMIDIEffect(uint8_t effTyp, uint8_t effData) } else { - if (!allocatePattern(nr)) + if (!allocatePattern(editor.editPattern)) return; - tonTyp *note = &patt[nr][(editor.pattPos * MAX_VOICES) + cursor.ch]; - if (note->effTyp != effTyp || note->eff != effData) + note_t *p = &pattern[editor.editPattern][(editor.row * MAX_CHANNELS) + cursor.ch]; + if (p->efx != efx || p->efxData != efxData) setSongModifiedFlag(); - note->effTyp = effTyp; - note->eff = effData; + p->efx = efx; + p->efxData = efxData; } } diff --git a/src/ft2_midi.h b/src/ft2_midi.h @@ -25,7 +25,7 @@ void closeMidiInDevice(void); void freeMidiIn(void); bool initMidiIn(void); bool openMidiInDevice(uint32_t deviceID); -void recordMIDIEffect(uint8_t effTyp, uint8_t effData); +void recordMIDIEffect(uint8_t efx, uint8_t efxData); bool saveMidiInputDeviceToConfig(void); bool setMidiInputDeviceFromConfig(void); void freeMidiInputDeviceList(void); diff --git a/src/ft2_module_loader.c b/src/ft2_module_loader.c @@ -10,7 +10,7 @@ #include <unistd.h> #endif #include "ft2_header.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_trim.h" #include "ft2_inst_ed.h" #include "ft2_sample_ed.h" @@ -56,10 +56,10 @@ char *supportedModExtensions[] = // globals for module loaders volatile bool tmpLinearPeriodsFlag; -int16_t pattLensTmp[MAX_PATTERNS]; -tonTyp *pattTmp[MAX_PATTERNS]; -instrTyp *instrTmp[1+256]; -songTyp songTmp; +int16_t patternNumRowsTmp[MAX_PATTERNS]; +note_t *patternTmp[MAX_PATTERNS]; +instr_t *instrTmp[1+256]; +song_t songTmp; // -------------------------- static volatile bool musicIsLoading, moduleLoaded, moduleFailedToLoad; @@ -214,12 +214,12 @@ loadError: static void clearTmpModule(void) { - memset(pattTmp, 0, sizeof (pattTmp)); + memset(patternTmp, 0, sizeof (patternTmp)); memset(instrTmp, 0, sizeof (instrTmp)); memset(&songTmp, 0, sizeof (songTmp)); for (uint32_t i = 0; i < MAX_PATTERNS; i++) - pattLensTmp[i] = 64; + patternNumRowsTmp[i] = 64; } static int32_t SDLCALL loadMusicThread(void *ptr) @@ -277,33 +277,33 @@ bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay) return false; } -bool allocateTmpPatt(int32_t nr, uint16_t rows) +bool allocateTmpPatt(int32_t pattNum, uint16_t numRows) { - pattTmp[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); - if (pattTmp[nr] == NULL) + patternTmp[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); + if (patternTmp[pattNum] == NULL) return false; - pattLensTmp[nr] = rows; + patternNumRowsTmp[pattNum] = numRows; return true; } -bool allocateTmpInstr(int16_t nr) +bool allocateTmpInstr(int16_t insNum) { - if (instrTmp[nr] != NULL) + if (instrTmp[insNum] != NULL) return false; // already allocated - instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp)); - if (p == NULL) + instr_t *ins = (instr_t *)calloc(1, sizeof (instr_t)); + if (ins == NULL) return false; - sampleTyp *s = p->samp; + sample_t *s = ins->smp; for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) { - s->pan = 128; - s->vol = 64; + s->panning = 128; + s->volume = 64; } - instrTmp[nr] = p; + instrTmp[insNum] = ins; return true; } @@ -312,10 +312,10 @@ static void freeTmpModule(void) // called on module load error // free all patterns for (int32_t i = 0; i < MAX_PATTERNS; i++) { - if (pattTmp[i] != NULL) + if (patternTmp[i] != NULL) { - free(pattTmp[i]); - pattTmp[i] = NULL; + free(patternTmp[i]); + patternTmp[i] = NULL; } } @@ -325,25 +325,22 @@ static void freeTmpModule(void) // called on module load error if (instrTmp[i] == NULL) continue; - sampleTyp *s = instrTmp[i]->samp; + sample_t *s = instrTmp[i]->smp; for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++) - { - if (s->origPek != NULL) - free(s->origPek); - } + freeSmpData(s); free(instrTmp[i]); instrTmp[i] = NULL; } } -bool tmpPatternEmpty(uint16_t nr) +bool tmpPatternEmpty(uint16_t pattNum) { - if (pattTmp[nr] == NULL) + if (patternTmp[pattNum] == NULL) return true; - uint8_t *scanPtr = (uint8_t *)pattTmp[nr]; - const uint32_t scanLen = pattLensTmp[nr] * TRACK_WIDTH; + uint8_t *scanPtr = (uint8_t *)patternTmp[pattNum]; + const uint32_t scanLen = patternNumRowsTmp[pattNum] * TRACK_WIDTH; for (uint32_t i = 0; i < scanLen; i++) { @@ -354,16 +351,16 @@ bool tmpPatternEmpty(uint16_t nr) return true; } -void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn) +void clearUnusedChannels(note_t *pattPtr, int16_t numRows, int32_t numChannels) { - if (p == NULL || antChn >= MAX_VOICES) + if (pattPtr == NULL || numChannels >= MAX_CHANNELS) return; - const int32_t width = sizeof (tonTyp) * (MAX_VOICES - antChn); + const int32_t width = sizeof (note_t) * (MAX_CHANNELS - numChannels); - tonTyp *ptr = &p[antChn]; - for (int32_t i = 0; i < pattLen; i++, ptr += MAX_VOICES) - memset(ptr, 0, width); + note_t *p = &pattPtr[numChannels]; + for (int32_t i = 0; i < numRows; i++, p += MAX_CHANNELS) + memset(p, 0, width); } // called from input/video thread after the module was done loading @@ -388,12 +385,12 @@ static void setupLoadedModule(void) // copy over new pattern pointers and lengths for (int32_t i = 0; i < MAX_PATTERNS; i++) { - patt[i] = pattTmp[i]; - pattLens[i] = pattLensTmp[i]; + pattern[i] = patternTmp[i]; + patternNumRows[i] = patternNumRowsTmp[i]; } // copy over song struct - memcpy(&song, &songTmp, sizeof (songTyp)); + memcpy(&song, &songTmp, sizeof (song_t)); fixSongName(); // copy over new instruments (includes sample pointers) @@ -404,12 +401,13 @@ static void setupLoadedModule(void) if (instr[i] != NULL) { + sanitizeInstrument(instr[i]); for (int32_t j = 0; j < MAX_SMP_PER_INST; j++) { - sampleTyp *s = &instr[i]->samp[j]; + sample_t *s = &instr[i]->smp[j]; - checkSampleRepeat(s); - if (s->pek != NULL) + sanitizeSample(s); + if (s->dataPtr != NULL) fixSample(s); // prepare sample for branchless linear interpolation } } @@ -417,69 +415,67 @@ static void setupLoadedModule(void) // we are the owners of the allocated memory ptrs set by the loader thread now - if (song.antChn == 0) - song.antChn = 2; - // support non-even channel numbers - if (song.antChn & 1) + if (song.numChannels & 1) { - song.antChn++; - if (song.antChn > MAX_VOICES) - song.antChn = MAX_VOICES; + song.numChannels++; + if (song.numChannels > MAX_CHANNELS) + song.numChannels = MAX_CHANNELS; } - if (song.len == 0) - song.len = 1; + song.numChannels = CLAMP(song.numChannels, 2, MAX_CHANNELS); + song.songLength = CLAMP(song.songLength, 1, MAX_ORDERS); + song.BPM = CLAMP(song.BPM, MIN_BPM, MAX_BPM); + song.initialSpeed = song.speed = CLAMP(song.speed, 1, MAX_SPEED); - if (song.repS >= song.len) - song.repS = 0; + if (song.songLoopStart >= song.songLength) + song.songLoopStart = 0; - song.globVol = 64; + song.globalVolume = 64; // remove overflown stuff in pattern data (FT2 doesn't do this) for (int32_t i = 0; i < MAX_PATTERNS; i++) { - if (pattLens[i] <= 0) - pattLens[i] = 64; + if (patternNumRows[i] <= 0) + patternNumRows[i] = 64; - if (pattLens[i] > MAX_PATT_LEN) - pattLens[i] = MAX_PATT_LEN; + if (patternNumRows[i] > MAX_PATT_LEN) + patternNumRows[i] = MAX_PATT_LEN; - tonTyp *p = patt[i]; - if (p == NULL) + if (pattern[i] == NULL) continue; - tonTyp *note = p; - for (int32_t j = 0; j < MAX_PATT_LEN * MAX_VOICES; j++, note++) + note_t *p = pattern[i]; + for (int32_t j = 0; j < MAX_PATT_LEN * MAX_CHANNELS; j++, p++) { - if (note->ton > 97) - note->ton = 0; + if (p->note > 97) + p->note = 0; - if (note->instr > 128) - note->instr = 0; + if (p->instr > 128) + p->instr = 0; - if (note->effTyp > 35) + if (p->efx > 35) { - note->effTyp = 0; - note->eff = 0; + p->efx = 0; + p->efxData = 0; } } } - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5); setScrollBarPos(SB_POS_ED, 0, false); resetChannels(); setPos(0, 0, true); - P_SetSpeed(song.speed); + setMixerBPM(song.BPM); editor.tmpPattern = editor.editPattern; // set kludge variable + editor.BPM = song.BPM; editor.speed = song.speed; - editor.tempo = song.tempo; - editor.timer = song.timer; - editor.globalVol = song.globVol; + editor.tick = song.tick; + editor.globalVolume = song.globalVolume; - setFrqTab(tmpLinearPeriodsFlag); + setFrequencyTable(tmpLinearPeriodsFlag); unlockMixerCallback(); @@ -688,6 +684,8 @@ void loadDroppedFile(char *fullPathUTF8, bool songModifiedCheck) } } + exitTextEditing(); + // pass UTF8 to these tests so that we can test file ending in ASCII/ANSI if (fileIsInstr(fullPathU)) diff --git a/src/ft2_module_loader.h b/src/ft2_module_loader.h @@ -5,10 +5,10 @@ #include "ft2_header.h" #include "ft2_unicode.h" -bool tmpPatternEmpty(uint16_t nr); -void clearUnusedChannels(tonTyp *p, int16_t pattLen, int32_t antChn); -bool allocateTmpInstr(int16_t nr); -bool allocateTmpPatt(int32_t nr, uint16_t rows); +bool tmpPatternEmpty(uint16_t pattNum); +void clearUnusedChannels(note_t *p, int16_t numRows, int32_t numChannels); +bool allocateTmpInstr(int16_t insNum); +bool allocateTmpPatt(int32_t pattNum, uint16_t numRows); void loadMusic(UNICHAR *filenameU); bool loadMusicUnthreaded(UNICHAR *filenameU, bool autoPlay); bool handleModuleLoadFromArg(int argc, char **argv); @@ -19,7 +19,7 @@ void handleLoadMusicEvents(void); extern char *supportedModExtensions[]; extern volatile bool tmpLinearPeriodsFlag; -extern int16_t pattLensTmp[MAX_PATTERNS]; -extern tonTyp *pattTmp[MAX_PATTERNS]; -extern instrTyp *instrTmp[1+256]; -extern songTyp songTmp; +extern int16_t patternNumRowsTmp[MAX_PATTERNS]; +extern note_t *patternTmp[MAX_PATTERNS]; +extern instr_t *instrTmp[1+256]; +extern song_t songTmp; diff --git a/src/ft2_module_saver.c b/src/ft2_module_saver.c @@ -14,16 +14,11 @@ #include "ft2_tables.h" #include "ft2_structs.h" -/* These savers are directly ported, so they should act identical to FT2 -** except for some very minor changes. -*/ - +static int8_t smpChunkBuf[1024]; +static uint8_t packedPattData[65536], modPattData[64*32*4]; static SDL_Thread *thread; -static uint8_t packedPattData[65536]; -static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows); - -static const char modSig[32][5] = +static const char modIDs[32][5] = { "1CHN", "2CHN", "3CHN", "4CHN", "5CHN", "6CHN", "7CHN", "8CHN", "9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH", @@ -31,16 +26,18 @@ static const char modSig[32][5] = "25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH" }; +static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows); + bool saveXM(UNICHAR *filenameU) { int16_t i, j, k, a; size_t result; - songHeaderTyp h; - patternHeaderTyp ph; - instrTyp *ins; - instrHeaderTyp ih; - sampleTyp *s; - sampleHeaderTyp *dst; + xmHdr_t h; + xmPatHdr_t ph; + instr_t *ins; + xmInsHdr_t ih; + sample_t *s; + xmSmpHdr_t *dst; FILE *f = UNICHAR_FOPEN(filenameU, "wb"); if (f == NULL) @@ -49,39 +46,56 @@ bool saveXM(UNICHAR *filenameU) return false; } - memcpy(h.sig, "Extended Module: ", 17); - memset(h.name, ' ', 20); - h.name[20] = 0x1A; - memcpy(h.name, song.name, strlen(song.name)); - memcpy(h.progName, PROG_NAME_STR, 20); - h.ver = 0x0104; + memcpy(h.ID, "Extended Module: ", 17); + + // song name + int32_t nameLength = (int32_t)strlen(song.name); + if (nameLength > 20) + nameLength = 20; + + memset(h.name, ' ', 20); // yes, FT2 pads the name with spaces + if (nameLength > 0) + memcpy(h.name, song.name, nameLength); + + h.x1A = 0x1A; + + // program/tracker name + nameLength = (int32_t)strlen(PROG_NAME_STR); + if (nameLength > 20) + nameLength = 20; + + memset(h.progName, ' ', 20); // yes, FT2 pads the name with spaces + if (nameLength > 0) + memcpy(h.progName, PROG_NAME_STR, nameLength); + + h.version = 0x0104; h.headerSize = 20 + 256; - h.len = song.len; - h.repS = song.repS; - h.antChn = (uint16_t)song.antChn; - h.defTempo = song.tempo; - h.defSpeed = song.speed; + h.numOrders = song.songLength; + h.songLoopStart = song.songLoopStart; + h.numChannels = (uint16_t)song.numChannels; + h.speed = song.speed; + h.BPM = song.BPM; // count number of patterns - int16_t ap = MAX_PATTERNS; + i = MAX_PATTERNS; do { - if (patternEmpty(ap - 1)) - ap--; + if (patternEmpty(i-1)) + i--; else break; } - while (ap > 0); - h.antPtn = ap; + while (i > 0); + h.numPatterns = i; // count number of instruments - int16_t ai = 128; - while (ai > 0 && getUsedSamples(ai) == 0 && song.instrName[ai][0] == '\0') - ai--; - h.antInstrs = ai; + i = 128; + while (i > 0 && getUsedSamples(i) == 0 && song.instrName[i][0] == '\0') + i--; + h.numInstr = i; h.flags = audio.linearPeriodsFlag; - memcpy(h.songTab, song.songTab, sizeof (song.songTab)); + memcpy(h.orders, song.orders, 256); if (fwrite(&h, sizeof (h), 1, f) != 1) { @@ -90,27 +104,27 @@ bool saveXM(UNICHAR *filenameU) return false; } - for (i = 0; i < ap; i++) + for (i = 0; i < h.numPatterns; i++) { if (patternEmpty(i)) { - if (patt[i] != NULL) + if (pattern[i] != NULL) { - free(patt[i]); - patt[i] = NULL; + free(pattern[i]); + pattern[i] = NULL; } - pattLens[i] = 64; + patternNumRows[i] = 64; } - ph.patternHeaderSize = sizeof (patternHeaderTyp); - ph.pattLen = pattLens[i]; - ph.typ = 0; + ph.headerSize = sizeof (xmPatHdr_t); + ph.numRows = patternNumRows[i]; + ph.type = 0; - if (patt[i] == NULL) + if (pattern[i] == NULL) { - ph.dataLen = 0; - if (fwrite(&ph, ph.patternHeaderSize, 1, f) != 1) + ph.dataSize = 0; + if (fwrite(&ph, ph.headerSize, 1, f) != 1) { fclose(f); okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); @@ -119,10 +133,10 @@ bool saveXM(UNICHAR *filenameU) } else { - ph.dataLen = packPatt(packedPattData, (uint8_t *)patt[i], pattLens[i]); + ph.dataSize = packPatt(packedPattData, (uint8_t *)pattern[i], patternNumRows[i]); - result = fwrite(&ph, ph.patternHeaderSize, 1, f); - result += fwrite(packedPattData, ph.dataLen, 1, f); + result = fwrite(&ph, ph.headerSize, 1, f); + result += fwrite(packedPattData, ph.dataSize, 1, f); if (result != 2) // write was not OK { @@ -135,7 +149,7 @@ bool saveXM(UNICHAR *filenameU) memset(&ih, 0, sizeof (ih)); // important, clears reserved stuff - for (i = 1; i <= ai; i++) + for (i = 1; i <= h.numInstr; i++) { if (instr[i] == NULL) j = 0; @@ -144,35 +158,40 @@ bool saveXM(UNICHAR *filenameU) a = getUsedSamples(i); - memset(ih.name, 0, 22); - memcpy(ih.name, song.instrName[i], strlen(song.instrName[i])); + nameLength = (int32_t)strlen(song.instrName[i]); + if (nameLength > 22) + nameLength = 22; + + memset(ih.name, 0, 22); // pad with zero + if (nameLength > 0) + memcpy(ih.name, song.instrName[i], nameLength); - ih.typ = 0; - ih.antSamp = a; - ih.sampleSize = sizeof (sampleHeaderTyp); + ih.type = 0; + ih.numSamples = a; + ih.sampleSize = sizeof (xmSmpHdr_t); if (a > 0) { ins = instr[j]; - memcpy(ih.ta, ins->ta, 96); - memcpy(ih.envVP, ins->envVP, 12*2*sizeof(int16_t)); - memcpy(ih.envPP, ins->envPP, 12*2*sizeof(int16_t)); - ih.envVPAnt = ins->envVPAnt; - ih.envPPAnt = ins->envPPAnt; - ih.envVSust = ins->envVSust; - ih.envVRepS = ins->envVRepS; - ih.envVRepE = ins->envVRepE; - ih.envPSust = ins->envPSust; - ih.envPRepS = ins->envPRepS; - ih.envPRepE = ins->envPRepE; - ih.envVTyp = ins->envVTyp; - ih.envPTyp = ins->envPTyp; - ih.vibTyp = ins->vibTyp; + memcpy(ih.note2SampleLUT, ins->note2SampleLUT, 96); + memcpy(ih.volEnvPoints, ins->volEnvPoints, 12*2*sizeof(int16_t)); + memcpy(ih.panEnvPoints, ins->panEnvPoints, 12*2*sizeof(int16_t)); + ih.volEnvLength = ins->volEnvLength; + ih.panEnvLength = ins->panEnvLength; + ih.volEnvSustain = ins->volEnvSustain; + ih.volEnvLoopStart = ins->volEnvLoopStart; + ih.volEnvLoopEnd = ins->volEnvLoopEnd; + ih.panEnvSustain = ins->panEnvSustain; + ih.panEnvLoopStart = ins->panEnvLoopStart; + ih.panEnvLoopEnd = ins->panEnvLoopEnd; + ih.volEnvFlags = ins->volEnvFlags; + ih.panEnvFlags = ins->panEnvFlags; + ih.vibType = ins->vibType; ih.vibSweep = ins->vibSweep; ih.vibDepth = ins->vibDepth; ih.vibRate = ins->vibRate; - ih.fadeOut = ins->fadeOut; + ih.fadeout = ins->fadeout; ih.midiOn = ins->midiOn ? 1 : 0; ih.midiChannel = ins->midiChannel; ih.midiProgram = ins->midiProgram; @@ -182,26 +201,40 @@ bool saveXM(UNICHAR *filenameU) for (k = 0; k < a; k++) { - s = &instr[j]->samp[k]; - dst = &ih.samp[k]; - - dst->len = s->len; - dst->repS = s->repS; - dst->repL = s->repL; - dst->vol = s->vol; - dst->fine = s->fine; - dst->typ = s->typ; - dst->pan = s->pan; - dst->relTon = s->relTon; - - uint8_t nameLen = (uint8_t)strlen(s->name); - - dst->nameLen = nameLen; - memset(dst->name, ' ', 22); - memcpy(dst->name, s->name, nameLen); - - if (s->pek == NULL) - dst->len = 0; + s = &instr[j]->smp[k]; + dst = &ih.smp[k]; + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + + dst->length = s->length; + dst->loopStart = s->loopStart; + dst->loopLength = s->loopLength; + + if (sample16Bit) + { + dst->length <<= 1; + dst->loopStart <<= 1; + dst->loopLength <<= 1; + } + + dst->volume = s->volume; + dst->finetune = s->finetune; + dst->flags = s->flags; + dst->panning = s->panning; + dst->relativeNote = s->relativeNote; + + nameLength = (int32_t)strlen(s->name); + if (nameLength > 22) + nameLength = 22; + + dst->nameLength = (uint8_t)nameLength; + + memset(dst->name, ' ', 22); // yes, FT2 pads the name with spaces + if (nameLength > 0) + memcpy(dst->name, s->name, nameLength); + + if (s->dataPtr == NULL) + dst->length = 0; } } else @@ -209,7 +242,7 @@ bool saveXM(UNICHAR *filenameU) ih.instrSize = 22 + 11; } - if (fwrite(&ih, ih.instrSize + (a * sizeof (sampleHeaderTyp)), 1, f) != 1) + if (fwrite(&ih, ih.instrSize + (a * sizeof (xmSmpHdr_t)), 1, f) != 1) { fclose(f); okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); @@ -218,18 +251,18 @@ bool saveXM(UNICHAR *filenameU) for (k = 1; k <= a; k++) { - s = &instr[j]->samp[k-1]; - if (s->pek != NULL) + s = &instr[j]->smp[k-1]; + if (s->dataPtr != NULL) { - restoreSample(s); - samp2Delta(s->pek, s->len, s->typ); + unfixSample(s); + samp2Delta(s->dataPtr, s->length, s->flags); - result = fwrite(s->pek, 1, s->len, f); + result = fwrite(s->dataPtr, 1, SAMPLE_LENGTH_BYTES(s), f); - delta2Samp(s->pek, s->len, s->typ); + delta2Samp(s->dataPtr, s->length, s->flags); fixSample(s); - if (result != (size_t)s->len) // write not OK + if (result != (size_t)SAMPLE_LENGTH_BYTES(s)) // write not OK { fclose(f); okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); @@ -251,49 +284,67 @@ bool saveXM(UNICHAR *filenameU) static bool saveMOD(UNICHAR *filenameU) { - bool test, tooManyInstr, incompatEfx, noteUnderflow; - int8_t *srcPtr, *dstPtr; - uint8_t ton, inst, pattBuff[64*4*32]; - int16_t a, i, ap; - int32_t j, k, l1, l2, l3, writeLen, bytesToWrite, bytesWritten; - FILE *f; - instrTyp *ins; - sampleTyp *smp; - tonTyp *t; - songMOD31HeaderTyp hm; - - tooManyInstr = false; - incompatEfx = false; - noteUnderflow = false; + int16_t i; + int32_t j, k; + instr_t *ins; + sample_t *smp; + modHdr_t hdr; if (audio.linearPeriodsFlag) - okBoxThreadSafe(0, "System message", "Linear frequency table used!"); - - // sanity checking + okBoxThreadSafe(0, "System message", "Warning: Amiga frequency table isn't used!"); - test = false; - if (song.len > 128) - test = true; + int32_t songLength = song.songLength; + if (songLength > 128) + { + songLength = 128; + okBoxThreadSafe(0, "System message", "Warning: Song length is above 128!"); + } + + // calculate number of patterns referenced (max 128 orders) + int32_t numPatterns = 0; + for (i = 0; i < songLength; i++) + { + if (song.orders[i] > numPatterns) + numPatterns = song.orders[i]; + } + numPatterns++; - for (i = 100; i < 256; i++) + if (numPatterns > 100) { - if (patt[i] != NULL) - { - test = true; - break; - } + numPatterns = 100; + okBoxThreadSafe(0, "System message", "Warning: Song has more than 100 patterns!"); } - if (test) okBoxThreadSafe(0, "System message", "Too many patterns!"); + // check if song has more than 31 instruments for (i = 32; i <= 128; i++) { if (getRealUsedSamples(i) > 0) { - okBoxThreadSafe(0, "System message", "Too many instruments!"); + okBoxThreadSafe(0, "System message", "Warning: Song has more than 31 instruments!"); break; } } + // check if the first 31 samples have a length above 65534 samples + bool test = false; + bool test2 = false; + for (i = 1; i <= 31; i++) + { + ins = instr[i]; + if (ins == NULL) + continue; + + smp = &ins->smp[0]; + + if (smp->length > 131070) + test = true; + else if (smp->length > 65534) + test2 = true; + } + if (test) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths that are too long for the MOD format!"); + else if (test2) okBoxThreadSafe(0, "System message", "Warning: Song has sample lengths above 65534! Not all MOD players support this."); + + // check if XM instrument features are being used test = false; for (i = 1; i <= 31; i++) { @@ -301,7 +352,7 @@ static bool saveMOD(UNICHAR *filenameU) if (ins == NULL) continue; - smp = &ins->samp[0]; + smp = &ins->smp[0]; j = getRealUsedSamples(i); if (j > 1) @@ -312,133 +363,137 @@ static bool saveMOD(UNICHAR *filenameU) if (j == 1) { - if (smp->len > 65534 || ins->fadeOut != 0 || ins->envVTyp != 0 || ins->envPTyp != 0 || - (smp->typ & 3) == 2 || smp->relTon != 0 || ins->midiOn) + if (ins->fadeout != 0 || ins->volEnvFlags != 0 || ins->panEnvFlags != 0 || ins->vibRate > 0 || + GET_LOOPTYPE(smp->flags) == LOOP_BIDI || smp->relativeNote != 0 || ins->midiOn) { test = true; break; } } } - if (test) okBoxThreadSafe(0, "System message", "Incompatible instruments!"); + if (test) okBoxThreadSafe(0, "System message", "Warning: Song is using XM instrument features!"); + + bool tooLongPatterns = false; + bool tooManyInstr = false; + bool incompatEfx = false; + bool noteUnderflow = false; - for (i = 0; i < 99; i++) + for (i = 0; i < numPatterns; i++) { - if (patt[i] != NULL) + if (pattern[i] == NULL) + continue; + + if (patternNumRows[i] < 64) { - if (pattLens[i] != 64) - { - okBoxThreadSafe(0, "System message", "Unable to convert module. (Illegal pattern length)"); - return false; - } + okBoxThreadSafe(0, "System message", "Error: Pattern lengths can't be below 64! Module wasn't saved."); + return false; + } - for (j = 0; j < 64; j++) + if (patternNumRows[i] > 64) + tooLongPatterns = true; + + for (j = 0; j < 64; j++) + { + for (k = 0; k < song.numChannels; k++) { - for (k = 0; k < song.antChn; k++) - { - t = &patt[i][(j * MAX_VOICES) + k]; + note_t *p = &pattern[i][(j * MAX_CHANNELS) + k]; - if (t->instr > 31) - tooManyInstr = true; + if (p->instr > 31) + tooManyInstr = true; - if (t->effTyp > 15 || t->vol != 0) - incompatEfx = true; + if (p->efx > 0xF || p->vol != 0) + incompatEfx = true; - // added security that wasn't present in FT2 - if (t->ton > 0 && t->ton < 10) - noteUnderflow = true; - } + // added security that wasn't present in FT2 + if (p->note > 0 && p->note < 10) + noteUnderflow = true; } } } - if (tooManyInstr) okBoxThreadSafe(0, "System message", "Instrument(s) above 31 was found in pattern data!"); - if (incompatEfx) okBoxThreadSafe(0, "System message", "Incompatible effect(s) was found in pattern data!"); - if (noteUnderflow) okBoxThreadSafe(0, "System message", "Note(s) below A-0 were found in pattern data!"); - - // setup header buffer - memset(&hm, 0, sizeof (hm)); - memcpy(hm.name, song.name, sizeof (hm.name)); - hm.len = (uint8_t)song.len; - if (hm.len > 128) hm.len = 128; - hm.repS = (uint8_t)song.repS; - if (hm.repS > 127) hm.repS = 0; - memcpy(hm.songTab, song.songTab, song.len); - - // calculate number of patterns - ap = 0; - for (i = 0; i < song.len; i++) - { - if (song.songTab[i] > ap) - ap = song.songTab[i]; - } - if (song.antChn == 4) - memcpy(hm.sig, (ap > 64) ? "M!K!" : "M.K.", 4); + if (tooLongPatterns) okBoxThreadSafe(0, "System message", "Warning: Song has pattern lengths above 64!"); + if (tooManyInstr) okBoxThreadSafe(0, "System message", "Warning: Patterns have instrument numbers above 31!"); + if (incompatEfx) okBoxThreadSafe(0, "System message", "Warning: Patterns have incompatible effects!"); + if (noteUnderflow) okBoxThreadSafe(0, "System message", "Warning: Patterns have notes below A-0!"); + + // save module now + + memset(&hdr, 0, sizeof (hdr)); + + // song name + int32_t nameLength = (int32_t)strlen(song.name); + if (nameLength > 20) + nameLength = 20; + + memset(hdr.name, 0, 20); // pad with zeroes + if (nameLength > 0) + memcpy(hdr.name, song.name, nameLength); + + hdr.numOrders = (uint8_t)songLength; // pre-clamped to 0..128 + + hdr.songLoopStart = (uint8_t)song.songLoopStart; + if (hdr.songLoopStart >= hdr.numOrders) // repeat-point must be lower than the song length + hdr.songLoopStart = 0; + + memcpy(hdr.orders, song.orders, hdr.numOrders); + + if (song.numChannels == 4) + memcpy(hdr.ID, (numPatterns > 64) ? "M!K!" : "M.K.", 4); else - memcpy(hm.sig, modSig[song.antChn-1], 4); + memcpy(hdr.ID, modIDs[song.numChannels-1], 4); - // read sample information into header buffer + // fill MOD sample headers for (i = 1; i <= 31; i++) { - songMODInstrHeaderTyp *modIns = &hm.instr[i-1]; - - memcpy(modIns->name, song.instrName[i], sizeof (modIns->name)); - if (instr[i] != NULL && getRealUsedSamples(i) != 0) - { - smp = &instr[i]->samp[0]; + modSmpHdr_t *modSmp = &hdr.smp[i-1]; - l1 = smp->len >> 1; - l2 = smp->repS >> 1; - l3 = smp->repL >> 1; + nameLength = (int32_t)strlen(song.instrName[i]); + if (nameLength > 22) + nameLength = 22; - if (smp->typ & 16) - { - l1 >>= 1; - l2 >>= 1; - l3 >>= 1; - } + memset(modSmp->name, 0, 22); // pad with zeroes + if (nameLength > 0) + memcpy(modSmp->name, song.instrName[i], nameLength); - if (l1 > 32767) - l1 = 32767; + if (instr[i] != NULL && getRealUsedSamples(i) != 0) + { + smp = &instr[i]->smp[0]; - if (l2 > l1) - l2 = l1; + int32_t length = smp->length >> 1; + int32_t loopStart = smp->loopStart >> 1; + int32_t loopLength = smp->loopLength >> 1; - if (l2+l3 > l1) - l3 = l1 - l2; + // length/loopStart/loopLength are now in units of words - // FT2 bug-fix - if (l3 < 1) - { - l2 = 0; - l3 = 1; - } - - modIns->len = (uint16_t)(SWAP16(l1)); - modIns->fine = ((smp->fine + 128) >> 4) ^ 8; - modIns->vol = smp->vol; + if (length > UINT16_MAX) + length = UINT16_MAX; - if ((smp->typ & 3) == 0) + if (GET_LOOPTYPE(smp->flags) == LOOP_OFF) { - modIns->repS = 0; - modIns->repL = SWAP16(1); + loopStart = 0; + loopLength = 1; } - else + else // looped sample { - modIns->repS = (uint16_t)(SWAP16(l2)); - modIns->repL = (uint16_t)(SWAP16(l3)); + if (loopLength == 0) // ProTracker hates loopLengths of zero + loopLength = 1; + + if (loopStart+loopLength > length) + { + loopStart = 0; + loopLength = 1; + } } - } - // FT2 bugfix: never allow replen being below 2 (1) - if (SWAP16(modIns->repL) < 1) - { - modIns->repS = SWAP16(0); - modIns->repL = SWAP16(1); + modSmp->length = (uint16_t)SWAP16(length); + modSmp->finetune = FINETUNE_XM2MOD(smp->finetune); + modSmp->volume = smp->volume; + modSmp->loopStart = (uint16_t)SWAP16(loopStart); + modSmp->loopLength = (uint16_t)SWAP16(loopLength); } } - f = UNICHAR_FOPEN(filenameU, "wb"); + FILE *f = UNICHAR_FOPEN(filenameU, "wb"); if (f == NULL) { okBoxThreadSafe(0, "System message", "Error opening file for saving, is it in use?"); @@ -446,73 +501,73 @@ static bool saveMOD(UNICHAR *filenameU) } // write header - if (fwrite(&hm, 1, sizeof (hm), f) != sizeof (hm)) + if (fwrite(&hdr, 1, sizeof (hdr), f) != sizeof (hdr)) { okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); goto modSaveError; } // write pattern data - for (i = 0; i <= ap; i++) + const int32_t patternBytes = song.numChannels * 64 * 4; + for (i = 0; i < numPatterns; i++) { - if (patt[i] == NULL) + if (pattern[i] == NULL) // empty pattern { - // empty pattern - memset(pattBuff, 0, song.antChn * (64 * 4)); + memset(modPattData, 0, patternBytes); } else { - a = 0; + int32_t offs = 0; for (j = 0; j < 64; j++) { - for (k = 0; k < song.antChn; k++) + for (k = 0; k < song.numChannels; k++) { - t = &patt[i][(j * MAX_VOICES) + k]; + note_t *p = &pattern[i][(j * MAX_CHANNELS) + k]; - inst = t->instr; - ton = t->ton; + uint8_t inst = p->instr; + uint8_t note = p->note; // FT2 bugfix: prevent overflow if (inst > 31) inst = 0; - // FT2 bugfix: convert note-off into no note - if (ton == 97) - ton = 0; + // FT2 bugfix: convert note-off into no note for MOD saving + if (note == NOTE_OFF) + note = 0; // FT2 bugfix: clamp notes below 10 (A-0) to prevent 12-bit period overflow - if (ton > 0 && ton < 10) - ton = 10; + if (note > 0 && note < 10) + note = 10; - if (ton == 0) + if (note == 0) { - pattBuff[a+0] = inst & 0xF0; - pattBuff[a+1] = 0; + modPattData[offs+0] = inst & 0xF0; + modPattData[offs+1] = 0; } else { - pattBuff[a+0] = (inst & 0xF0) | ((amigaPeriod[ton-1] >> 8) & 0x0F); - pattBuff[a+1] = amigaPeriod[ton-1] & 0xFF; + modPattData[offs+0] = (inst & 0xF0) | ((amigaPeriod[note-1] >> 8) & 0x0F); + modPattData[offs+1] = amigaPeriod[note-1] & 0xFF; } // FT2 bugfix: if effect is overflowing (0xF in .MOD), set effect and param to 0 - if (t->effTyp > 0x0F) + if (p->efx > 0x0F) { - pattBuff[a+2] = (inst & 0x0F) << 4; - pattBuff[a+3] = 0; + modPattData[offs+2] = (inst & 0x0F) << 4; + modPattData[offs+3] = 0; } else { - pattBuff[a+2] = ((inst & 0x0F) << 4) | (t->effTyp & 0x0F); - pattBuff[a+3] = t->eff; + modPattData[offs+2] = ((inst & 0x0F) << 4) | (p->efx & 0x0F); + modPattData[offs+3] = p->efxData; } - a += 4; + offs += 4; } } } - if (fwrite(pattBuff, 1, song.antChn * (64 * 4), f) != (size_t)(song.antChn * (64 * 4))) + if (fwrite(modPattData, 1, patternBytes, f) != (size_t)patternBytes) { okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); goto modSaveError; @@ -520,58 +575,50 @@ static bool saveMOD(UNICHAR *filenameU) } // write sample data - for (i = 0; i < 31; i++) + for (i = 1; i <= 31; i++) { - if (instr[1+i] == NULL || getRealUsedSamples(1+i) == 0) + if (instr[i] == NULL || getRealUsedSamples(i) == 0) continue; - smp = &instr[1+i]->samp[0]; - if (smp->pek == NULL || smp->len <= 0) + smp = &instr[i]->smp[0]; + if (smp->dataPtr == NULL || smp->length <= 0) continue; - restoreSample(smp); + modSmpHdr_t *modSmp = &hdr.smp[i-1]; - l1 = smp->len >> 1; - if (smp->typ & 16) // 16-bit sample (convert to 8-bit) - { - if (l1 > 65534) - l1 = 65534; + unfixSample(smp); - // let's borrow "pattBuff" here - dstPtr = (int8_t *)pattBuff; + int32_t sampleBytes = SWAP16(modSmp->length) * 2; - writeLen = l1; - bytesWritten = 0; - while (bytesWritten < writeLen) // write in 8K blocks + if (smp->flags & SAMPLE_16BIT) // 16-bit sample (convert to 8-bit) + { + int8_t *dstPtr = (int8_t *)smpChunkBuf; + int32_t writeLen = sampleBytes; + int32_t samplesWritten = 0; + + while (samplesWritten < writeLen) // write in chunks { - bytesToWrite = sizeof (pattBuff); - if (bytesWritten+bytesToWrite > writeLen) - bytesToWrite = writeLen - bytesWritten; + int32_t samplesToWrite = sizeof (smpChunkBuf); + if (samplesWritten+samplesToWrite > writeLen) + samplesToWrite = writeLen - samplesWritten; - srcPtr = &smp->pek[(bytesWritten << 1) + 1]; // +1 to align to high byte - for (j = 0; j < bytesToWrite; j++) - dstPtr[j] = srcPtr[j << 1]; + int16_t *srcPtr16 = (int16_t *)smp->dataPtr + samplesWritten; + for (j = 0; j < samplesToWrite; j++) + dstPtr[j] = srcPtr16[j] >> 8; // convert 16-bit to 8-bit - if (fwrite(dstPtr, 1, bytesToWrite, f) != (size_t)bytesToWrite) + if (fwrite(dstPtr, 1, samplesToWrite, f) != (size_t)samplesToWrite) { fixSample(smp); okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); goto modSaveError; } - bytesWritten += bytesToWrite; + samplesWritten += samplesToWrite; } } - else + else // 8-bit sample { - // 8-bit sample - - if (l1 > 32767) - l1 = 32767; - - l1 <<= 1; - - if (fwrite(smp->pek, 1, l1, f) != (size_t)l1) + if (fwrite(smp->dataPtr, 1, sampleBytes, f) != (size_t)sampleBytes) { fixSample(smp); okBoxThreadSafe(0, "System message", "Error saving module: general I/O error!"); @@ -638,10 +685,10 @@ static uint16_t packPatt(uint8_t *writePtr, uint8_t *pattPtr, uint16_t numRows) uint16_t totalPackLen = 0; - const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - song.antChn); + const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - song.numChannels); for (int32_t row = 0; row < numRows; row++) { - for (int32_t chn = 0; chn < song.antChn; chn++) + for (int32_t chn = 0; chn < song.numChannels; chn++) { bytes[0] = *pattPtr++; bytes[1] = *pattPtr++; diff --git a/src/ft2_mouse.c b/src/ft2_mouse.c @@ -8,7 +8,7 @@ #include "ft2_header.h" #include "ft2_gui.h" #include "ft2_video.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_help.h" #include "ft2_sample_ed.h" #include "ft2_inst_ed.h" @@ -127,7 +127,7 @@ bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse p outX[xScale] = pixel; } - outX += video.xScale; + outX += scaleFactor; } } } @@ -383,11 +383,11 @@ static void mouseWheelDecRow(void) if (songPlaying) return; - int16_t pattPos = editor.pattPos - 1; - if (pattPos < 0) - pattPos = pattLens[editor.editPattern] - 1; + int16_t row = editor.row - 1; + if (row < 0) + row = patternNumRows[editor.editPattern] - 1; - setPos(-1, pattPos, true); + setPos(-1, row, true); } static void mouseWheelIncRow(void) @@ -395,11 +395,11 @@ static void mouseWheelIncRow(void) if (songPlaying) return; - int16_t pattPos = editor.pattPos + 1; - if (pattPos > (pattLens[editor.editPattern] - 1)) - pattPos = 0; + int16_t row = editor.row + 1; + if (row >= patternNumRows[editor.editPattern]) + row = 0; - setPos(-1, pattPos, true); + setPos(-1, row, true); } void mouseWheelHandler(bool directionUp) @@ -569,7 +569,7 @@ void mouseButtonUpHandler(uint8_t mouseButton) { // right mouse button released after hand-editing sample data if (instr[editor.curInstr] != NULL) - fixSample(&instr[editor.curInstr]->samp[editor.curSmp]); + fixSample(&instr[editor.curInstr]->smp[editor.curSmp]); resumeAudio(); diff --git a/src/ft2_nibbles.c b/src/ft2_nibbles.c @@ -1,5 +1,3 @@ -// directly ported from FT2 source code (except some routines) - #include <stdint.h> #include <stdio.h> #include <math.h> // round() @@ -13,7 +11,6 @@ #include "ft2_structs.h" #define STAGES_BMP_WIDTH 530 - #define NI_MAXLEVEL 30 static const char *NI_HelpText[] = @@ -35,14 +32,14 @@ static const char *NI_HelpText[] = typedef struct { - int16_t antal; + int16_t length; uint8_t data[8]; -} nibbleBufferTyp; +} nibblesBuffer_t; typedef struct { uint8_t x, y; -} nibbleCrd; +} nibblesCoord_t; static const char nibblesCheatCode1[] = "skip", nibblesCheatCode2[] = "triton"; static char nibblesCheatBuffer[16]; @@ -51,11 +48,11 @@ static const char convHexTable2[10] = { 7, 8, 9, 10, 11, 12, 13, 16, 17, 18 }; static const uint8_t NI_Speeds[4] = { 12, 8, 6, 4 }; static bool NI_EternalLives; static uint8_t NI_CheatIndex, NI_CurSpeed, NI_CurTick60Hz, NI_CurSpeed60Hz, NI_Screen[51][23], NI_Level; -static int16_t NI_P1Dir, NI_P2Dir, NI_P1Len, NI_P2Len, NI_Number, NI_NumberX, NI_NumberY, NI_P1NoRens, NI_P2NoRens; +static int16_t NI_P1Dir, NI_P2Dir, NI_P1Len, NI_P2Len, NI_Number, NI_NumberX, NI_NumberY, NI_P1NoClear, NI_P2NoClear; static uint16_t NI_P1Lives, NI_P2Lives; static int32_t NI_P1Score, NI_P2Score; -static nibbleCrd NI_P1[256], NI_P2[256]; -static nibbleBufferTyp nibblesBuffer[2]; +static nibblesCoord_t NI_P1[256], NI_P2[256]; +static nibblesBuffer_t nibblesBuffer[2]; /* Non-FT2 feature: Check if either the Desktop or Buttons palette color ** is so close to black that the player would have troubles seeing the walls @@ -150,29 +147,29 @@ static void redrawNibblesScreen(void) } } -static void nibblesAddBuffer(int16_t nr, uint8_t typ) +static void nibblesAddBuffer(int16_t bufNum, uint8_t value) { - nibbleBufferTyp *n = &nibblesBuffer[nr]; - if (n->antal < 8) + nibblesBuffer_t *n = &nibblesBuffer[bufNum]; + if (n->length < 8) { - n->data[n->antal] = typ; - n->antal++; + n->data[n->length] = value; + n->length++; } } -static bool nibblesBufferFull(int16_t nr) +static bool nibblesBufferFull(int16_t bufNum) { - return (nibblesBuffer[nr].antal > 0); + return (nibblesBuffer[bufNum].length > 0); } -static int16_t nibblesGetBuffer(int16_t nr) +static int16_t nibblesGetBuffer(int16_t bufNum) { - nibbleBufferTyp *n = &nibblesBuffer[nr]; - if (n->antal > 0) + nibblesBuffer_t *n = &nibblesBuffer[bufNum]; + if (n->length > 0) { const int16_t dataOut = n->data[0]; memmove(&n->data[0], &n->data[1], 7); - n->antal--; + n->length--; return dataOut; } @@ -180,10 +177,10 @@ static int16_t nibblesGetBuffer(int16_t nr) return -1; } -static void nibblesGetLevel(int16_t nr) +static void nibblesGetLevel(int16_t levelNum) { - const int32_t readX = 1 + ((51+2) * (nr % 10)); - const int32_t readY = 1 + ((23+2) * (nr / 10)); + const int32_t readX = 1 + ((51+2) * (levelNum % 10)); + const int32_t readY = 1 + ((23+2) * (levelNum / 10)); const uint8_t *stagePtr = &bmp.nibblesStages[(readY * STAGES_BMP_WIDTH) + readX]; @@ -196,12 +193,12 @@ static void nibblesGetLevel(int16_t nr) } } -static void nibblesCreateLevel(int16_t nr) +static void nibblesCreateLevel(int16_t levelNum) { - if (nr >= NI_MAXLEVEL) - nr = NI_MAXLEVEL - 1; + if (levelNum >= NI_MAXLEVEL) + levelNum = NI_MAXLEVEL-1; - nibblesGetLevel(nr); + nibblesGetLevel(levelNum); int32_t x1 = 0; int32_t x2 = 0; @@ -233,19 +230,19 @@ static void nibblesCreateLevel(int16_t nr) } } - const int32_t readX = (51 + 2) * (nr % 10); - const int32_t readY = (23 + 2) * (nr / 10); + const int32_t readX = (51 + 2) * (levelNum % 10); + const int32_t readY = (23 + 2) * (levelNum / 10); NI_P1Dir = bmp.nibblesStages[(readY * 530) + (readX + 1)]; NI_P2Dir = bmp.nibblesStages[(readY * 530) + (readX + 0)]; NI_P1Len = 5; NI_P2Len = 5; - NI_P1NoRens = 0; - NI_P2NoRens = 0; + NI_P1NoClear = 0; + NI_P2NoClear = 0; NI_Number = 0; - nibblesBuffer[0].antal = 0; - nibblesBuffer[1].antal = 0; + nibblesBuffer[0].length = 0; + nibblesBuffer[1].length = 0; for (int32_t i = 0; i < 256; i++) { @@ -256,10 +253,10 @@ static void nibblesCreateLevel(int16_t nr) } } -static void nibbleWriteLevelSprite(int16_t xOut, int16_t yOut, int16_t nr) +static void nibbleWriteLevelSprite(int16_t xOut, int16_t yOut, int16_t levelNum) { - const int32_t readX = (51 + 2) * (nr % 10); - const int32_t readY = (23 + 2) * (nr / 10); + const int32_t readX = (51 + 2) * (levelNum % 10); + const int32_t readY = (23 + 2) * (levelNum / 10); const uint8_t *src = (const uint8_t *)&bmp.nibblesStages[(readY * 530) + readX]; uint32_t *dst = &video.frameBuffer[(yOut * SCREEN_W) + xOut]; @@ -385,7 +382,7 @@ static void newNibblesGame(void) redrawNibblesScreen(); setNibbleDot(NI_P1[0].x, NI_P1[0].y, 6); - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) setNibbleDot(NI_P2[0].x, NI_P2[0].y, 7); if (!config.NI_Surround) @@ -450,7 +447,7 @@ static void nibblesDecLives(int16_t l1, int16_t l2) // prevent highscore table from showing overflowing level graphics if (NI_Level >= NI_MAXLEVEL) - NI_Level = NI_MAXLEVEL - 1; + NI_Level = NI_MAXLEVEL-1; if (NI_P1Score > config.NI_HighScore[9].score) { @@ -528,7 +525,7 @@ static void nibblesNewLevel(void) // cast to int16_t to simulate a bug in FT2 NI_P1Score += 0x10000 + (int16_t)((12 - NI_CurSpeed) * 0x2000); - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) NI_P2Score += 0x10000; NI_Level++; @@ -536,7 +533,7 @@ static void nibblesNewLevel(void) if (NI_P1Lives < 99) NI_P1Lives++; - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) { if (NI_P2Lives < 99) NI_P2Lives++; @@ -549,7 +546,7 @@ static void nibblesNewLevel(void) nibblesGenNewNumber(); } -void moveNibblePlayers(void) +void moveNibblesPlayers(void) { if (ui.sysReqShown || --NI_CurTick60Hz != 0) return; @@ -578,9 +575,9 @@ void moveNibblePlayers(void) } } - memmove(&NI_P1[1], &NI_P1[0], 255 * sizeof (nibbleCrd)); - if (config.NI_AntPlayers == 1) - memmove(&NI_P2[1], &NI_P2[0], 255 * sizeof (nibbleCrd)); + memmove(&NI_P1[1], &NI_P1[0], 255 * sizeof (nibblesCoord_t)); + if (config.NI_NumPlayers == 1) + memmove(&NI_P2[1], &NI_P2[0], 255 * sizeof (nibblesCoord_t)); switch (NI_P1Dir) { @@ -591,7 +588,7 @@ void moveNibblePlayers(void) default: break; } - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) { switch (NI_P2Dir) { @@ -613,7 +610,7 @@ void moveNibblePlayers(void) NI_P2[0].x %= 51; NI_P2[0].y %= 23; - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) { if (nibblesInvalid(NI_P1[0].x, NI_P1[0].y, NI_P1Dir) && nibblesInvalid(NI_P2[0].x, NI_P2[0].y, NI_P2Dir)) { @@ -651,22 +648,22 @@ void moveNibblePlayers(void) { NI_P1Score += (i & 15) * 999 * (NI_Level + 1); nibblesEraseNumber(); j = 1; - NI_P1NoRens = NI_P1Len / 2; + NI_P1NoClear = NI_P1Len >> 1; } - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) { i = NI_Screen[NI_P2[0].x][NI_P2[0].y]; if (i >= 16) { NI_P2Score += ((i & 15) * 999 * (NI_Level + 1)); nibblesEraseNumber(); j = 1; - NI_P2NoRens = NI_P2Len / 2; + NI_P2NoClear = NI_P2Len >> 1; } } NI_P1Score -= 17; - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) NI_P2Score -= 17; if (NI_P1Score < 0) NI_P1Score = 0; @@ -674,9 +671,9 @@ void moveNibblePlayers(void) if (!config.NI_Surround) { - if (NI_P1NoRens > 0 && NI_P1Len < 255) + if (NI_P1NoClear > 0 && NI_P1Len < 255) { - NI_P1NoRens--; + NI_P1NoClear--; NI_P1Len++; } else @@ -684,11 +681,11 @@ void moveNibblePlayers(void) setNibbleDot(NI_P1[NI_P1Len].x, NI_P1[NI_P1Len].y, 0); } - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) { - if (NI_P2NoRens > 0 && NI_P2Len < 255) + if (NI_P2NoClear > 0 && NI_P2Len < 255) { - NI_P2NoRens--; + NI_P2NoClear--; NI_P2Len++; } else @@ -699,7 +696,7 @@ void moveNibblePlayers(void) } setNibbleDot(NI_P1[0].x, NI_P1[0].y, 6); - if (config.NI_AntPlayers == 1) + if (config.NI_NumPlayers == 1) setNibbleDot(NI_P2[0].x, NI_P2[0].y, 5); if (j == 1 && !config.NI_Surround) @@ -770,7 +767,7 @@ void showNibblesScreen(void) showCheckBox(CB_NIBBLES_WRAP); uncheckRadioButtonGroup(RB_GROUP_NIBBLES_PLAYERS); - if (config.NI_AntPlayers == 0) + if (config.NI_NumPlayers == 0) radioButtons[RB_NIBBLES_1PLAYER].state = RADIOBUTTON_CHECKED; else radioButtons[RB_NIBBLES_2PLAYERS].state = RADIOBUTTON_CHECKED; @@ -821,7 +818,7 @@ void nibblesPlay(void) return; } - if (config.NI_Surround && config.NI_AntPlayers == 0) + if (config.NI_Surround && config.NI_NumPlayers == 0) { okBox(0, "Nibbles message", "Surround mode is not appropriate in one-player mode."); return; @@ -834,8 +831,8 @@ void nibblesPlay(void) NI_CurSpeed = NI_Speeds[config.NI_Speed]; // adjust for 70Hz -> 60Hz frames - NI_CurSpeed60Hz = (uint8_t)round(NI_CurSpeed * ((double)VBLANK_HZ / FT2_VBLANK_HZ)); - NI_CurTick60Hz = (uint8_t)round(NI_Speeds[2] * ((double)VBLANK_HZ / FT2_VBLANK_HZ)); + NI_CurSpeed60Hz = (uint8_t)SCALE_VBLANK_DELTA(NI_CurSpeed); + NI_CurTick60Hz = (uint8_t)SCALE_VBLANK_DELTA(NI_Speeds[2]); editor.NI_Play = true; NI_P1Score = 0; @@ -882,13 +879,13 @@ void nibblesExit(void) void nibblesSet1Player(void) { - config.NI_AntPlayers = 0; + config.NI_NumPlayers = 0; checkRadioButton(RB_NIBBLES_1PLAYER); } void nibblesSet2Players(void) { - config.NI_AntPlayers = 1; + config.NI_NumPlayers = 1; checkRadioButton(RB_NIBBLES_2PLAYERS); } diff --git a/src/ft2_nibbles.h b/src/ft2_nibbles.h @@ -5,7 +5,7 @@ #include <SDL2/SDL.h> void nibblesKeyAdministrator(SDL_Scancode scancode); -void moveNibblePlayers(void); +void moveNibblesPlayers(void); void showNibblesScreen(void); void hideNibblesScreen(void); void exitNibblesScreen(void); diff --git a/src/ft2_palette.c b/src/ft2_palette.c @@ -1,5 +1,6 @@ #include <stdint.h> #include <stdbool.h> +#include <math.h> #include "ft2_palette.h" #include "ft2_gui.h" #include "ft2_config.h" @@ -7,7 +8,7 @@ #include "ft2_palette.h" #include "ft2_tables.h" -uint8_t cfg_ColorNr = 0; // globalized +uint8_t cfg_ColorNum = 0; // globalized static uint8_t cfg_Red, cfg_Green, cfg_Blue, cfg_Contrast; @@ -27,7 +28,7 @@ void setCustomPalColor(uint32_t color) void setPal16(pal16 *p, bool redrawScreen) { -#define LOOP_PIN_COL_SUB 96 +#define LOOP_PIN_COL_SUB 110 #define TEXT_MARK_COLOR 0x0078D7 #define BOX_SELECT_COLOR 0x7F7F7F @@ -89,12 +90,12 @@ static double palPow(double dX, double dY) uint8_t palMax(int32_t c) { - return (uint8_t)(CLAMP(c, 0, 63)); + return (uint8_t)CLAMP(c, 0, 63); } static void drawCurrentPaletteColor(void) { - const uint8_t palIndex = FTC_EditOrder[cfg_ColorNr]; + const uint8_t palIndex = FTC_EditOrder[cfg_ColorNum]; const uint8_t r = P6_TO_P8(cfg_Red); const uint8_t g = P6_TO_P8(cfg_Green); @@ -108,14 +109,14 @@ static void drawCurrentPaletteColor(void) static void updatePaletteEditor(void) { - const uint8_t nr = FTC_EditOrder[cfg_ColorNr]; + const uint8_t colorNum = FTC_EditOrder[cfg_ColorNum]; - cfg_Red = palTable[config.cfg_StdPalNr][nr].r; - cfg_Green = palTable[config.cfg_StdPalNr][nr].g; - cfg_Blue = palTable[config.cfg_StdPalNr][nr].b; + cfg_Red = palTable[config.cfg_StdPalNum][colorNum].r; + cfg_Green = palTable[config.cfg_StdPalNum][colorNum].g; + cfg_Blue = palTable[config.cfg_StdPalNum][colorNum].b; - if (cfg_ColorNr == 4 || cfg_ColorNr == 5) - cfg_Contrast = palContrast[config.cfg_StdPalNr][cfg_ColorNr-4]; + if (cfg_ColorNum == 4 || cfg_ColorNum == 5) + cfg_Contrast = palContrast[config.cfg_StdPalNum][cfg_ColorNum-4]; else cfg_Contrast = 0; @@ -129,28 +130,28 @@ static void updatePaletteEditor(void) static void paletteDragMoved(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) { updatePaletteEditor(); // resets colors/contrast vars showColorErrorMsg(); return; } - if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) { updatePaletteEditor(); // resets colors/contrast vars showMouseColorErrorMsg(); return; } - const uint8_t nr = FTC_EditOrder[cfg_ColorNr]; - const uint8_t p = (uint8_t)config.cfg_StdPalNr; + const uint8_t colorNum = FTC_EditOrder[cfg_ColorNum]; + const uint8_t p = (uint8_t)config.cfg_StdPalNum; - palTable[p][nr].r = cfg_Red; - palTable[p][nr].g = cfg_Green; - palTable[p][nr].b = cfg_Blue; + palTable[p][colorNum].r = cfg_Red; + palTable[p][colorNum].g = cfg_Green; + palTable[p][colorNum].b = cfg_Blue; - if (cfg_ColorNr == 4 || cfg_ColorNr == 5) + if (cfg_ColorNum == 4 || cfg_ColorNum == 5) { double dRed = cfg_Red; double dGreen = cfg_Green; @@ -164,7 +165,7 @@ static void paletteDragMoved(void) for (int32_t i = 0; i < 3; i++) { - const int32_t k = scaleOrder[i] + (cfg_ColorNr - 4) * 2; + const int32_t k = scaleOrder[i] + (cfg_ColorNum - 4) * 2; double dMul = palPow((i + 1) * (1.0 / 2.0), dContrast); @@ -173,7 +174,7 @@ static void paletteDragMoved(void) palTable[p][k].b = palMax((int32_t)((dBlue * dMul) + 0.5)); } - palContrast[p][cfg_ColorNr-4] = cfg_Contrast; + palContrast[p][cfg_ColorNum-4] = cfg_Contrast; } else { @@ -186,7 +187,7 @@ static void paletteDragMoved(void) setScrollBarPos(SB_PAL_CONTRAST, cfg_Contrast, false); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); drawCurrentPaletteColor(); } @@ -228,9 +229,9 @@ void sbPalContrastPos(uint32_t pos) void configPalRDown(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollLeft(SB_PAL_R, 1); @@ -238,9 +239,9 @@ void configPalRDown(void) void configPalRUp(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollRight(SB_PAL_R, 1); @@ -248,9 +249,9 @@ void configPalRUp(void) void configPalGDown(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollLeft(SB_PAL_G, 1); @@ -258,9 +259,9 @@ void configPalGDown(void) void configPalGUp(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollRight(SB_PAL_G, 1); @@ -268,9 +269,9 @@ void configPalGUp(void) void configPalBDown(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollLeft(SB_PAL_B, 1); @@ -278,9 +279,9 @@ void configPalBDown(void) void configPalBUp(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollRight(SB_PAL_B, 1); @@ -288,9 +289,9 @@ void configPalBUp(void) void configPalContDown(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollLeft(SB_PAL_CONTRAST, 1); @@ -298,9 +299,9 @@ void configPalContDown(void) void configPalContUp(void) { - if (config.cfg_StdPalNr != PAL_USER_DEFINED) + if (config.cfg_StdPalNum != PAL_USER_DEFINED) showColorErrorMsg(); - else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + else if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNum == 3) showMouseColorErrorMsg(); else scrollBarScrollRight(SB_PAL_CONTRAST, 1); @@ -334,138 +335,138 @@ void showPaletteEditor(void) void rbConfigPalPatternText(void) { - cfg_ColorNr = 0; + cfg_ColorNum = 0; checkRadioButton(RB_CONFIG_PAL_PATTERNTEXT); updatePaletteEditor(); } void rbConfigPalBlockMark(void) { - cfg_ColorNr = 1; + cfg_ColorNum = 1; checkRadioButton(RB_CONFIG_PAL_BLOCKMARK); updatePaletteEditor(); } void rbConfigPalTextOnBlock(void) { - cfg_ColorNr = 2; + cfg_ColorNum = 2; checkRadioButton(RB_CONFIG_PAL_TEXTONBLOCK); updatePaletteEditor(); } void rbConfigPalMouse(void) { - cfg_ColorNr = 3; + cfg_ColorNum = 3; checkRadioButton(RB_CONFIG_PAL_MOUSE); updatePaletteEditor(); } void rbConfigPalDesktop(void) { - cfg_ColorNr = 4; + cfg_ColorNum = 4; checkRadioButton(RB_CONFIG_PAL_DESKTOP); updatePaletteEditor(); } void rbConfigPalButttons(void) { - cfg_ColorNr = 5; + cfg_ColorNum = 5; checkRadioButton(RB_CONFIG_PAL_BUTTONS); updatePaletteEditor(); } void rbConfigPalArctic(void) { - config.cfg_StdPalNr = PAL_ARCTIC; + config.cfg_StdPalNum = PAL_ARCTIC; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_ARCTIC); } void rbConfigPalLitheDark(void) { - config.cfg_StdPalNr = PAL_LITHE_DARK; + config.cfg_StdPalNum = PAL_LITHE_DARK; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_LITHE_DARK); } void rbConfigPalAuroraBorealis(void) { - config.cfg_StdPalNr = PAL_AURORA_BOREALIS; + config.cfg_StdPalNum = PAL_AURORA_BOREALIS; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_AURORA_BOREALIS); } void rbConfigPalRose(void) { - config.cfg_StdPalNr = PAL_ROSE; + config.cfg_StdPalNum = PAL_ROSE; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_ROSE); } void rbConfigPalBlues(void) { - config.cfg_StdPalNr = PAL_BLUES; + config.cfg_StdPalNum = PAL_BLUES; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_BLUES); } void rbConfigPalDarkMode(void) { - config.cfg_StdPalNr = PAL_DARK_MODE; + config.cfg_StdPalNum = PAL_DARK_MODE; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_DARK_MODE); } void rbConfigPalGold(void) { - config.cfg_StdPalNr = PAL_GOLD; + config.cfg_StdPalNum = PAL_GOLD; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_GOLD); } void rbConfigPalViolent(void) { - config.cfg_StdPalNr = PAL_VIOLENT; + config.cfg_StdPalNum = PAL_VIOLENT; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_VIOLENT); } void rbConfigPalHeavyMetal(void) { - config.cfg_StdPalNr = PAL_HEAVY_METAL; + config.cfg_StdPalNum = PAL_HEAVY_METAL; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_HEAVY_METAL); } void rbConfigPalWhyColors(void) { - config.cfg_StdPalNr = PAL_WHY_COLORS; + config.cfg_StdPalNum = PAL_WHY_COLORS; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_WHY_COLORS); } void rbConfigPalJungle(void) { - config.cfg_StdPalNr = PAL_JUNGLE; + config.cfg_StdPalNum = PAL_JUNGLE; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_JUNGLE); } void rbConfigPalUserDefined(void) { - config.cfg_StdPalNr = PAL_USER_DEFINED; + config.cfg_StdPalNum = PAL_USER_DEFINED; updatePaletteEditor(); - setPal16(palTable[config.cfg_StdPalNr], true); + setPal16(palTable[config.cfg_StdPalNum], true); checkRadioButton(RB_CONFIG_PAL_USER_DEFINED); } diff --git a/src/ft2_palette.h b/src/ft2_palette.h @@ -49,8 +49,6 @@ typedef struct pal16_t uint8_t r, g, b; } pal16; -extern uint8_t cfg_ColorNr; - void setCustomPalColor(uint32_t color); uint8_t palMax(int32_t c); @@ -88,3 +86,5 @@ void rbConfigPalHeavyMetal(void); void rbConfigPalWhyColors(void); void rbConfigPalJungle(void); void rbConfigPalUserDefined(void); + +extern uint8_t cfg_ColorNum; diff --git a/src/ft2_pattern_draw.c b/src/ft2_pattern_draw.c @@ -14,7 +14,7 @@ #include "ft2_bmp.h" #include "ft2_structs.h" -static tonTyp emptyPattern[MAX_VOICES * MAX_PATT_LEN]; +static note_t emptyPattern[MAX_CHANNELS * MAX_PATT_LEN]; static const uint8_t *font4Ptr, *font5Ptr; static const uint8_t vol2charTab1[16] = { 39, 0, 1, 2, 3, 4, 36, 52, 53, 54, 28, 31, 25, 58, 59, 22 }; @@ -36,13 +36,13 @@ static const uint16_t flatNote2Char_big[12] = { 36*16, 38*16, 36*16, 38*16, 36*1 static void pattCharOut(uint32_t xPos, uint32_t yPos, uint8_t chr, uint8_t fontType, uint32_t color); static void drawEmptyNoteSmall(uint32_t xPos, uint32_t yPos, uint32_t color); static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color); -static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color); +static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); static void drawEmptyNoteMedium(uint32_t xPos, uint32_t yPos, uint32_t color); static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color); -static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color); +static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); static void drawEmptyNoteBig(uint32_t xPos, uint32_t yPos, uint32_t color); static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color); -static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color); +static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color); void updatePattFontPtrs(void) { @@ -54,7 +54,7 @@ void updatePattFontPtrs(void) void drawPatternBorders(void) { // get heights/pos/rows depending on configuration - const pattCoord2_t *pattCoord = &pattCoord2Table[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended]; + const pattCoord2_t *pattCoord = &pattCoord2Table[config.ptnStretch][ui.pattChanScrollShown][ui.extended]; // set pattern cursor Y position editor.ptnCursorY = pattCoord->lowerRowsY - 9; @@ -66,7 +66,7 @@ void drawPatternBorders(void) // in some configurations, there will be two empty channels to the right, fix that if (chans == 2) chans = 4; - else if (chans == 10 && !config.ptnS3M) + else if (chans == 10 && !config.ptnShowVolColumn) chans = 12; assert(chans >= 2 && chans <= 12); @@ -177,7 +177,7 @@ void drawPatternBorders(void) static void writeCursor(void) { - const int32_t tabOffset = (config.ptnS3M * 32) + (columnModeTab[ui.numChannelsShown-1] * 8) + cursor.object; + const int32_t tabOffset = (config.ptnShowVolColumn * 32) + (columnModeTab[ui.numChannelsShown-1] * 8) + cursor.object; int32_t xPos = pattCursorXTab[tabOffset]; const int32_t width = pattCursorWTab[tabOffset]; @@ -212,7 +212,7 @@ static void writePatternBlockMark(int32_t currRow, uint32_t rowHeight, const pat if (pattMark.markX1 > endCh || pattMark.markX2 < startCh || pattMark.markY1 > endRow || pattMark.markY2 < startRow) return; - const markCoord_t *markCoord = &markCoordTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended]; + const markCoord_t *markCoord = &markCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended]; const int32_t pattYStart = markCoord->upperRowsY; // X1 @@ -264,7 +264,7 @@ static void writePatternBlockMark(int32_t currRow, uint32_t rowHeight, const pat } // kludge! (some mark situations could overwrite illegal areas) - if (config.ptnUnpressed && ui.pattChanScrollShown) + if (config.ptnStretch && ui.pattChanScrollShown) { if (y1 == pattCoord->upperRowsY-1 || y1 == pattCoord->lowerRowsY-1) y1++; @@ -368,29 +368,29 @@ static void drawRowNums(int32_t yPos, uint8_t row, bool selectedRowFlag) // DRAWING ROUTINES (WITH VOLUME COLUMN) -static void showNoteNum(uint32_t xPos, uint32_t yPos, int16_t ton, uint32_t color) +static void showNoteNum(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color) { xPos += 3; - assert(ton >= 0 && ton <= 97); + assert(note >= 0 && note <= 97); if (ui.numChannelsShown <= 4) { - if (ton <= 0 || ton > 97) + if (note <= 0 || note > 97) drawEmptyNoteBig(xPos, yPos, color); - else if (ton == 97) + else if (note == NOTE_OFF) drawKeyOffBig(xPos, yPos, color); else - drawNoteBig(xPos, yPos, ton, color); + drawNoteBig(xPos, yPos, note, color); } else { - if (ton <= 0 || ton > 97) + if (note <= 0 || note > 97) drawEmptyNoteMedium(xPos, yPos, color); - else if (ton == 97) + else if (note == NOTE_OFF) drawKeyOffMedium(xPos, yPos, color); else - drawNoteMedium(xPos, yPos, ton, color); + drawNoteMedium(xPos, yPos, note, color); } } @@ -480,7 +480,7 @@ static void showVolEfx(uint32_t xPos, uint32_t yPos, uint8_t vol, uint32_t color pattCharOut(xPos + charW, yPos, char2, fontType, color); } -static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uint8_t eff, uint32_t color) +static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color) { uint8_t fontType, charW; @@ -503,45 +503,45 @@ static void showEfx(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uint8_t eff, u xPos += 55; } - pattCharOut(xPos, yPos, effTyp, fontType, color); - pattCharOut(xPos + charW, yPos, eff >> 4, fontType, color); - pattCharOut(xPos + (charW * 2), yPos, eff & 0x0F, fontType, color); + pattCharOut(xPos, yPos, efx, fontType, color); + pattCharOut(xPos + charW, yPos, efxData >> 4, fontType, color); + pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color); } // DRAWING ROUTINES (WITHOUT VOLUME COLUMN) -static void showNoteNumNoVolColumn(uint32_t xPos, uint32_t yPos, int16_t ton, uint32_t color) +static void showNoteNumNoVolColumn(uint32_t xPos, uint32_t yPos, int16_t note, uint32_t color) { xPos += 3; - assert(ton >= 0 && ton <= 97); + assert(note >= 0 && note <= 97); if (ui.numChannelsShown <= 6) { - if (ton <= 0 || ton > 97) + if (note <= 0 || note > 97) drawEmptyNoteBig(xPos, yPos, color); - else if (ton == 97) + else if (note == NOTE_OFF) drawKeyOffBig(xPos, yPos, color); else - drawNoteBig(xPos, yPos, ton, color); + drawNoteBig(xPos, yPos, note, color); } else if (ui.numChannelsShown <= 8) { - if (ton <= 0 || ton > 97) + if (note <= 0 || note > 97) drawEmptyNoteMedium(xPos, yPos, color); - else if (ton == 97) + else if (note == NOTE_OFF) drawKeyOffMedium(xPos, yPos, color); else - drawNoteMedium(xPos, yPos, ton, color); + drawNoteMedium(xPos, yPos, note, color); } else { - if (ton <= 0 || ton > 97) + if (note <= 0 || note > 97) drawEmptyNoteSmall(xPos, yPos, color); - else if (ton == 97) + else if (note == NOTE_OFF) drawKeyOffSmall(xPos, yPos, color); else - drawNoteSmall(xPos, yPos, ton, color); + drawNoteSmall(xPos, yPos, note, color); } } @@ -600,7 +600,7 @@ static void showNoVolEfx(uint32_t xPos, uint32_t yPos, uint8_t vol, uint32_t col (void)color; } -static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uint8_t eff, uint32_t color) +static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t efx, uint8_t efxData, uint32_t color) { uint8_t charW, fontType; @@ -629,12 +629,12 @@ static void showEfxNoVolColumn(uint32_t xPos, uint32_t yPos, uint8_t effTyp, uin xPos += 31; } - pattCharOut(xPos, yPos, effTyp, fontType, color); - pattCharOut(xPos + charW, yPos, eff >> 4, fontType, color); - pattCharOut(xPos + (charW * 2), yPos, eff & 0x0F, fontType, color); + pattCharOut(xPos, yPos, efx, fontType, color); + pattCharOut(xPos + charW, yPos, efxData >> 4, fontType, color); + pattCharOut(xPos + (charW * 2), yPos, efxData & 0x0F, fontType, color); } -void writePattern(int32_t currRow, int32_t pattern) +void writePattern(int32_t currRow, int32_t currPattern) { uint32_t noteTextColors[2]; @@ -663,8 +663,8 @@ void writePattern(int32_t currRow, int32_t pattern) ui.patternChannelWidth = (uint16_t)(chanWidth + 3); // get heights/pos/rows depending on configuration - uint32_t rowHeight = config.ptnUnpressed ? 11 : 8; - const pattCoord_t *pattCoord = &pattCoordTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended]; + uint32_t rowHeight = config.ptnStretch ? 11 : 8; + const pattCoord_t *pattCoord = &pattCoordTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended]; const int32_t midRowTextY = pattCoord->midRowTextY; const int32_t lowerRowsTextY = pattCoord->lowerRowsTextY; int32_t row = currRow - pattCoord->numUpperRows; @@ -672,17 +672,15 @@ void writePattern(int32_t currRow, int32_t pattern) int32_t textY = pattCoord->upperRowsTextY; const int32_t afterCurrRow = currRow + 1; const int32_t numChannels = ui.numChannelsShown; - tonTyp *pattPtr = patt[pattern]; - const int32_t numRows = pattLens[pattern]; - - + note_t *pattPtr = pattern[currPattern]; + const int32_t numRows = patternNumRows[currPattern]; // increment pattern data pointer by horizontal scrollbar offset/channel if (pattPtr != NULL) pattPtr += ui.channelOffset; // set up function pointers for drawing - if (config.ptnS3M) + if (config.ptnShowVolColumn) { drawNote = showNoteNum; drawInst = showInstrNum; @@ -709,19 +707,17 @@ void writePattern(int32_t currRow, int32_t pattern) drawRowNums(textY, (uint8_t)row, selectedRowFlag); - const tonTyp *note = (pattPtr == NULL) ? emptyPattern : &pattPtr[(uint32_t)row * MAX_VOICES]; + const note_t *p = (pattPtr == NULL) ? emptyPattern : &pattPtr[(uint32_t)row * MAX_CHANNELS]; const int32_t xWidth = ui.patternChannelWidth; const uint32_t color = noteTextColors[selectedRowFlag]; int32_t xPos = 29; - for (int32_t j = 0; j < numChannels; j++, note++) + for (int32_t j = 0; j < numChannels; j++, p++, xPos += xWidth) { - drawNote(xPos, textY, note->ton, color); - drawInst(xPos, textY, note->instr, color); - drawVolEfx(xPos, textY, note->vol, color); - drawEfx(xPos, textY, note->effTyp, note->eff, color); - - xPos += xWidth; + drawNote(xPos, textY, p->note, color); + drawInst(xPos, textY, p->instr, color); + drawVolEfx(xPos, textY, p->vol, color); + drawEfx(xPos, textY, p->efx, p->efxData, color); } } @@ -932,15 +928,14 @@ static void drawKeyOffSmall(uint32_t xPos, uint32_t yPos, uint32_t color) } } -static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color) +static void drawNoteSmall(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) { uint32_t char1, char2; - assert(ton >= 1 && ton <= 97); - ton--; + noteNum--; - const uint8_t note = noteTab1[ton]; - const uint32_t char3 = noteTab2[ton] * FONT7_CHAR_W; + const uint8_t note = noteTab1[noteNum]; + const uint32_t char3 = noteTab2[noteNum] * FONT7_CHAR_W; if (config.ptnAcc == 0) { @@ -1039,14 +1034,14 @@ static void drawKeyOffMedium(uint32_t xPos, uint32_t yPos, uint32_t color) } } -static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color) +static void drawNoteMedium(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) { uint32_t char1, char2; - ton--; + noteNum--; - const uint8_t note = noteTab1[ton]; - const uint32_t char3 = noteTab2[ton] * FONT4_CHAR_W; + const uint8_t note = noteTab1[noteNum]; + const uint32_t char3 = noteTab2[noteNum] * FONT4_CHAR_W; if (config.ptnAcc == 0) { @@ -1145,14 +1140,14 @@ static void drawKeyOffBig(uint32_t xPos, uint32_t yPos, uint32_t color) } } -static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t ton, uint32_t color) +static void drawNoteBig(uint32_t xPos, uint32_t yPos, int32_t noteNum, uint32_t color) { uint32_t char1, char2; - ton--; + noteNum--; - const uint8_t note = noteTab1[ton]; - const uint32_t char3 = noteTab2[ton] * FONT5_CHAR_W; + const uint8_t note = noteTab1[noteNum]; + const uint32_t char3 = noteTab2[noteNum] * FONT5_CHAR_W; if (config.ptnAcc == 0) { diff --git a/src/ft2_pattern_draw.h b/src/ft2_pattern_draw.h @@ -4,5 +4,5 @@ void updatePattFontPtrs(void); void drawPatternBorders(void); -void writePattern(int32_t currRow, int32_t pattern); +void writePattern(int32_t currRow, int32_t currPattern); void pattTwoHexOut(uint32_t xPos, uint32_t yPos, uint8_t val, uint32_t color); diff --git a/src/ft2_pattern_ed.c b/src/ft2_pattern_ed.c @@ -13,7 +13,7 @@ #include "ft2_sample_ed.h" #include "ft2_pattern_draw.h" #include "ft2_inst_ed.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_diskop.h" #include "ft2_audio.h" #include "ft2_wav_renderer.h" @@ -32,7 +32,7 @@ static int16_t lastRowMark; // for pattern marking w/ mouse static int32_t lastMarkX1 = -1, lastMarkX2 = -1, lastMarkY1 = -1, lastMarkY2 = -1; -static const uint8_t ptnAntLine[8] = { 27, 25, 20, 19, 42, 40, 31, 30 }; +static const uint8_t ptnNumRows[8] = { 27, 25, 20, 19, 42, 40, 31, 30 }; static const uint8_t ptnLineSub[8] = { 13, 12, 9, 9, 20, 19, 15, 14 }; static const uint8_t iSwitchExtW[4] = { 40, 40, 40, 39 }; static const uint8_t iSwitchExtY[8] = { 2, 2, 2, 2, 19, 19, 19, 19 }; @@ -42,13 +42,13 @@ static const uint16_t iSwitchExtX[4] = { 221, 262, 303, 344 }; static int32_t lastMouseX, lastMouseY; static int32_t last_TimeH, last_TimeM, last_TimeS; -bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! +bool allocatePattern(uint16_t pattNum) // for tracker use only, not in loader! { const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (patt[nr] == NULL) + if (pattern[pattNum] == NULL) { /* Original FT2 allocates only the amount of rows needed, but we don't ** do that to avoid out of bondary row look-up between out-of-sync replayer @@ -57,8 +57,8 @@ bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! ** patterns would be ~10MB. **/ - patt[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); - if (patt[nr] == NULL) + pattern[pattNum] = (note_t *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); + if (pattern[pattNum] == NULL) { if (audioWasntLocked) unlockAudio(); @@ -66,7 +66,7 @@ bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! return false; } - song.pattLen = pattLens[nr]; + song.currNumRows = patternNumRows[pattNum]; } if (audioWasntLocked) @@ -75,18 +75,18 @@ bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! return true; } -void killPatternIfUnused(uint16_t nr) // for tracker use only, not in loader! +void killPatternIfUnused(uint16_t pattNum) // for tracker use only, not in loader! { const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (patternEmpty(nr)) + if (patternEmpty(pattNum)) { - if (patt[nr] != NULL) + if (pattern[pattNum] != NULL) { - free(patt[nr]); - patt[nr] = NULL; + free(pattern[pattNum]); + pattern[pattNum] = NULL; } } @@ -97,7 +97,7 @@ void killPatternIfUnused(uint16_t nr) // for tracker use only, not in loader! uint8_t getMaxVisibleChannels(void) { assert(config.ptnMaxChannels >= 0 && config.ptnMaxChannels <= 3); - if (config.ptnS3M) + if (config.ptnShowVolColumn) return maxVisibleChans1[config.ptnMaxChannels]; else return maxVisibleChans2[config.ptnMaxChannels]; @@ -360,9 +360,9 @@ void cursorChannelLeft(void) if (cursor.ch == 0) { - cursor.ch = (uint8_t)(song.antChn - 1); + cursor.ch = (uint8_t)(song.numChannels - 1); if (ui.pattChanScrollShown) - setScrollBarPos(SB_CHAN_SCROLL, song.antChn, true); + setScrollBarPos(SB_CHAN_SCROLL, song.numChannels, true); } else { @@ -379,7 +379,7 @@ void cursorChannelRight(void) { cursor.object = CURSOR_NOTE; - if (cursor.ch >= song.antChn-1) + if (cursor.ch >= song.numChannels-1) { cursor.ch = 0; if (ui.pattChanScrollShown) @@ -427,7 +427,7 @@ void cursorLeft(void) { cursor.object--; - if (!config.ptnS3M) + if (!config.ptnShowVolColumn) { while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2) cursor.object--; @@ -446,7 +446,7 @@ void cursorRight(void) { cursor.object++; - if (!config.ptnS3M) + if (!config.ptnShowVolColumn) { while (cursor.object == CURSOR_VOL1 || cursor.object == CURSOR_VOL2) cursor.object++; @@ -665,7 +665,7 @@ void patternEditorExtended(void) showInstrumentSwitcher(); drawSongLength(); - drawSongRepS(); + drawSongLoopStart(); drawEditPattern(editor.editPattern); drawPatternLength(editor.editPattern); drawPosEdNums(editor.songPos); @@ -728,11 +728,11 @@ void clearPattMark(void) void checkMarkLimits(void) { - const uint16_t limitY = pattLens[editor.editPattern]; + const uint16_t limitY = patternNumRows[editor.editPattern]; pattMark.markY1 = CLAMP(pattMark.markY1, 0, limitY); pattMark.markY2 = CLAMP(pattMark.markY2, 0, limitY); - const uint16_t limitX = (uint16_t)(song.antChn - 1); + const uint16_t limitX = (uint16_t)(song.numChannels - 1); pattMark.markX1 = CLAMP(pattMark.markX1, 0, limitX); pattMark.markX2 = CLAMP(pattMark.markX2, 0, limitX); @@ -756,27 +756,27 @@ static int8_t mouseXToCh(void) // used to get channel num from mouse x (for patt ch = CLAMP(ch, 0, chEnd); // in some setups there can be non-used channels to the right, do clamping - if (ch >= song.antChn) - ch = (int8_t)(song.antChn - 1); + if (ch >= song.numChannels) + ch = (int8_t)(song.numChannels - 1); return ch; } static int16_t mouseYToRow(void) // used to get row num from mouse y (for pattern marking) { - const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnUnpressed][ui.pattChanScrollShown][ui.extended]; + const pattCoordsMouse_t *pattCoordsMouse = &pattCoordMouseTable[config.ptnStretch][ui.pattChanScrollShown][ui.extended]; // clamp mouse y to boundaries const int16_t maxY = ui.pattChanScrollShown ? 382 : 396; const int16_t my = (int16_t)CLAMP(mouse.y, pattCoordsMouse->upperRowsY, maxY); - const uint8_t charHeight = config.ptnUnpressed ? 11 : 8; + const uint8_t charHeight = config.ptnStretch ? 11 : 8; // test top/middle/bottom rows if (my < pattCoordsMouse->midRowY) { // top rows - int16_t row = editor.pattPos - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight)); + int16_t row = editor.row - (pattCoordsMouse->numUpperRows - ((my - pattCoordsMouse->upperRowsY) / charHeight)); if (row < 0) row = 0; @@ -785,22 +785,22 @@ static int16_t mouseYToRow(void) // used to get row num from mouse y (for patter else if (my >= pattCoordsMouse->midRowY && my <= pattCoordsMouse->midRowY+10) { // current row (middle) - return editor.pattPos; + return editor.row; } else { // bottom rows - int16_t row = (editor.pattPos + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight); + int16_t row = (editor.row + 1) + ((my - pattCoordsMouse->lowerRowsY) / charHeight); // prevent being able to mark the next unseen row on the bottom (in some configurations) - const uint8_t mode = (ui.extended * 4) + (config.ptnUnpressed * 2) + ui.pattChanScrollShown; + const uint8_t mode = (ui.extended * 4) + (config.ptnStretch * 2) + ui.pattChanScrollShown; - const int16_t maxRow = (ptnAntLine[mode] + (editor.pattPos - ptnLineSub[mode])) - 1; + const int16_t maxRow = (ptnNumRows[mode] + (editor.row - ptnLineSub[mode])) - 1; if (row > maxRow) row = maxRow; // clamp to pattern length - const int16_t patternLen = pattLens[editor.editPattern]; + const int16_t patternLen = patternNumRows[editor.editPattern]; if (row >= patternLen) row = patternLen - 1; @@ -897,17 +897,17 @@ void handlePatternDataMouseDown(bool mouseButtonHeld) if (mouse.y < y1) { - if (editor.pattPos > 0) - setPos(-1, editor.pattPos - 1, true); + if (editor.row > 0) + setPos(-1, editor.row - 1, true); forceMarking = true; ui.updatePatternEditor = true; } else if (mouse.y > y2) { - const int16_t pattLen = pattLens[editor.editPattern]; - if (editor.pattPos < pattLen-1) - setPos(-1, editor.pattPos + 1, true); + const int16_t numRows = patternNumRows[editor.editPattern]; + if (editor.row < numRows-1) + setPos(-1, editor.row + 1, true); forceMarking = true; ui.updatePatternEditor = true; @@ -948,10 +948,10 @@ void rowOneUpWrap(void) if (audioWasntLocked) lockAudio(); - song.pattPos = (song.pattPos - 1 + song.pattLen) % song.pattLen; + song.row = (song.row - 1 + song.currNumRows) % song.currNumRows; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -967,12 +967,12 @@ void rowOneDownWrap(void) if (songPlaying) { - song.timer = 2; + song.tick = 2; } else { - song.pattPos = (song.pattPos + 1 + song.pattLen) % song.pattLen; - editor.pattPos = (uint8_t)song.pattPos; + song.row = (song.row + 1 + song.currNumRows) % song.currNumRows; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -986,13 +986,13 @@ void rowUp(uint16_t amount) if (audioWasntLocked) lockAudio(); - song.pattPos -= amount; - if (song.pattPos < 0) - song.pattPos = 0; + song.row -= amount; + if (song.row < 0) + song.row = 0; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -1006,13 +1006,13 @@ void rowDown(uint16_t amount) if (audioWasntLocked) lockAudio(); - song.pattPos += amount; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen - 1; + song.row += amount; + if (song.row >= song.currNumRows) + song.row = song.currNumRows - 1; if (!songPlaying) { - editor.pattPos = (uint8_t)song.pattPos; + editor.row = (uint8_t)song.row; ui.updatePatternEditor = true; } @@ -1023,30 +1023,30 @@ void rowDown(uint16_t amount) void keybPattMarkUp(void) { int8_t xPos = cursor.ch; - int16_t pattPos = editor.pattPos; + int16_t row = editor.row; if (xPos != pattMark.markX1 && xPos != pattMark.markX2) { pattMark.markX1 = xPos; pattMark.markX2 = xPos; - pattMark.markY1 = pattPos; - pattMark.markY2 = pattPos + 1; + pattMark.markY1 = row; + pattMark.markY2 = row + 1; } - if (pattPos == pattMark.markY1-1) + if (row == pattMark.markY1-1) { - pattMark.markY1 = pattPos; + pattMark.markY1 = row; } - else if (pattPos == pattMark.markY2) + else if (row == pattMark.markY2) { - pattMark.markY2 = pattPos - 1; + pattMark.markY2 = row - 1; } - else if (pattPos != pattMark.markY1 && pattPos != pattMark.markY2) + else if (row != pattMark.markY1 && row != pattMark.markY2) { pattMark.markX1 = xPos; pattMark.markX2 = xPos; - pattMark.markY1 = pattPos; - pattMark.markY2 = pattPos + 1; + pattMark.markY1 = row; + pattMark.markY2 = row + 1; } @@ -1057,30 +1057,30 @@ void keybPattMarkUp(void) void keybPattMarkDown(void) { int8_t xPos = cursor.ch; - int16_t pattPos = editor.pattPos; + int16_t row = editor.row; if (xPos != pattMark.markX1 && xPos != pattMark.markX2) { pattMark.markX1 = xPos; pattMark.markX2 = xPos; - pattMark.markY1 = pattPos; - pattMark.markY2 = pattPos + 1; + pattMark.markY1 = row; + pattMark.markY2 = row + 1; } - if (pattPos == pattMark.markY2) + if (row == pattMark.markY2) { - pattMark.markY2 = pattPos + 1; + pattMark.markY2 = row + 1; } - else if (pattPos == pattMark.markY1-1) + else if (row == pattMark.markY1-1) { - pattMark.markY1 = pattPos + 2; + pattMark.markY1 = row + 2; } - else if (pattPos != pattMark.markY1 && pattPos != pattMark.markY2) + else if (row != pattMark.markY1 && row != pattMark.markY2) { pattMark.markX1 = xPos; pattMark.markX2 = xPos; - pattMark.markY1 = pattPos; - pattMark.markY2 = pattPos + 1; + pattMark.markY1 = row; + pattMark.markY2 = row + 1; } checkMarkLimits(); @@ -1090,12 +1090,12 @@ void keybPattMarkDown(void) void keybPattMarkLeft(void) { int8_t xPos = cursor.ch; - int16_t pattPos = editor.pattPos; + int16_t row = editor.row; - if (pattPos != pattMark.markY1-1 && pattPos != pattMark.markY2) + if (row != pattMark.markY1-1 && row != pattMark.markY2) { - pattMark.markY1 = pattPos - 1; - pattMark.markY2 = pattPos; + pattMark.markY1 = row - 1; + pattMark.markY2 = row; } if (xPos == pattMark.markX1) @@ -1110,8 +1110,8 @@ void keybPattMarkLeft(void) { pattMark.markX1 = xPos - 1; pattMark.markX2 = xPos; - pattMark.markY1 = pattPos - 1; - pattMark.markY2 = pattPos; + pattMark.markY1 = row - 1; + pattMark.markY2 = row; } checkMarkLimits(); @@ -1121,12 +1121,12 @@ void keybPattMarkLeft(void) void keybPattMarkRight(void) { int8_t xPos = cursor.ch; - int16_t pattPos = editor.pattPos; + int16_t row = editor.row; - if (pattPos != pattMark.markY1-1 && pattPos != pattMark.markY2) + if (row != pattMark.markY1-1 && row != pattMark.markY2) { - pattMark.markY1 = pattPos - 1; - pattMark.markY2 = pattPos; + pattMark.markY1 = row - 1; + pattMark.markY2 = row; } if (xPos == pattMark.markX2) @@ -1141,8 +1141,8 @@ void keybPattMarkRight(void) { pattMark.markX1 = xPos; pattMark.markX2 = xPos + 1; - pattMark.markY1 = pattPos - 1; - pattMark.markY2 = pattPos; + pattMark.markY1 = row - 1; + pattMark.markY2 = row; } checkMarkLimits(); @@ -1151,8 +1151,8 @@ void keybPattMarkRight(void) bool loadTrack(UNICHAR *filenameU) { - tonTyp loadBuff[MAX_PATT_LEN]; - trackHeaderType th; + note_t loadBuff[MAX_PATT_LEN]; + xtHdr_t h; FILE *f = UNICHAR_FOPEN(filenameU, "rb"); if (f == NULL) @@ -1161,55 +1161,56 @@ bool loadTrack(UNICHAR *filenameU) return false; } - uint16_t nr = editor.editPattern; - int16_t pattLen = pattLens[nr]; - - if (fread(&th, 1, sizeof (th), f) != sizeof (th)) + if (fread(&h, 1, sizeof (h), f) != sizeof (h)) { okBox(0, "System message", "General I/O error during loading! Is the file in use?"); goto trackLoadError; } - if (th.ver != 1) + if (h.version != 1) { okBox(0, "System message", "Incompatible format version!"); goto trackLoadError; } - if (th.len > MAX_PATT_LEN) - th.len = MAX_PATT_LEN; + if (h.numRows > MAX_PATT_LEN) + h.numRows = MAX_PATT_LEN; - if (pattLen > th.len) - pattLen = th.len; + int16_t numRows = patternNumRows[editor.editPattern]; + if (numRows > h.numRows) + numRows = h.numRows; - if (fread(loadBuff, pattLen * sizeof (tonTyp), 1, f) != 1) + if (fread(loadBuff, numRows * sizeof (note_t), 1, f) != 1) { okBox(0, "System message", "General I/O error during loading! Is the file in use?"); goto trackLoadError; } - if (!allocatePattern(nr)) + if (!allocatePattern(editor.editPattern)) { okBox(0, "System message", "Not enough memory!"); goto trackLoadError; } - tonTyp *pattPtr = patt[nr]; - lockMixerCallback(); - for (int32_t i = 0; i < pattLen; i++) + for (int32_t i = 0; i < numRows; i++) { - pattPtr = &patt[nr][(i * MAX_VOICES) + cursor.ch]; - *pattPtr = loadBuff[i]; + note_t *p = &pattern[editor.editPattern][(i * MAX_CHANNELS) + cursor.ch]; + + *p = loadBuff[i]; + + // sanitize stuff (FT2 doesn't do this!) - // non-FT2 security fix: remove overflown (illegal) stuff - if (pattPtr->ton > 97) - pattPtr->ton = 0; + if (p->note > 97) + p->note = 0; - if (pattPtr->effTyp > 35) + if (p->instr > 128) + p->instr = 0; + + if (p->efx > 35) { - pattPtr->effTyp = 0; - pattPtr->eff = 0; + p->efx = 0; + p->efxData = 0; } } unlockMixerCallback(); @@ -1231,13 +1232,11 @@ trackLoadError: bool saveTrack(UNICHAR *filenameU) { - tonTyp saveBuff[MAX_PATT_LEN]; - trackHeaderType th; - - uint16_t nr = editor.editPattern; - tonTyp *pattPtr = patt[nr]; + note_t saveBuff[MAX_PATT_LEN]; + xtHdr_t h; - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) { okBox(0, "System message", "The current pattern is empty!"); return false; @@ -1250,21 +1249,20 @@ bool saveTrack(UNICHAR *filenameU) return false; } - const int16_t pattLen = pattLens[nr]; - for (int32_t i = 0; i < pattLen; i++) - saveBuff[i] = pattPtr[(i * MAX_VOICES) + cursor.ch]; + h.version = 1; + h.numRows = patternNumRows[editor.editPattern]; - th.len = pattLen; - th.ver = 1; + for (int32_t i = 0; i < h.numRows; i++) + saveBuff[i] = p[(i * MAX_CHANNELS) + cursor.ch]; - if (fwrite(&th, sizeof (th), 1, f) != 1) + if (fwrite(&h, sizeof (h), 1, f) != 1) { fclose(f); okBox(0, "System message", "General I/O error during saving! Is the file in use?"); return false; } - if (fwrite(saveBuff, pattLen * sizeof (tonTyp), 1, f) != 1) + if (fwrite(saveBuff, h.numRows * sizeof (note_t), 1, f) != 1) { fclose(f); okBox(0, "System message", "General I/O error during saving! Is the file in use?"); @@ -1277,7 +1275,7 @@ bool saveTrack(UNICHAR *filenameU) bool loadPattern(UNICHAR *filenameU) { - patternHeaderType th; + xpHdr_t h; FILE *f = UNICHAR_FOPEN(filenameU, "rb"); if (f == NULL) @@ -1286,68 +1284,66 @@ bool loadPattern(UNICHAR *filenameU) return false; } - uint16_t nr = editor.editPattern; - - if (!allocatePattern(nr)) + if (!allocatePattern(editor.editPattern)) { okBox(0, "System message", "Not enough memory!"); goto loadPattError; } - tonTyp *pattPtr = patt[nr]; - uint16_t pattLen = pattLens[nr]; - - if (fread(&th, 1, sizeof (th), f) != sizeof (th)) + if (fread(&h, 1, sizeof (h), f) != sizeof (h)) { okBox(0, "System message", "General I/O error during loading! Is the file in use?"); goto loadPattError; } - if (th.ver != 1) + if (h.version != 1) { okBox(0, "System message", "Incompatible format version!"); goto loadPattError; } - if (th.len > MAX_PATT_LEN) - th.len = MAX_PATT_LEN; - - pattLen = th.len; + if (h.numRows > MAX_PATT_LEN) + h.numRows = MAX_PATT_LEN; lockMixerCallback(); - if (fread(pattPtr, pattLen * TRACK_WIDTH, 1, f) != 1) + note_t *p = pattern[editor.editPattern]; + if (fread(p, h.numRows * TRACK_WIDTH, 1, f) != 1) { unlockMixerCallback(); okBox(0, "System message", "General I/O error during loading! Is the file in use?"); goto loadPattError; } - // non-FT2 security fix: remove overflown (illegal) stuff - for (int32_t i = 0; i < pattLen; i++) + // sanitize data (FT2 doesn't do this!) + for (int32_t row = 0; row < h.numRows; row++) { - for (int32_t j = 0; j < MAX_VOICES; j++) + for (int32_t ch = 0; ch < MAX_CHANNELS; ch++) { - pattPtr = &patt[nr][(i * MAX_VOICES) + j]; - if (pattPtr->ton > 97) - pattPtr->ton = 0; + p = &pattern[editor.editPattern][(row * MAX_CHANNELS) + ch]; + + if (p->note > 97) + p->note = 0; - if (pattPtr->effTyp > 35) + if (p->instr > 128) + p->instr = 128; + + if (p->efx > 35) { - pattPtr->effTyp = 0; - pattPtr->eff = 0; + p->efx = 0; + p->efxData = 0; } } } // set new pattern length (FT2 doesn't do this, strange...) - pattLens[nr] = pattLen; - song.pattLen = pattLen; - if (song.pattPos >= pattLen) + song.currNumRows = patternNumRows[editor.editPattern] = h.numRows; + + if (song.row >= song.currNumRows) { - song.pattPos = pattLen-1; + song.row = song.currNumRows-1; if (!songPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; } unlockMixerCallback(); @@ -1369,12 +1365,10 @@ loadPattError: bool savePattern(UNICHAR *filenameU) { - patternHeaderType th; + xpHdr_t h; - uint16_t nr = editor.editPattern; - tonTyp *pattPtr = patt[nr]; - - if (pattPtr == NULL) + note_t *p = pattern[editor.editPattern]; + if (p == NULL) { okBox(0, "System message", "The current pattern is empty!"); return false; @@ -1387,19 +1381,17 @@ bool savePattern(UNICHAR *filenameU) return false; } - uint16_t pattLen = pattLens[nr]; - - th.len = pattLen; - th.ver = 1; - - if (fwrite(&th, 1, sizeof (th), f) != sizeof (th)) + h.version = 1; + h.numRows = patternNumRows[editor.editPattern]; + + if (fwrite(&h, 1, sizeof (h), f) != sizeof (h)) { fclose(f); okBox(0, "System message", "General I/O error during saving! Is the file in use?"); return false; } - if (fwrite(pattPtr, pattLen * TRACK_WIDTH, 1, f) != 1) + if (fwrite(p, h.numRows * TRACK_WIDTH, 1, f) != 1) { fclose(f); okBox(0, "System message", "General I/O error during saving! Is the file in use?"); @@ -1433,9 +1425,9 @@ void setChannelScrollPos(uint32_t pos) ui.channelOffset = (uint8_t)pos; - assert(song.antChn > ui.numChannelsShown); - if (ui.channelOffset >= song.antChn-ui.numChannelsShown) - ui.channelOffset = (uint8_t)(song.antChn-ui.numChannelsShown); + assert(song.numChannels > ui.numChannelsShown); + if (ui.channelOffset >= song.numChannels-ui.numChannelsShown) + ui.channelOffset = (uint8_t)(song.numChannels-ui.numChannelsShown); if (cursor.ch >= ui.channelOffset+ui.numChannelsShown) { @@ -1451,26 +1443,26 @@ void setChannelScrollPos(uint32_t pos) ui.updatePatternEditor = true; } -void jumpToChannel(uint8_t channel) // for ALT+q..i ALT+a..k +void jumpToChannel(uint8_t chNr) // for ALT+q..i ALT+a..k { if (ui.sampleEditorShown || ui.instEditorShown) return; - channel %= song.antChn; - if (cursor.ch == channel) + chNr %= song.numChannels; + if (cursor.ch == chNr) return; if (ui.pattChanScrollShown) { - assert(song.antChn > ui.numChannelsShown); + assert(song.numChannels > ui.numChannelsShown); - if (channel >= ui.channelOffset+ui.numChannelsShown) - scrollBarScrollDown(SB_CHAN_SCROLL, (channel - (ui.channelOffset + ui.numChannelsShown)) + 1); - else if (channel < ui.channelOffset) - scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - channel); + if (chNr >= ui.channelOffset+ui.numChannelsShown) + scrollBarScrollDown(SB_CHAN_SCROLL, (chNr - (ui.channelOffset + ui.numChannelsShown)) + 1); + else if (chNr < ui.channelOffset) + scrollBarScrollUp(SB_CHAN_SCROLL, ui.channelOffset - chNr); } - cursor.ch = channel; // set it here since scrollBarScrollX() changes it... + cursor.ch = chNr; // set it here since scrollBarScrollX() changes it... ui.updatePatternEditor = true; } @@ -1499,17 +1491,17 @@ void pbPosEdPosDown(void) void pbPosEdIns(void) { - if (song.len >= 255) + if (song.songLength >= 255) return; lockMixerCallback(); - const uint8_t oldPatt = song.songTab[song.songPos]; + const uint8_t oldPatt = song.orders[song.songPos]; for (uint16_t i = 0; i < 255-song.songPos; i++) - song.songTab[255-i] = song.songTab[254-i]; - song.songTab[song.songPos] = oldPatt; + song.orders[255-i] = song.orders[254-i]; + song.orders[song.songPos] = oldPatt; - song.len++; + song.songLength++; ui.updatePosSections = true; ui.updatePosEdScrollBar = true; @@ -1520,7 +1512,7 @@ void pbPosEdIns(void) void pbPosEdDel(void) { - if (song.len <= 1) + if (song.songLength <= 1) return; lockMixerCallback(); @@ -1528,16 +1520,16 @@ void pbPosEdDel(void) if (song.songPos < 254) { for (uint16_t i = 0; i < 254-song.songPos; i++) - song.songTab[song.songPos+i] = song.songTab[song.songPos+1+i]; + song.orders[song.songPos+i] = song.orders[song.songPos+1+i]; } - song.len--; - if (song.repS >= song.len) - song.repS = song.len - 1; + song.songLength--; + if (song.songLoopStart >= song.songLength) + song.songLoopStart = song.songLength - 1; - if (song.songPos > song.len-1) + if (song.songPos > song.songLength-1) { - editor.songPos = song.songPos = song.len-1; + editor.songPos = song.songPos = song.songLength-1; setPos(song.songPos, -1, false); } @@ -1550,25 +1542,25 @@ void pbPosEdDel(void) void pbPosEdPattUp(void) { - if (song.songTab[song.songPos] == 255) + if (song.orders[song.songPos] == 255) return; lockMixerCallback(); - if (song.songTab[song.songPos] < 255) + if (song.orders[song.songPos] < 255) { - song.songTab[song.songPos]++; - song.pattNr = song.songTab[song.songPos]; + song.orders[song.songPos]++; + song.pattNum = song.orders[song.songPos]; - song.pattLen = pattLens[song.pattNr]; - if (song.pattPos >= song.pattLen) + song.currNumRows = patternNumRows[song.pattNum]; + if (song.row >= song.currNumRows) { - song.pattPos = song.pattLen-1; + song.row = song.currNumRows-1; if (!songPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; } if (!songPlaying) - editor.editPattern = (uint8_t)song.pattNr; + editor.editPattern = (uint8_t)song.pattNum; checkMarkLimits(); ui.updatePatternEditor = true; @@ -1581,25 +1573,25 @@ void pbPosEdPattUp(void) void pbPosEdPattDown(void) { - if (song.songTab[song.songPos] == 0) + if (song.orders[song.songPos] == 0) return; lockMixerCallback(); - if (song.songTab[song.songPos] > 0) + if (song.orders[song.songPos] > 0) { - song.songTab[song.songPos]--; - song.pattNr = song.songTab[song.songPos]; + song.orders[song.songPos]--; + song.pattNum = song.orders[song.songPos]; - song.pattLen = pattLens[song.pattNr]; - if (song.pattPos >= song.pattLen) + song.currNumRows = patternNumRows[song.pattNum]; + if (song.row >= song.currNumRows) { - song.pattPos = song.pattLen-1; + song.row = song.currNumRows-1; if (!songPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; } if (!songPlaying) - editor.editPattern = (uint8_t)song.pattNr; + editor.editPattern = (uint8_t)song.pattNum; checkMarkLimits(); ui.updatePatternEditor = true; @@ -1612,14 +1604,14 @@ void pbPosEdPattDown(void) void pbPosEdLenUp(void) { - if (song.len >= 255) + if (song.songLength >= 255) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - song.len++; + song.songLength++; ui.updatePosSections = true; ui.updatePosEdScrollBar = true; @@ -1631,21 +1623,20 @@ void pbPosEdLenUp(void) void pbPosEdLenDown(void) { - if (song.len <= 1) + if (song.songLength <= 1) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - song.len--; - - if (song.repS >= song.len) - song.repS = song.len - 1; + song.songLength--; + if (song.songLoopStart >= song.songLength) + song.songLoopStart = song.songLength - 1; - if (song.songPos >= song.len) + if (song.songPos >= song.songLength) { - song.songPos = song.len - 1; + song.songPos = song.songLength - 1; setPos(song.songPos, -1, false); } @@ -1663,9 +1654,9 @@ void pbPosEdRepSUp(void) if (audioWasntLocked) lockAudio(); - if (song.repS < song.len-1) + if (song.songLoopStart < song.songLength-1) { - song.repS++; + song.songLoopStart++; ui.updatePosSections = true; setSongModifiedFlag(); } @@ -1680,9 +1671,9 @@ void pbPosEdRepSDown(void) if (audioWasntLocked) lockAudio(); - if (song.repS > 0) + if (song.songLoopStart > 0) { - song.repS--; + song.songLoopStart--; ui.updatePosSections = true; setSongModifiedFlag(); } @@ -1693,23 +1684,23 @@ void pbPosEdRepSDown(void) void pbBPMUp(void) { - if (song.speed == 255) + if (song.BPM == 255) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (song.speed < 255) + if (song.BPM < 255) { - song.speed++; - P_SetSpeed(song.speed); + song.BPM++; + setMixerBPM(song.BPM); // if song is playing, the update is handled in the audio/video sync queue if (!songPlaying) { - editor.speed = song.speed; - drawSongBPM(song.speed); + editor.BPM = song.BPM; + drawSongBPM(song.BPM); } } @@ -1719,23 +1710,23 @@ void pbBPMUp(void) void pbBPMDown(void) { - if (song.speed == 32) + if (song.BPM == 32) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (song.speed > 32) + if (song.BPM > 32) { - song.speed--; - P_SetSpeed(song.speed); + song.BPM--; + setMixerBPM(song.BPM); // if song is playing, the update is handled in the audio/video sync queue if (!songPlaying) { - editor.speed = song.speed; - drawSongBPM(editor.speed); + editor.BPM = song.BPM; + drawSongBPM(editor.BPM); } } @@ -1745,22 +1736,22 @@ void pbBPMDown(void) void pbSpeedUp(void) { - if (song.tempo == 31) + if (song.speed == 31) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (song.tempo < 31) + if (song.speed < 31) { - song.tempo++; + song.speed++; // if song is playing, the update is handled in the audio/video sync queue if (!songPlaying) { - editor.tempo = song.tempo; - drawSongSpeed(editor.tempo); + editor.speed = song.speed; + drawSongSpeed(editor.speed); } } @@ -1770,22 +1761,22 @@ void pbSpeedUp(void) void pbSpeedDown(void) { - if (song.tempo == 0) + if (song.speed == 0) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (song.tempo > 0) + if (song.speed > 0) { - song.tempo--; + song.speed--; // if song is playing, the update is handled in the audio/video sync queue if (!songPlaying) { - editor.tempo = song.tempo; - drawSongSpeed(editor.tempo); + editor.speed = song.speed; + drawSongSpeed(editor.speed); } } @@ -1795,31 +1786,31 @@ void pbSpeedDown(void) void pbIncAdd(void) { - if (editor.ID_Add == 16) - editor.ID_Add = 0; + if (editor.editRowSkip == 16) + editor.editRowSkip = 0; else - editor.ID_Add++; + editor.editRowSkip++; drawIDAdd(); } void pbDecAdd(void) { - if (editor.ID_Add == 0) - editor.ID_Add = 16; + if (editor.editRowSkip == 0) + editor.editRowSkip = 16; else - editor.ID_Add--; + editor.editRowSkip--; drawIDAdd(); } void pbAddChan(void) { - if (song.antChn > 30) + if (song.numChannels > 30) return; lockMixerCallback(); - song.antChn += 2; + song.numChannels += 2; hideTopScreen(); showTopLeftMainScreen(true); @@ -1834,12 +1825,12 @@ void pbAddChan(void) void pbSubChan(void) { - if (song.antChn < 4) + if (song.numChannels < 4) return; lockMixerCallback(); - song.antChn -= 2; + song.numChannels -= 2; checkMarkLimits(); @@ -1860,20 +1851,20 @@ void pbEditPattUp(void) if (audioWasntLocked) lockAudio(); - if (song.pattNr < 255) + if (song.pattNum < 255) { - song.pattNr++; + song.pattNum++; - song.pattLen = pattLens[song.pattNr]; - if (song.pattPos >= song.pattLen) + song.currNumRows = patternNumRows[song.pattNum]; + if (song.row >= song.currNumRows) { - song.pattPos = song.pattLen-1; + song.row = song.currNumRows-1; if (!songPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; } if (!songPlaying) - editor.editPattern = (uint8_t)song.pattNr; + editor.editPattern = (uint8_t)song.pattNum; checkMarkLimits(); ui.updatePatternEditor = true; @@ -1890,20 +1881,20 @@ void pbEditPattDown(void) if (audioWasntLocked) lockAudio(); - if (song.pattNr > 0) + if (song.pattNum > 0) { - song.pattNr--; + song.pattNum--; - song.pattLen = pattLens[song.pattNr]; - if (song.pattPos >= song.pattLen) + song.currNumRows = patternNumRows[song.pattNum]; + if (song.row >= song.currNumRows) { - song.pattPos = song.pattLen-1; + song.row = song.currNumRows-1; if (!songPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; } if (!songPlaying) - editor.editPattern = (uint8_t)song.pattNr; + editor.editPattern = (uint8_t)song.pattNum; checkMarkLimits(); ui.updatePatternEditor = true; @@ -1916,16 +1907,16 @@ void pbEditPattDown(void) void pbPattLenUp(void) { - const uint16_t pattLen = pattLens[editor.editPattern]; - if (pattLen >= 256) + const uint16_t numRows = patternNumRows[editor.editPattern]; + if (numRows >= MAX_PATT_LEN) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - setPatternLen(editor.editPattern, pattLen + 1); - checkMarkLimits(); + song.pattNum = editor.editPattern; // kludge + setPatternLen(editor.editPattern, numRows+1); ui.updatePatternEditor = true; ui.updatePosSections = true; @@ -1937,16 +1928,16 @@ void pbPattLenUp(void) void pbPattLenDown(void) { - const uint16_t pattLen = pattLens[editor.editPattern]; - if (pattLen <= 1) + const uint16_t numRows = patternNumRows[editor.editPattern]; + if (numRows <= 1) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - setPatternLen(editor.editPattern, pattLen - 1); - checkMarkLimits(); + song.pattNum = editor.editPattern; // kludge + setPatternLen(editor.editPattern, numRows-1); ui.updatePatternEditor = true; ui.updatePosSections = true; @@ -1958,8 +1949,8 @@ void pbPattLenDown(void) void drawPosEdNums(int16_t songPos) { - if (songPos >= song.len) - songPos = song.len - 1; + if (songPos >= song.songLength) + songPos = song.songLength - 1; // clear if (ui.extended) @@ -1990,12 +1981,12 @@ void drawPosEdNums(int16_t songPos) if (ui.extended) { pattTwoHexOut(8, 4 + (y * 9), (uint8_t)entry, color1); - pattTwoHexOut(32, 4 + (y * 9), song.songTab[entry], color1); + pattTwoHexOut(32, 4 + (y * 9), song.orders[entry], color1); } else { pattTwoHexOut(8, 4 + (y * 8), (uint8_t)entry, color1); - pattTwoHexOut(32, 4 + (y * 8), song.songTab[entry], color1); + pattTwoHexOut(32, 4 + (y * 8), song.orders[entry], color1); } } @@ -2005,30 +1996,30 @@ void drawPosEdNums(int16_t songPos) if (ui.extended) { pattTwoHexOut(8, 23, (uint8_t)songPos, color2); - pattTwoHexOut(32, 23, song.songTab[songPos], color2); + pattTwoHexOut(32, 23, song.orders[songPos], color2); } else { pattTwoHexOut(8, 22, (uint8_t)songPos, color2); - pattTwoHexOut(32, 22, song.songTab[songPos], color2); + pattTwoHexOut(32, 22, song.orders[songPos], color2); } // bottom two for (int16_t y = 0; y < 2; y++) { int16_t entry = songPos + (1 + y); - if (entry >= song.len) + if (entry >= song.songLength) break; if (ui.extended) { pattTwoHexOut(8, 33 + (y * 9), (uint8_t)entry, color1); - pattTwoHexOut(32, 33 + (y * 9), song.songTab[entry], color1); + pattTwoHexOut(32, 33 + (y * 9), song.orders[entry], color1); } else { pattTwoHexOut(8, 32 + (y * 8), (uint8_t)entry, color1); - pattTwoHexOut(32, 32 + (y * 8), song.songTab[entry], color1); + pattTwoHexOut(32, 32 + (y * 8), song.orders[entry], color1); } } } @@ -2048,10 +2039,10 @@ void drawSongLength(void) y = 52; } - hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.len, 2); + hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLength, 2); } -void drawSongRepS(void) +void drawSongLoopStart(void) { int16_t x, y; @@ -2066,36 +2057,18 @@ void drawSongRepS(void) y = 64; } - hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.repS, 2); + hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, (uint8_t)song.songLoopStart, 2); } void drawSongBPM(uint16_t val) { - char str[4]; - const char *strOut; - if (ui.extended) return; - if (val <= 255) - { - strOut = dec3StrTab[val]; - } - else - { - if (val > MAX_BPM) - val = MAX_BPM; - - assert(MAX_BPM == 999); - str[0] = '0' + (char)(val / 100); - str[1] = '0' + ((val / 10) % 10); - str[2] = '0' + (val % 10); - str[3] = 0; + if (val > 255) + val = 255; - strOut = str; - } - - textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, strOut); + textOutFixed(145, 36, PAL_FORGRND, PAL_DESKTOP, dec3StrTab[val]); } void drawSongSpeed(uint16_t val) @@ -2142,7 +2115,7 @@ void drawPatternLength(uint16_t editPattern) y = 50; } - hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, pattLens[editPattern], 3); + hexOutBg(x, y, PAL_FORGRND, PAL_DESKTOP, patternNumRows[editPattern], 3); } void drawGlobalVol(uint16_t val) @@ -2156,8 +2129,8 @@ void drawGlobalVol(uint16_t val) void drawIDAdd(void) { - assert(editor.ID_Add <= 16); - textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.ID_Add]); + assert(editor.editRowSkip <= 16); + textOutFixed(152, 64, PAL_FORGRND, PAL_DESKTOP, dec2StrTab[editor.editRowSkip]); } void resetPlaybackTime(void) @@ -2173,10 +2146,14 @@ void drawPlaybackTime(void) if (songPlaying) { const uint32_t ms1024 = song.musicTime64 >> 32; // milliseconds (scaled from 1000 to 1024) - uint32_t seconds = ms1024 >> 10; - last_TimeH = seconds / 3600; seconds -= last_TimeH * 3600; - last_TimeM = seconds / 60; seconds -= last_TimeM * 60; + + last_TimeH = seconds / 3600; + seconds -= last_TimeH * 3600; + + last_TimeM = seconds / 60; + seconds -= last_TimeM * 60; + last_TimeS = seconds; } @@ -2584,37 +2561,37 @@ static void zapSong(void) { lockMixerCallback(); - song.len = 1; - song.repS = 0; // Bug: FT2 doesn't do this! - song.speed = 125; - song.tempo = 6; + song.songLength = 1; + song.songLoopStart = 0; // FT2 doesn't do this! + song.BPM = 125; + song.speed = 6; song.songPos = 0; - song.globVol = 64; + song.globalVolume = 64; memset(song.name, 0, sizeof (song.name)); - memset(song.songTab, 0, sizeof (song.songTab)); + memset(song.orders, 0, sizeof (song.orders)); // zero all pattern data and reset pattern lengths freeAllPatterns(); - for (uint16_t i = 0; i < MAX_PATTERNS; i++) - pattLens[i] = 64; - song.pattLen = pattLens[song.pattNr]; + for (int32_t i = 0; i < MAX_PATTERNS; i++) + patternNumRows[i] = 64; + song.currNumRows = patternNumRows[song.pattNum]; resetMusic(); - P_SetSpeed(song.speed); + setMixerBPM(song.BPM); editor.songPos = song.songPos; - editor.editPattern = song.pattNr; + editor.editPattern = song.pattNum; + editor.BPM = song.BPM; editor.speed = song.speed; - editor.tempo = song.tempo; - editor.globalVol = song.globVol; - editor.timer = 1; + editor.globalVolume = song.globalVolume; + editor.tick = 1; resetPlaybackTime(); if (!audio.linearPeriodsFlag) - setFrqTab(true); + setFrequencyTable(true); clearPattMark(); resetWavRenderer(); @@ -2622,7 +2599,7 @@ static void zapSong(void) unlockMixerCallback(); setScrollBarPos(SB_POS_ED, 0, false); - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5); updateWindowTitle(true); } @@ -2705,7 +2682,7 @@ void pbToggleBadge(void) void resetChannelOffset(void) { - ui.pattChanScrollShown = song.antChn > getMaxVisibleChannels(); + ui.pattChanScrollShown = song.numChannels > getMaxVisibleChannels(); cursor.object = CURSOR_NOTE; cursor.ch = 0; setScrollBarPos(SB_CHAN_SCROLL, 0, true); @@ -2717,48 +2694,46 @@ void shrinkPattern(void) if (okBox(2, "System request", "Shrink pattern?") != 1) return; - uint16_t nr = editor.editPattern; + int16_t numRows = patternNumRows[editor.editPattern]; + if (numRows <= 1) + return; - int16_t pattLen = pattLens[nr]; - if (pattLen > 1) - { - lockMixerCallback(); + lockMixerCallback(); - tonTyp *pattPtr = patt[nr]; - if (pattPtr != NULL) + note_t *p = pattern[editor.editPattern]; + if (p != NULL) + { + const int32_t length = numRows >> 1; + for (int32_t i = 0; i < length; i++) { - for (int32_t i = 0; i < pattLen/2; i++) - { - for (int32_t j = 0; j < MAX_VOICES; j++) - pattPtr[(i * MAX_VOICES) + j] = pattPtr[((i * 2) * MAX_VOICES) + j]; - } + for (int32_t j = 0; j < MAX_CHANNELS; j++) + p[(i * MAX_CHANNELS) + j] = p[((i*2) * MAX_CHANNELS) + j]; } + } - pattLens[nr] >>= 1; + patternNumRows[editor.editPattern] >>= 1; + numRows = patternNumRows[editor.editPattern]; - if (song.pattNr == nr) - song.pattLen = pattLens[nr]; + if (song.pattNum == editor.editPattern) + song.currNumRows = numRows; - song.pattPos >>= 1; - if (song.pattPos >= pattLens[nr]) - song.pattPos = pattLens[nr] - 1; + song.row >>= 1; + if (song.row >= numRows) + song.row = numRows-1; - editor.pattPos = song.pattPos; + editor.row = song.row; - ui.updatePatternEditor = true; - ui.updatePosSections = true; + ui.updatePatternEditor = true; + ui.updatePosSections = true; - unlockMixerCallback(); - setSongModifiedFlag(); - } + unlockMixerCallback(); + setSongModifiedFlag(); } void expandPattern(void) { - uint16_t nr = editor.editPattern; - - int16_t pattLen = pattLens[nr]; - if (pattLen > 128) + int16_t numRows = patternNumRows[editor.editPattern]; + if (numRows > 128) { okBox(0, "System message", "Pattern is too long to be expanded."); } @@ -2766,9 +2741,9 @@ void expandPattern(void) { lockMixerCallback(); - if (patt[nr] != NULL) + if (pattern[editor.editPattern] != NULL) { - tonTyp *tmpPtn = (tonTyp *)malloc((pattLen * 2) * TRACK_WIDTH); + note_t *tmpPtn = (note_t *)malloc((numRows * 2) * TRACK_WIDTH); if (tmpPtn == NULL) { unlockMixerCallback(); @@ -2776,28 +2751,29 @@ void expandPattern(void) return; } - for (int32_t i = 0; i < pattLen; i++) + for (int32_t i = 0; i < numRows; i++) { - for (int32_t j = 0; j < MAX_VOICES; j++) - tmpPtn[((i * 2) * MAX_VOICES) + j] = patt[nr][(i * MAX_VOICES) + j]; + for (int32_t j = 0; j < MAX_CHANNELS; j++) + tmpPtn[((i * 2) * MAX_CHANNELS) + j] = pattern[editor.editPattern][(i * MAX_CHANNELS) + j]; - memset(&tmpPtn[((i * 2) + 1) * MAX_VOICES], 0, TRACK_WIDTH); + memset(&tmpPtn[((i * 2) + 1) * MAX_CHANNELS], 0, TRACK_WIDTH); } - free(patt[nr]); - patt[nr] = tmpPtn; + free(pattern[editor.editPattern]); + pattern[editor.editPattern] = tmpPtn; } - pattLens[nr] *= 2; + patternNumRows[editor.editPattern] *= 2; + numRows = patternNumRows[editor.editPattern]; - if (song.pattNr == nr) - song.pattLen = pattLens[nr]; + if (song.pattNum == editor.editPattern) + song.currNumRows = numRows; - song.pattPos *= 2; - if (song.pattPos >= pattLens[nr]) - song.pattPos = pattLens[nr] - 1; + song.row *= 2; + if (song.row >= numRows) + song.row = numRows-1; - editor.pattPos = song.pattPos; + editor.row = song.row; ui.updatePatternEditor = true; ui.updatePosSections = true; diff --git a/src/ft2_pattern_ed.h b/src/ft2_pattern_ed.h @@ -18,15 +18,15 @@ enum TRANSP_BLOCK = 3 }; -typedef struct trackHeaderType_t +typedef struct xtHdr_t { - uint16_t ver, len; -} trackHeaderType; + uint16_t version, numRows; +} xtHdr_t; -typedef struct patternHeaderType_t +typedef struct xpHdr_t { - uint16_t ver, len; -} patternHeaderType; + uint16_t version, numRows; +} xpHdr_t; typedef struct pattCoord_t { @@ -61,8 +61,8 @@ extern pattMark_t pattMark; // ft2_pattern_ed.c void resetPlaybackTime(void); -bool allocatePattern(uint16_t nr); -void killPatternIfUnused(uint16_t nr); +bool allocatePattern(uint16_t pattNum); +void killPatternIfUnused(uint16_t pattNum); uint8_t getMaxVisibleChannels(void); void updatePatternWidth(void); void updateAdvEdit(void); @@ -106,7 +106,7 @@ bool savePattern(UNICHAR *filenameU); void scrollChannelLeft(void); void scrollChannelRight(void); void setChannelScrollPos(uint32_t pos); -void jumpToChannel(uint8_t channel); // for ALT+q..i ALT+a..k +void jumpToChannel(uint8_t chNr); // for ALT+q..i ALT+a..k void sbPosEdPos(uint32_t pos); void pbPosEdPosUp(void); void pbPosEdPosDown(void); @@ -132,7 +132,7 @@ void pbPattLenUp(void); void pbPattLenDown(void); void drawPosEdNums(int16_t songPos); void drawSongLength(void); -void drawSongRepS(void); +void drawSongLoopStart(void); void drawSongBPM(uint16_t val); void drawSongSpeed(uint16_t val); void drawEditPattern(uint16_t editPattern); diff --git a/src/ft2_pushbuttons.c b/src/ft2_pushbuttons.c @@ -219,7 +219,7 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] = { 300, 382, 50, 16, 0, 0, "X-Fade", NULL, NULL, sampXFade }, { 430, 348, 54, 16, 0, 0, "Exit", NULL, NULL, exitSampleEditor }, { 594, 348, 35, 13, 0, 0, "Clr S.", NULL, NULL, clearSample }, - { 594, 360, 35, 13, 0, 0, "Min.", NULL, NULL, sampMin }, + { 594, 360, 35, 13, 0, 0, "Min.", NULL, NULL, sampMinimize }, { 594, 373, 18, 13, 2, 4, ARROW_UP_STRING, NULL, sampRepeatUp, NULL }, { 611, 373, 18, 13, 2, 4, ARROW_DOWN_STRING, NULL, sampRepeatDown, NULL }, { 594, 385, 18, 13, 2, 4, ARROW_UP_STRING, NULL, sampReplenUp, NULL }, @@ -228,10 +228,10 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] = // ------ SAMPLE EDITOR EXTENSION PUSHBUTTONS ------ //x, y, w, h, p, d, text #1, text #2, funcOnDown, funcOnUp { 3, 138, 52, 16, 0, 0, "Clr. c.bf", NULL, NULL, clearCopyBuffer }, - { 56, 138, 49, 16, 0, 0, "Conv", NULL, NULL, sampleConv }, + { 56, 138, 49, 16, 0, 0, "Sign", NULL, NULL, sampleChangeSign }, { 106, 138, 49, 16, 0, 0, "Echo", NULL, NULL, pbSampleEcho }, { 3, 155, 52, 16, 0, 0, "Backw.", NULL, NULL, sampleBackwards }, - { 56, 155, 49, 16, 0, 0, "Conv W", NULL, NULL, sampleConvW }, + { 56, 155, 49, 16, 0, 0, "B. swap", NULL, NULL, sampleByteSwap }, { 106, 155, 49, 16, 0, 0, "Fix DC", NULL, NULL, fixDC }, { 161, 121, 60, 16, 0, 0, "Copy ins.", NULL, NULL, copyInstr }, { 222, 121, 66, 16, 0, 0, "Copy smp.", NULL, NULL, copySmp }, @@ -285,10 +285,10 @@ pushButton_t pushButtons[NUM_PUSHBUTTONS] = { 606, 248, 23, 13, 1, 4, ARROW_RIGHT_STRING, NULL, vibDepthUp, NULL }, { 521, 262, 23, 13, 1, 4, ARROW_LEFT_STRING, NULL, vibSweepDown, NULL }, { 606, 262, 23, 13, 1, 4, ARROW_RIGHT_STRING, NULL, vibSweepUp, NULL }, - { 441, 312, 94, 16, 1, 4, "Octave up", NULL, relToneOctUp, NULL }, - { 536, 312, 93, 16, 1, 4, "Halftone up", NULL, relToneUp, NULL }, - { 441, 329, 94, 16, 1, 4, "Octave down", NULL, relToneOctDown, NULL }, - { 536, 329, 93, 16, 1, 4, "Halftone down", NULL, relToneDown, NULL }, + { 441, 312, 94, 16, 1, 4, "Octave up", NULL, relativeNoteOctUp, NULL }, + { 536, 312, 93, 16, 1, 4, "Halftone up", NULL, relativeNoteUp, NULL }, + { 441, 329, 94, 16, 1, 4, "Octave down", NULL, relativeNoteOctDown, NULL }, + { 536, 329, 93, 16, 1, 4, "Halftone down", NULL, relativeNoteDown, NULL }, // ------ INSTRUMENT EDITOR EXTENSION PUSHBUTTONS ------ //x, y, w, h, p, d, text #1, text #2, funcOnDown, funcOnUp diff --git a/src/ft2_radiobuttons.c b/src/ft2_radiobuttons.c @@ -33,7 +33,7 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] = //x, y, w, group, funcOnUp { 5, 18, 69, RB_GROUP_HELP, rbHelpFeatures }, { 5, 34, 60, RB_GROUP_HELP, rbHelpEffects }, - { 5, 50, 71, RB_GROUP_HELP, rbHelpKeyboard }, + { 5, 50, 86, RB_GROUP_HELP, rbHelpKeybindings }, { 5, 66, 109, RB_GROUP_HELP, rbHelpHowToUseFT2 }, { 5, 82, 101, RB_GROUP_HELP, rbHelpFAQ }, { 5, 98, 86, RB_GROUP_HELP, rbHelpKnownBugs }, @@ -74,7 +74,7 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] = // ------ CONFIG AUDIO ------ // audio buffer size - //x, y, w, group, funcOnUp + //x, y, w, group, funcOnUp { 390, 16, 46, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs512 }, { 390, 30, 113, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs1024 }, { 390, 44, 50, RB_GROUP_CONFIG_SOUND_BUFF_SIZE, rbConfigSbs2048 }, @@ -94,11 +94,13 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] = //x, y, w, group, funcOnUp { 509, 16, 66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio44kHz }, { 509, 30, 121, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio48kHz }, +#if CPU_64BIT { 509, 44, 66, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio96kHz }, { 509, 58, 73, RB_GROUP_CONFIG_AUDIO_FREQ, rbConfigAudio192kHz }, +#endif // audio input frequency - //x, y, w, group, funcOnUp + //x, y, w, group, funcOnUp { 180, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput44kHz }, { 251, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput48kHz }, { 322, 156, 60, RB_GROUP_CONFIG_AUDIO_INPUT_FREQ, rbConfigAudioInput96kHz }, @@ -128,7 +130,7 @@ radioButton_t radioButtons[NUM_RADIOBUTTONS] = { 346, 145, 46, RB_GROUP_CONFIG_SCOPE, rbConfigScopeLined }, // visible pattern channels - //x, y, w, group, funcOnUp + //x, y, w, group, funcOnUp { 257, 42, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt4Chans }, { 257, 56, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt6Chans }, { 257, 70, 78, RB_GROUP_CONFIG_PATTERN_CHANS, rbConfigPatt8Chans }, diff --git a/src/ft2_radiobuttons.h b/src/ft2_radiobuttons.h @@ -2,13 +2,14 @@ #include <stdint.h> #include <stdbool.h> +#include "ft2_cpu.h" enum // RADIOBUTTONS { // HELP RB_HELP_FEATURES, RB_HELP_EFFECTS, - RB_HELP_KEYBOARD, + RB_HELP_KEYBINDINGS, RB_HELP_HOW_TO_USE_FT2, RB_HELP_FAQ, RB_HELP_KNOWN_BUGS, @@ -62,8 +63,10 @@ enum // RADIOBUTTONS // AUDIO FREQUENCY RB_CONFIG_AUDIO_44KHZ, RB_CONFIG_AUDIO_48KHZ, +#if CPU_64BIT RB_CONFIG_AUDIO_96KHZ, RB_CONFIG_AUDIO_192KHZ, +#endif // AUDIO INPUT FREQUENCY RB_CONFIG_AUDIO_INPUT_44KHZ, diff --git a/src/ft2_replayer.c b/src/ft2_replayer.c @@ -15,7 +15,7 @@ #include "ft2_inst_ed.h" #include "ft2_diskop.h" #include "ft2_midi.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_mouse.h" #include "ft2_sample_loader.h" #include "ft2_tables.h" @@ -29,22 +29,22 @@ static double dLogTab[768], dExp2MulTab[32]; static bool bxxOverflow; -static tonTyp nilPatternLine[MAX_VOICES]; +static note_t nilPatternLine[MAX_CHANNELS]; -typedef void (*volKolEfxRoutine)(stmTyp *ch); -typedef void (*volKolEfxRoutine2)(stmTyp *ch, uint8_t *volKol); -typedef void (*efxRoutine)(stmTyp *ch, uint8_t param); +typedef void (*volColumnEfxRoutine)(channel_t *ch); +typedef void (*volColumnEfxRoutine2)(channel_t *ch, uint8_t *volColumnData); +typedef void (*efxRoutine)(channel_t *ch, uint8_t param); // globally accessed int8_t playMode = 0; bool songPlaying = false, audioPaused = false, musicPaused = false; volatile bool replayerBusy = false; const uint16_t *note2Period = NULL; -int16_t pattLens[MAX_PATTERNS]; -stmTyp stm[MAX_VOICES]; -songTyp song; -instrTyp *instr[132]; -tonTyp *patt[MAX_PATTERNS]; +int16_t patternNumRows[MAX_PATTERNS]; +channel_t channel[MAX_CHANNELS]; +song_t song; +instr_t *instr[132]; +note_t *pattern[MAX_PATTERNS]; // ---------------------------------- void fixString(char *str, int32_t lastChrPos) // removes leading spaces and 0x1A chars @@ -64,12 +64,12 @@ void fixSongName(void) fixString(song.name, 19); } -void fixInstrAndSampleNames(int16_t nr) +void fixInstrAndSampleNames(int16_t insNum) { - fixString(song.instrName[nr], 21); - if (instr[nr] != NULL) + fixString(song.instrName[insNum], 21); + if (instr[insNum] != NULL) { - sampleTyp *s = instr[nr]->samp; + sample_t *s = instr[insNum]->smp; for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) fixString(s->name, 21); } @@ -81,10 +81,10 @@ void resetChannels(void) if (audioWasntLocked) lockAudio(); - memset(stm, 0, sizeof (stm)); + memset(channel, 0, sizeof (channel)); - stmTyp *ch = stm; - for (int32_t i = 0; i < MAX_VOICES; i++, ch++) + channel_t *ch = channel; + for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) { ch->instrPtr = instr[0]; ch->status = IS_Vol; @@ -92,7 +92,7 @@ void resetChannels(void) ch->outPan = 128; ch->finalPan = 128; - ch->stOff = !editor.chnMode[i]; // set channel mute flag from global mute flag + ch->channelOff = !editor.chnMode[i]; // set channel mute flag from global mute flag } if (audioWasntLocked) @@ -112,7 +112,7 @@ void removeSongModifiedFlag(void) } // used on external sample load and during sample loading in some module formats -void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag) +void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag) { #define NOTE_C4 (4*12) #define MIN_PERIOD (0) @@ -123,7 +123,7 @@ void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag) if (midCFreq <= 0 || periodTab == NULL) { - s->fine = s->relTon = 0; + s->finetune = s->relativeNote = 0; return; } @@ -131,15 +131,15 @@ void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag) if (midCFreq <= (int32_t)dGetHzFromPeriod(periodTab[MIN_PERIOD])) { - s->fine = -128; - s->relTon = -48; + s->finetune = -128; + s->relativeNote = -48; return; } if (midCFreq >= (int32_t)dGetHzFromPeriod(periodTab[MAX_PERIOD])) { - s->fine = 127; - s->relTon = 71; + s->finetune = 127; + s->relativeNote = 71; return; } @@ -149,8 +149,8 @@ void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag) { if (midCFreq == (int32_t)dGetHzFromPeriod(periodTab[16 + (i<<4)])) { - s->fine = 0; - s->relTon = i - NOTE_C4; + s->finetune = 0; + s->relativeNote = i - NOTE_C4; return; } } @@ -178,30 +178,37 @@ void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag) } } - s->fine = ((period & 31) - 16) << 3; - s->relTon = (int8_t)(((period & ~31) >> 4) - NOTE_C4); + s->finetune = ((period & 31) - 16) << 3; + s->relativeNote = (int8_t)(((period & ~31) >> 4) - NOTE_C4); } -void setPatternLen(uint16_t nr, int16_t len) +void setPatternLen(uint16_t pattNum, int16_t numRows) { - assert(nr < MAX_PATTERNS); - if ((len < 1 || len > MAX_PATT_LEN) || len == pattLens[nr]) + assert(pattNum < MAX_PATTERNS); + if ((numRows < 1 || numRows > MAX_PATT_LEN) || numRows == patternNumRows[pattNum]) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - pattLens[nr] = len; + patternNumRows[pattNum] = numRows; - if (patt[nr] != NULL) - killPatternIfUnused(nr); + if (pattern[pattNum] != NULL) + killPatternIfUnused(pattNum); - song.pattLen = pattLens[nr]; - if (song.pattPos >= song.pattLen) + // non-FT2 security + song.pattDelTime = 0; + song.pattDelTime2 = 0; + song.pBreakFlag = false; + song.posJumpFlag = false; + song.pBreakPos = 0; + + song.currNumRows = numRows; + if (song.row >= song.currNumRows) { - song.pattPos = song.pattLen - 1; - editor.pattPos = song.pattPos; + song.row = song.currNumRows - 1; + editor.row = song.row; } checkMarkLimits(); @@ -213,15 +220,15 @@ void setPatternLen(uint16_t nr, int16_t len) ui.updatePosSections = true; } -int16_t getUsedSamples(int16_t nr) +int16_t getUsedSamples(int16_t smpNum) { - if (instr[nr] == NULL) + if (instr[smpNum] == NULL) return 0; - instrTyp *ins = instr[nr]; + instr_t *ins = instr[smpNum]; int16_t i = 16 - 1; - while (i >= 0 && ins->samp[i].pek == NULL && ins->samp[i].name[0] == '\0') + while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0') i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 @@ -229,20 +236,20 @@ int16_t getUsedSamples(int16_t nr) */ for (int16_t j = 0; j < 96; j++) { - if (ins->ta[j] > i) - i = ins->ta[j]; + if (ins->note2SampleLUT[j] > i) + i = ins->note2SampleLUT[j]; } return i+1; } -int16_t getRealUsedSamples(int16_t nr) +int16_t getRealUsedSamples(int16_t smpNum) { - if (instr[nr] == NULL) + if (instr[smpNum] == NULL) return 0; int8_t i = 16 - 1; - while (i >= 0 && instr[nr]->samp[i].pek == NULL) + while (i >= 0 && instr[smpNum]->smp[i].dataPtr == NULL) i--; return i+1; @@ -253,9 +260,10 @@ double dLinearPeriod2Hz(int32_t period) if (period == 0) return 0.0; // in FT2, a period of 0 results in 0Hz - const uint16_t invPeriod = (12 * 192 * 4) - (uint16_t)period; // intentionally underflows (uint16_t) to be FT2-accurate - const int32_t quotient = invPeriod / 768; - const int32_t remainder = invPeriod % 768; + const uint32_t invPeriod = ((12 * 192 * 4) - period) & 0xFFFF; // mask needed for FT2 frequency quirk + + const uint32_t quotient = invPeriod / 768; + const uint32_t remainder = invPeriod % 768; return dLogTab[remainder] * dExp2MulTab[(14-quotient) & 31]; // x = y / 2^((14-quotient) & 31) } @@ -273,24 +281,24 @@ double dPeriod2Hz(int32_t period) return audio.linearPeriodsFlag ? dLinearPeriod2Hz(period) : dAmigaPeriod2Hz(period); } -// returns *exact* FT2 C-4 voice rate (depending on finetune, relative note and linear/Amiga period mode) -double getSampleC4Rate(sampleTyp *s) +// returns *exact* FT2 C-4 voice rate (depending on finetune, relativeNote and linear/Amiga period mode) +double getSampleC4Rate(sample_t *s) { - int32_t note = (96/2) + s->relTon; + int32_t note = (96/2) + s->relativeNote; if (note >= (10*12)-1) - return -1; // B-9 (from relTon) = illegal! (won't play in replayer) + return -1; // B-9 (from relativeTone) = illegal! (won't play in replayer) - const int32_t C4Period = (note << 4) + (((int8_t)s->fine >> 3) + 16); + const int32_t C4Period = (note << 4) + (((int8_t)s->finetune >> 3) + 16); const uint16_t period = audio.linearPeriodsFlag ? linearPeriods[C4Period] : amigaPeriods[C4Period]; return dPeriod2Hz(period); } -void setFrqTab(bool linear) +void setFrequencyTable(bool linearPeriodsFlag) { pauseAudio(); - audio.linearPeriodsFlag = linear; + audio.linearPeriodsFlag = linearPeriodsFlag; if (audio.linearPeriodsFlag) note2Period = linearPeriods; @@ -310,7 +318,7 @@ void setFrqTab(bool linear) } } -static void retrigVolume(stmTyp *ch) +static void retrigVolume(channel_t *ch) { ch->realVol = ch->oldVol; ch->outVol = ch->oldVol; @@ -318,7 +326,7 @@ static void retrigVolume(stmTyp *ch) ch->status |= IS_Vol + IS_Pan + IS_QuickVol; } -static void retrigEnvelopeVibrato(stmTyp *ch) +static void retrigEnvelopeVibrato(channel_t *ch) { if (!(ch->waveCtrl & 0x04)) ch->vibPos = 0; if (!(ch->waveCtrl & 0x40)) ch->tremPos = 0; @@ -328,23 +336,23 @@ static void retrigEnvelopeVibrato(stmTyp *ch) ch->envSustainActive = true; - instrTyp *ins = ch->instrPtr; + instr_t *ins = ch->instrPtr; assert(ins != NULL); - if (ins->envVTyp & 1) + if (ins->volEnvFlags & ENV_ENABLED) { - ch->envVCnt = 65535; - ch->envVPos = 0; + ch->volEnvTick = 65535; + ch->volEnvPos = 0; } - if (ins->envPTyp & 1) + if (ins->panEnvFlags & ENV_ENABLED) { - ch->envPCnt = 65535; - ch->envPPos = 0; + ch->panEnvTick = 65535; + ch->panEnvPos = 0; } - ch->fadeOutSpeed = ins->fadeOut; // FT2 doesn't check if fadeout is more than 4095 - ch->fadeOutAmp = 32768; + ch->fadeoutSpeed = ins->fadeout; // FT2 doesn't check if fadeout is more than 4095! + ch->fadeoutVol = 32768; if (ins->vibDepth > 0) { @@ -363,23 +371,17 @@ static void retrigEnvelopeVibrato(stmTyp *ch) } } -void keyOff(stmTyp *ch) +void keyOff(channel_t *ch) { ch->envSustainActive = false; - instrTyp *ins = ch->instrPtr; + instr_t *ins = ch->instrPtr; assert(ins != NULL); - if (!(ins->envPTyp & 1)) // yes, FT2 does this (!). Most likely a bug? + if (ins->volEnvFlags & ENV_ENABLED) { - if (ch->envPCnt >= (uint16_t)ins->envPP[ch->envPPos][0]) - ch->envPCnt = ins->envPP[ch->envPPos][0] - 1; - } - - if (ins->envVTyp & 1) - { - if (ch->envVCnt >= (uint16_t)ins->envVP[ch->envVPos][0]) - ch->envVCnt = ins->envVP[ch->envVPos][0] - 1; + if (ch->volEnvTick >= (uint16_t)ins->volEnvPoints[ch->volEnvPos][0]) + ch->volEnvTick = ins->volEnvPoints[ch->volEnvPos][0] - 1; } else { @@ -387,6 +389,12 @@ void keyOff(stmTyp *ch) ch->outVol = 0; ch->status |= IS_Vol + IS_QuickVol; } + + if (!(ins->panEnvFlags & ENV_ENABLED)) // What..? Probably an FT2 bug. + { + if (ch->panEnvTick >= (uint16_t)ins->panEnvPoints[ch->panEnvPos][0]) + ch->panEnvTick = ins->panEnvPoints[ch->panEnvPos][0] - 1; + } } void calcReplayerLogTab(void) @@ -395,7 +403,7 @@ void calcReplayerLogTab(void) dExp2MulTab[i] = 1.0 / exp2(i); // 1/2^i for (int32_t i = 0; i < 768; i++) - dLogTab[i] = 8363.0 * 256.0 * exp2(i * (1.0 / 768.0)); + dLogTab[i] = 8363.0 * 256.0 * exp2(i / 768.0); } void calcReplayerVars(int32_t audioFreq) @@ -405,18 +413,18 @@ void calcReplayerVars(int32_t audioFreq) return; audio.dHz2MixDeltaMul = (double)MIXER_FRAC_SCALE / audioFreq; - audio.quickVolRampSamples = (int32_t)((audioFreq / 200.0) + 0.5); // rounded - audio.dRampQuickVolMul = 1.0 / audio.quickVolRampSamples; - - audio.dSamplesPerTickTab[0] = 0.0; - audio.tickTimeTab[0] = UINT64_MAX; - audio.dRampTickMulTab[0] = 0.0; + audio.quickVolRampSamples = (int32_t)round(audioFreq / 200.0); + audio.fRampQuickVolMul = (float)(1.0 / audio.quickVolRampSamples); - for (int32_t i = MIN_BPM; i <= MAX_BPM; i++) + for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++) { - const double dBpmHz = i * (1.0 / 2.5); // i / 2.5 + const int32_t i = bpm - MIN_BPM; // index for tables + + const double dBpmHz = bpm / 2.5; const double dSamplesPerTick = audioFreq / dBpmHz; - audio.dSamplesPerTickTab[i] = dSamplesPerTick; + + // convert to 32.32 fixed-point + audio.samplesPerTick64Tab[i] = (int64_t)((dSamplesPerTick * (UINT32_MAX+1.0)) + 0.5); // rounded // BPM Hz -> tick length for performance counter (syncing visuals to audio) double dTimeInt; @@ -425,11 +433,12 @@ void calcReplayerVars(int32_t audioFreq) dTimeFrac = floor((UINT32_MAX+1.0) * dTimeFrac); // fractional part (scaled to 0..2^32-1) - audio.tickTimeTab[i] = ((uint64_t)timeInt << 32) | (uint32_t)dTimeFrac; + audio.tickTimeTab[i] = (uint32_t)timeInt; + audio.tickTimeFracTab[i] = (uint32_t)dTimeFrac; // for calculating volume ramp length for tick-lenghted ramps - const int32_t samplesPerTick = (int32_t)(dSamplesPerTick + 0.5); // this has to be rounded first - audio.dRampTickMulTab[i] = 1.0 / samplesPerTick; + const int32_t samplesPerTickRounded = (int32_t)(dSamplesPerTick + 0.5); // must be rounded + audio.fRampTickMulTab[i] = (float)(1.0 / samplesPerTickRounded); } } @@ -438,7 +447,7 @@ int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) // fo assert(note2Period != NULL); if (period > note2Period[0]) - return -1; // out of lower range on piano + return -1; // outside lower range on piano if (audio.linearPeriodsFlag) { @@ -449,7 +458,7 @@ int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) // fo return note; } - /* Amiga periods require a slower method... + /* Amiga periods require a slightly slower method... ** This is not 100% accurate for all periods, but should be faster ** than using log2() and floating-point arithmetics. */ @@ -478,57 +487,56 @@ int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote) // fo return note; } -static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) +static void startTone(uint8_t note, uint8_t efx, uint8_t efxData, channel_t *ch) { - if (ton == 97) + if (note == NOTE_OFF) { keyOff(ch); return; } // if we came from Rxy (retrig), we didn't check note (Ton) yet - if (ton == 0) + if (note == 0) { - ton = ch->tonNr; - if (ton == 0) + note = ch->noteNum; + if (note == 0) return; // if still no note, exit from routine } - ch->tonNr = ton; + ch->noteNum = note; - assert(ch->instrNr <= 130); - instrTyp *ins = instr[ch->instrNr]; + assert(ch->instrNum <= 130); + instr_t *ins = instr[ch->instrNum]; if (ins == NULL) ins = instr[0]; // empty instruments use this placeholder instrument ch->instrPtr = ins; ch->mute = ins->mute; - if (ton > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now) - ton = 96; + if (note > 96) // non-FT2 security (should never happen because I clamp in the patt. loader now) + note = 96; - const uint8_t smp = ins->ta[ton-1] & 0xF; - ch->sampleNr = smp; + ch->smpNum = ins->note2SampleLUT[note-1] & 0xF; // FT2 doesn't AND here, but let's do it for safety + sample_t *s = &ins->smp[ch->smpNum]; - sampleTyp *s = &ins->samp[smp]; ch->smpPtr = s; - ch->relTonNr = s->relTon; + ch->relativeNote = s->relativeNote; - ton += ch->relTonNr; - if (ton >= 10*12) + note += ch->relativeNote; + if (note >= 10*12) return; - ch->oldVol = s->vol; - ch->oldPan = s->pan; + ch->oldVol = s->volume; + ch->oldPan = s->panning; - if (effTyp == 0x0E && (eff & 0xF0) == 0x50) - ch->fineTune = ((eff & 0x0F) << 4) - 128; // result is now -128..127 + if (efx == 0xE && (efxData & 0xF0) == 0x50) + ch->finetune = ((efxData & 0x0F) << 4) - 128; else - ch->fineTune = s->fine; + ch->finetune = s->finetune; - if (ton != 0) + if (note != 0) { - const uint16_t tmpTon = ((ton-1) << 4) + (((int8_t)ch->fineTune >> 3) + 16); // 0..1935 + const uint16_t tmpTon = ((note-1) << 4) + (((int8_t)ch->finetune >> 3) + 16); // 0..1935 if (tmpTon < MAX_NOTES) // tmpTon is *always* below MAX_NOTES here, so this check is not really needed { assert(note2Period != NULL); @@ -536,12 +544,12 @@ static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) } } - ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol; + ch->status |= IS_Period + IS_Vol + IS_Pan + IS_Trigger + IS_QuickVol; - if (effTyp == 9) + if (efx == 9) { - if (eff) - ch->smpOffset = ch->eff; + if (efxData > 0) + ch->smpOffset = ch->efxData; ch->smpStartPos = ch->smpOffset << 8; } @@ -551,18 +559,18 @@ static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) } } -static void volume(stmTyp *ch, uint8_t param); // volume slide -static void vibrato2(stmTyp *ch); -static void tonePorta(stmTyp *ch, uint8_t param); +static void volume(channel_t *ch, uint8_t param); // volume slide +static void vibrato2(channel_t *ch); +static void tonePorta(channel_t *ch, uint8_t param); -static void dummy(stmTyp *ch, uint8_t param) +static void dummy(channel_t *ch, uint8_t param) { (void)ch; (void)param; return; } -static void finePortaUp(stmTyp *ch, uint8_t param) +static void finePortaUp(channel_t *ch, uint8_t param) { if (param == 0) param = ch->fPortaUpSpeed; @@ -577,7 +585,7 @@ static void finePortaUp(stmTyp *ch, uint8_t param) ch->status |= IS_Period; } -static void finePortaDown(stmTyp *ch, uint8_t param) +static void finePortaDown(channel_t *ch, uint8_t param) { if (param == 0) param = ch->fPortaDownSpeed; @@ -592,42 +600,42 @@ static void finePortaDown(stmTyp *ch, uint8_t param) ch->status |= IS_Period; } -static void setGlissCtrl(stmTyp *ch, uint8_t param) +static void setGlissCtrl(channel_t *ch, uint8_t param) { ch->glissFunk = param; } -static void setVibratoCtrl(stmTyp *ch, uint8_t param) +static void setVibratoCtrl(channel_t *ch, uint8_t param) { ch->waveCtrl = (ch->waveCtrl & 0xF0) | param; } -static void jumpLoop(stmTyp *ch, uint8_t param) +static void jumpLoop(channel_t *ch, uint8_t param) { if (param == 0) { - ch->pattPos = song.pattPos & 0xFF; + ch->jumpToRow = song.row & 0xFF; } - else if (ch->loopCnt == 0) + else if (ch->patLoopCounter == 0) { - ch->loopCnt = param; + ch->patLoopCounter = param; - song.pBreakPos = ch->pattPos; + song.pBreakPos = ch->jumpToRow; song.pBreakFlag = true; } - else if (--ch->loopCnt > 0) + else if (--ch->patLoopCounter > 0) { - song.pBreakPos = ch->pattPos; + song.pBreakPos = ch->jumpToRow; song.pBreakFlag = true; } } -static void setTremoloCtrl(stmTyp *ch, uint8_t param) +static void setTremoloCtrl(channel_t *ch, uint8_t param) { ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F); } -static void volFineUp(stmTyp *ch, uint8_t param) +static void volFineUp(channel_t *ch, uint8_t param) { if (param == 0) param = ch->fVolSlideUpSpeed; @@ -642,7 +650,7 @@ static void volFineUp(stmTyp *ch, uint8_t param) ch->status |= IS_Vol; } -static void volFineDown(stmTyp *ch, uint8_t param) +static void volFineDown(channel_t *ch, uint8_t param) { if (param == 0) param = ch->fVolSlideDownSpeed; @@ -657,7 +665,7 @@ static void volFineDown(stmTyp *ch, uint8_t param) ch->status |= IS_Vol; } -static void noteCut0(stmTyp *ch, uint8_t param) +static void noteCut0(channel_t *ch, uint8_t param) { if (param == 0) // only a parameter of zero is handled here { @@ -667,7 +675,7 @@ static void noteCut0(stmTyp *ch, uint8_t param) } } -static void pattDelay(stmTyp *ch, uint8_t param) +static void pattDelay(channel_t *ch, uint8_t param) { if (song.pattDelTime2 == 0) song.pattDelTime = param + 1; @@ -695,12 +703,12 @@ static const efxRoutine EJumpTab_TickZero[16] = dummy // F }; -static void E_Effects_TickZero(stmTyp *ch, uint8_t param) +static void E_Effects_TickZero(channel_t *ch, uint8_t param) { const uint8_t efx = param >> 4; param &= 0x0F; - if (ch->stOff) // channel is muted, only handle some E effects + if (ch->channelOff) // channel is muted, only handle some E effects { if (efx == 0x6) jumpLoop(ch, param); else if (efx == 0xE) pattDelay(ch, param); @@ -711,12 +719,12 @@ static void E_Effects_TickZero(stmTyp *ch, uint8_t param) EJumpTab_TickZero[efx](ch, param); } -static void posJump(stmTyp *ch, uint8_t param) +static void posJump(channel_t *ch, uint8_t param) { if (playMode != PLAYMODE_PATT && playMode != PLAYMODE_RECPATT) { const int16_t pos = (int16_t)param - 1; - if (pos < 0 || pos >= song.len) + if (pos < 0 || pos >= song.songLength) bxxOverflow = true; // non-FT2 security fix... else song.songPos = pos; @@ -728,90 +736,93 @@ static void posJump(stmTyp *ch, uint8_t param) (void)ch; } -static void pattBreak(stmTyp *ch, uint8_t param) +static void pattBreak(channel_t *ch, uint8_t param) { - song.posJumpFlag = true; - param = ((param >> 4) * 10) + (param & 0x0F); if (param <= 63) song.pBreakPos = param; else song.pBreakPos = 0; + song.posJumpFlag = true; + (void)ch; } -static void setSpeed(stmTyp *ch, uint8_t param) +static void setSpeed(channel_t *ch, uint8_t param) { if (param >= 32) { - song.speed = param; - P_SetSpeed(song.speed); + song.BPM = param; + setMixerBPM(song.BPM); } else { - song.timer = song.tempo = param; + song.tick = song.speed = param; } (void)ch; } -static void setGlobaVol(stmTyp *ch, uint8_t param) +static void setGlobalVolume(channel_t *ch, uint8_t param) { if (param > 64) param = 64; - song.globVol = param; + song.globalVolume = param; - stmTyp *c = stm; - for (int32_t i = 0; i < song.antChn; i++, c++) // update all voice volumes + channel_t *c = channel; + for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes c->status |= IS_Vol; (void)ch; } -static void setEnvelopePos(stmTyp *ch, uint8_t param) +static void setEnvelopePos(channel_t *ch, uint8_t param) { bool envUpdate; int8_t point; int16_t tick; - instrTyp *ins = ch->instrPtr; + instr_t *ins = ch->instrPtr; assert(ins != NULL); // *** VOLUME ENVELOPE *** - if (ins->envVTyp & 1) + if (ins->volEnvFlags & ENV_ENABLED) { - ch->envVCnt = param-1; + ch->volEnvTick = param-1; point = 0; envUpdate = true; tick = param; - if (ins->envVPAnt > 1) + if (ins->volEnvLength > 1) { point++; - for (int32_t i = 0; i < ins->envVPAnt-1; i++) + for (int32_t i = 0; i < ins->volEnvLength-1; i++) { - if (tick < ins->envVP[point][0]) + if (tick < ins->volEnvPoints[point][0]) { point--; - tick -= ins->envVP[point][0]; + tick -= ins->volEnvPoints[point][0]; if (tick == 0) { envUpdate = false; break; } - if (ins->envVP[point+1][0] <= ins->envVP[point][0]) + if (ins->volEnvPoints[point+1][0] <= ins->volEnvPoints[point][0]) { envUpdate = true; break; } - ch->envVIPValue = ((ins->envVP[point+1][1] - ins->envVP[point][1]) << 16) / (ins->envVP[point+1][0] - ins->envVP[point][0]); - ch->envVAmp = (ch->envVIPValue * (tick-1)) + (ins->envVP[point][1] << 16); + int32_t delta = (int8_t)((ins->volEnvPoints[point+1][1] - ins->volEnvPoints[point][1]) & 0xFF) << 16; + delta /= (ins->volEnvPoints[point+1][0]-ins->volEnvPoints[point][0]); + + ch->volEnvDelta = delta; + ch->volEnvValue = (ch->volEnvDelta * (tick-1)) + ((int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16); point++; @@ -828,53 +839,56 @@ static void setEnvelopePos(stmTyp *ch, uint8_t param) if (envUpdate) { - ch->envVIPValue = 0; - ch->envVAmp = ins->envVP[point][1]; + ch->volEnvDelta = 0; + ch->volEnvValue = (int8_t)(ins->volEnvPoints[point][1] & 0xFF) << 16; } - if (point >= ins->envVPAnt) + if (point >= ins->volEnvLength) { - point = ins->envVPAnt-1; + point = ins->volEnvLength-1; if (point < 0) point = 0; } - ch->envVPos = point; + ch->volEnvPos = point; } // *** PANNING ENVELOPE *** - if (ins->envVTyp & 2) // probably an FT2 bug + if (ins->volEnvFlags & ENV_SUSTAIN) // What..? Probably an FT2 bug! { - ch->envPCnt = param-1; + ch->panEnvTick = param-1; point = 0; envUpdate = true; tick = param; - if (ins->envPPAnt > 1) + if (ins->panEnvLength > 1) { point++; - for (int32_t i = 0; i < ins->envPPAnt-1; i++) + for (int32_t i = 0; i < ins->panEnvLength-1; i++) { - if (tick < ins->envPP[point][0]) + if (tick < ins->panEnvPoints[point][0]) { point--; - tick -= ins->envPP[point][0]; + tick -= ins->panEnvPoints[point][0]; if (tick == 0) { envUpdate = false; break; } - if (ins->envPP[point+1][0] <= ins->envPP[point][0]) + if (ins->panEnvPoints[point+1][0] <= ins->panEnvPoints[point][0]) { envUpdate = true; break; } - ch->envPIPValue = ((ins->envPP[point+1][1] - ins->envPP[point][1]) << 16) / (ins->envPP[point+1][0] - ins->envPP[point][0]); - ch->envPAmp = (ch->envPIPValue * (tick-1)) + (ins->envPP[point][1] << 16); + int32_t delta = (int8_t)((ins->panEnvPoints[point+1][1] - ins->panEnvPoints[point][1]) & 0xFF) << 16; + delta /= (ins->panEnvPoints[point+1][0]-ins->panEnvPoints[point][0]); + + ch->panEnvDelta = delta; + ch->panEnvValue = (ch->panEnvDelta * (tick-1)) + ((int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16); point++; @@ -891,18 +905,18 @@ static void setEnvelopePos(stmTyp *ch, uint8_t param) if (envUpdate) { - ch->envPIPValue = 0; - ch->envPAmp = ins->envPP[point][1]; + ch->panEnvDelta = 0; + ch->panEnvValue = (int8_t)(ins->panEnvPoints[point][1] & 0xFF) << 16; } - if (point >= ins->envPPAnt) + if (point >= ins->panEnvLength) { - point = ins->envPPAnt-1; + point = ins->panEnvLength-1; if (point < 0) point = 0; } - ch->envPPos = point; + ch->panEnvPos = point; } } @@ -924,7 +938,7 @@ static const efxRoutine JumpTab_TickZero[36] = pattBreak, // D E_Effects_TickZero, // E setSpeed, // F - setGlobaVol, // G + setGlobalVolume, // G dummy, // H dummy, // I dummy, // J @@ -946,68 +960,68 @@ static const efxRoutine JumpTab_TickZero[36] = dummy // Z }; -static void handleMoreEffects_TickZero(stmTyp *ch) // called even if channel is muted +static void handleMoreEffects_TickZero(channel_t *ch) // called even if channel is muted { - if (ch->effTyp > 35) + if (ch->efx > 35) return; - JumpTab_TickZero[ch->effTyp](ch, ch->eff); + JumpTab_TickZero[ch->efx](ch, ch->efxData); } /* -- tick-zero volume column effects -- ** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig) */ -static void v_SetVibSpeed(stmTyp *ch, uint8_t *volKol) +static void v_SetVibSpeed(channel_t *ch, uint8_t *volColumnData) { - *volKol = (ch->volKolVol & 0x0F) << 2; - if (*volKol != 0) - ch->vibSpeed = *volKol; + *volColumnData = (ch->volColumnVol & 0x0F) << 2; + if (*volColumnData != 0) + ch->vibSpeed = *volColumnData; } -static void v_Volume(stmTyp *ch, uint8_t *volKol) +static void v_Volume(channel_t *ch, uint8_t *volColumnData) { - *volKol -= 16; - if (*volKol > 64) // no idea why FT2 has this check... - *volKol = 64; + *volColumnData -= 16; + if (*volColumnData > 64) // no idea why FT2 has this check... + *volColumnData = 64; - ch->outVol = ch->realVol = *volKol; + ch->outVol = ch->realVol = *volColumnData; ch->status |= IS_Vol + IS_QuickVol; } -static void v_FineSlideDown(stmTyp *ch, uint8_t *volKol) +static void v_FineSlideDown(channel_t *ch, uint8_t *volColumnData) { - *volKol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; - if ((int8_t)*volKol < 0) - *volKol = 0; + *volColumnData = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol; + if ((int8_t)*volColumnData < 0) + *volColumnData = 0; - ch->outVol = ch->realVol = *volKol; + ch->outVol = ch->realVol = *volColumnData; ch->status |= IS_Vol; } -static void v_FineSlideUp(stmTyp *ch, uint8_t *volKol) +static void v_FineSlideUp(channel_t *ch, uint8_t *volColumnData) { - *volKol = (ch->volKolVol & 0x0F) + ch->realVol; - if (*volKol > 64) - *volKol = 64; + *volColumnData = (ch->volColumnVol & 0x0F) + ch->realVol; + if (*volColumnData > 64) + *volColumnData = 64; - ch->outVol = ch->realVol = *volKol; + ch->outVol = ch->realVol = *volColumnData; ch->status |= IS_Vol; } -static void v_SetPan(stmTyp *ch, uint8_t *volKol) +static void v_SetPan(channel_t *ch, uint8_t *volColumnData) { - *volKol <<= 4; + *volColumnData <<= 4; - ch->outPan = *volKol; + ch->outPan = *volColumnData; ch->status |= IS_Pan; } // -- non-tick-zero volume column effects -- -static void v_SlideDown(stmTyp *ch) +static void v_SlideDown(channel_t *ch) { - uint8_t newVol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; + uint8_t newVol = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->realVol; if ((int8_t)newVol < 0) newVol = 0; @@ -1015,9 +1029,9 @@ static void v_SlideDown(stmTyp *ch) ch->status |= IS_Vol; } -static void v_SlideUp(stmTyp *ch) +static void v_SlideUp(channel_t *ch) { - uint8_t newVol = (ch->volKolVol & 0x0F) + ch->realVol; + uint8_t newVol = (ch->volColumnVol & 0x0F) + ch->realVol; if (newVol > 64) newVol = 64; @@ -1025,18 +1039,18 @@ static void v_SlideUp(stmTyp *ch) ch->status |= IS_Vol; } -static void v_Vibrato(stmTyp *ch) +static void v_Vibrato(channel_t *ch) { - const uint8_t param = ch->volKolVol & 0xF; + const uint8_t param = ch->volColumnVol & 0xF; if (param > 0) ch->vibDepth = param; vibrato2(ch); } -static void v_PanSlideLeft(stmTyp *ch) +static void v_PanSlideLeft(channel_t *ch) { - uint16_t tmp16 = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->outPan; + uint16_t tmp16 = (uint8_t)(0 - (ch->volColumnVol & 0x0F)) + ch->outPan; if (tmp16 < 256) // includes an FT2 bug: pan-slide-left of 0 = set pan to 0 tmp16 = 0; @@ -1044,9 +1058,9 @@ static void v_PanSlideLeft(stmTyp *ch) ch->status |= IS_Pan; } -static void v_PanSlideRight(stmTyp *ch) +static void v_PanSlideRight(channel_t *ch) { - uint16_t tmp16 = (ch->volKolVol & 0x0F) + ch->outPan; + uint16_t tmp16 = (ch->volColumnVol & 0x0F) + ch->outPan; if (tmp16 > 255) tmp16 = 255; @@ -1054,25 +1068,25 @@ static void v_PanSlideRight(stmTyp *ch) ch->status |= IS_Pan; } -static void v_TonePorta(stmTyp *ch) +static void v_TonePorta(channel_t *ch) { tonePorta(ch, 0); // the last parameter is actually not used in tonePorta() } -static void v_dummy(stmTyp *ch) +static void v_dummy(channel_t *ch) { (void)ch; return; } -static void v_dummy2(stmTyp *ch, uint8_t *volKol) +static void v_dummy2(channel_t *ch, uint8_t *volColumnData) { (void)ch; - (void)volKol; + (void)volColumnData; return; } -static const volKolEfxRoutine VJumpTab_TickNonZero[16] = +static const volColumnEfxRoutine VJumpTab_TickNonZero[16] = { v_dummy, v_dummy, v_dummy, v_dummy, v_dummy, v_dummy, v_SlideDown, v_SlideUp, @@ -1080,7 +1094,7 @@ static const volKolEfxRoutine VJumpTab_TickNonZero[16] = v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta }; -static const volKolEfxRoutine2 VJumpTab_TickZero[16] = +static const volColumnEfxRoutine2 VJumpTab_TickZero[16] = { v_dummy2, v_Volume, v_Volume, v_Volume, v_Volume, v_Volume, v_dummy2, v_dummy2, @@ -1088,13 +1102,13 @@ static const volKolEfxRoutine2 VJumpTab_TickZero[16] = v_SetPan, v_dummy2, v_dummy2, v_dummy2 }; -static void setPan(stmTyp *ch, uint8_t param) +static void setPan(channel_t *ch, uint8_t param) { ch->outPan = param; ch->status |= IS_Pan; } -static void setVol(stmTyp *ch, uint8_t param) +static void setVol(channel_t *ch, uint8_t param) { if (param > 64) param = 64; @@ -1103,7 +1117,7 @@ static void setVol(stmTyp *ch, uint8_t param) ch->status |= IS_Vol + IS_QuickVol; } -static void xFinePorta(stmTyp *ch, uint8_t param) +static void xFinePorta(channel_t *ch, uint8_t param) { const uint8_t type = param >> 4; param &= 0x0F; @@ -1142,7 +1156,7 @@ static void xFinePorta(stmTyp *ch, uint8_t param) } } -static void doMultiRetrig(stmTyp *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure) +static void doMultiRetrig(channel_t *ch, uint8_t param) // "param" is never used (needed for efx jumptable structure) { uint8_t cnt = ch->retrigCnt + 1; if (cnt < ch->retrigSpeed) @@ -1178,14 +1192,14 @@ static void doMultiRetrig(stmTyp *ch, uint8_t param) // "param" is never used (n ch->realVol = (uint8_t)vol; ch->outVol = ch->realVol; - if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) + if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50) { - ch->outVol = ch->volKolVol - 0x10; + ch->outVol = ch->volColumnVol - 0x10; ch->realVol = ch->outVol; } - else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) + else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF) { - ch->outPan = (ch->volKolVol & 0x0F) << 4; + ch->outPan = (ch->volColumnVol & 0x0F) << 4; } startTone(0, 0, 0, ch); @@ -1193,7 +1207,7 @@ static void doMultiRetrig(stmTyp *ch, uint8_t param) // "param" is never used (n (void)param; } -static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData) +static void multiRetrig(channel_t *ch, uint8_t param, uint8_t volumeColumnData) { uint8_t tmpParam; @@ -1213,47 +1227,47 @@ static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData) doMultiRetrig(ch, 0); // the second parameter is never used (needed for efx jumptable structure) } -static void handleEffects_TickZero(stmTyp *ch) +static void handleEffects_TickZero(channel_t *ch) { // volume column effects - uint8_t newVolKol = ch->volKolVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk) - VJumpTab_TickZero[ch->volKolVol >> 4](ch, &newVolKol); + uint8_t newVolCol = ch->volColumnVol; // manipulated by vol. column effects, then used for multiretrig check (FT2 quirk) + VJumpTab_TickZero[ch->volColumnVol >> 4](ch, &newVolCol); // normal effects - const uint8_t param = ch->eff; - if (ch->effTyp == 0 && param == 0) + const uint8_t param = ch->efxData; + if (ch->efx == 0 && param == 0) return; // no effect - if (ch->effTyp == 8) setPan(ch, param); - else if (ch->effTyp == 12) setVol(ch, param); - else if (ch->effTyp == 27) multiRetrig(ch, param, newVolKol); - else if (ch->effTyp == 33) xFinePorta(ch, param); + if (ch->efx == 8) setPan(ch, param); + else if (ch->efx == 12) setVol(ch, param); + else if (ch->efx == 27) multiRetrig(ch, param, newVolCol); + else if (ch->efx == 33) xFinePorta(ch, param); handleMoreEffects_TickZero(ch); } -static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst) +static void fixTonePorta(channel_t *ch, const note_t *p, uint8_t inst) { - if (p->ton > 0) + if (p->note > 0) { - if (p->ton == 97) + if (p->note == NOTE_OFF) { keyOff(ch); } else { - const uint16_t note = (((p->ton-1) + ch->relTonNr) << 4) + (((int8_t)ch->fineTune >> 3) + 16); + const uint16_t note = (((p->note-1) + ch->relativeNote) << 4) + (((int8_t)ch->finetune >> 3) + 16); if (note < MAX_NOTES) { assert(note2Period != NULL); ch->wantPeriod = note2Period[note]; if (ch->wantPeriod == ch->realPeriod) - ch->portaDir = 0; + ch->portaDirection = 0; else if (ch->wantPeriod > ch->realPeriod) - ch->portaDir = 1; + ch->portaDirection = 1; else - ch->portaDir = 2; + ch->portaDirection = 2; } } } @@ -1261,18 +1275,18 @@ static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst) if (inst > 0) { retrigVolume(ch); - if (p->ton != 97) + if (p->note != NOTE_OFF) retrigEnvelopeVibrato(ch); } } -static void getNewNote(stmTyp *ch, const tonTyp *p) +static void getNewNote(channel_t *ch, const note_t *p) { - ch->volKolVol = p->vol; + ch->volColumnVol = p->vol; - if (ch->effTyp == 0) + if (ch->efx == 0) { - if (ch->eff > 0) // we have an arpeggio running, set period back + if (ch->efxData > 0) // we have an arpeggio running, set period back { ch->outPeriod = ch->realPeriod; ch->status |= IS_Period; @@ -1281,18 +1295,18 @@ static void getNewNote(stmTyp *ch, const tonTyp *p) else { // if we have a vibrato on previous row (ch) that ends at current row (p), set period back - if ((ch->effTyp == 4 || ch->effTyp == 6) && (p->effTyp != 4 && p->effTyp != 6)) + if ((ch->efx == 4 || ch->efx == 6) && (p->efx != 4 && p->efx != 6)) { ch->outPeriod = ch->realPeriod; ch->status |= IS_Period; } } - ch->effTyp = p->effTyp; - ch->eff = p->eff; - ch->tonTyp = (p->instr << 8) | p->ton; + ch->efx = p->efx; + ch->efxData = p->efxData; + ch->noteData = (p->instr << 8) | p->note; - if (ch->stOff) // channel is muted, only handle some effects + if (ch->channelOff) // channel is muted, only handle some effects { handleMoreEffects_TickZero(ch); return; @@ -1303,44 +1317,44 @@ static void getNewNote(stmTyp *ch, const tonTyp *p) if (inst > 0) { if (inst <= MAX_INST) - ch->instrNr = inst; + ch->instrNum = inst; else inst = 0; } bool checkEfx = true; - if (p->effTyp == 0x0E) + if (p->efx == 0x0E) { - if (p->eff >= 0xD1 && p->eff <= 0xDF) + if (p->efxData >= 0xD1 && p->efxData <= 0xDF) return; // we have a note delay (ED1..EDF) - else if (p->eff == 0x90) + else if (p->efxData == 0x90) checkEfx = false; } if (checkEfx) { - if ((ch->volKolVol & 0xF0) == 0xF0) // gxx + if ((ch->volColumnVol & 0xF0) == 0xF0) // gxx { - const uint8_t volKolParam = ch->volKolVol & 0x0F; - if (volKolParam > 0) - ch->portaSpeed = volKolParam << 6; + const uint8_t volColumnData = ch->volColumnVol & 0x0F; + if (volColumnData > 0) + ch->portaSpeed = volColumnData << 6; fixTonePorta(ch, p, inst); handleEffects_TickZero(ch); return; } - if (p->effTyp == 3 || p->effTyp == 5) // 3xx or 5xx + if (p->efx == 3 || p->efx == 5) // 3xx or 5xx { - if (p->effTyp != 5 && p->eff != 0) - ch->portaSpeed = p->eff << 2; + if (p->efx != 5 && p->efxData != 0) + ch->portaSpeed = p->efxData << 2; fixTonePorta(ch, p, inst); handleEffects_TickZero(ch); return; } - if (p->effTyp == 0x14 && p->eff == 0) // K00 (KeyOff - only handle tick 0 here) + if (p->efx == 0x14 && p->efxData == 0) // K00 (KeyOff - only handle tick 0 here) { keyOff(ch); @@ -1351,7 +1365,7 @@ static void getNewNote(stmTyp *ch, const tonTyp *p) return; } - if (p->ton == 0) + if (p->note == 0) { if (inst > 0) { @@ -1364,31 +1378,31 @@ static void getNewNote(stmTyp *ch, const tonTyp *p) } } - if (p->ton == 97) + if (p->note == NOTE_OFF) keyOff(ch); else - startTone(p->ton, p->effTyp, p->eff, ch); + startTone(p->note, p->efx, p->efxData, ch); if (inst > 0) { retrigVolume(ch); - if (p->ton != 97) + if (p->note != NOTE_OFF) retrigEnvelopeVibrato(ch); } handleEffects_TickZero(ch); } -static void fixaEnvelopeVibrato(stmTyp *ch) +static void updateChannel(channel_t *ch) { bool envInterpolateFlag, envDidInterpolate; uint8_t envPos; int16_t autoVibVal; uint16_t autoVibAmp; int32_t envVal; - double dVol; + float fVol; - instrTyp *ins = ch->instrPtr; + instr_t *ins = ch->instrPtr; assert(ins != NULL); // *** FADEOUT *** @@ -1397,14 +1411,14 @@ static void fixaEnvelopeVibrato(stmTyp *ch) ch->status |= IS_Vol; // unsigned clamp + reset - if (ch->fadeOutAmp >= ch->fadeOutSpeed) + if (ch->fadeoutVol >= ch->fadeoutSpeed) { - ch->fadeOutAmp -= ch->fadeOutSpeed; + ch->fadeoutVol -= ch->fadeoutSpeed; } else { - ch->fadeOutAmp = 0; - ch->fadeOutSpeed = 0; + ch->fadeoutVol = 0; + ch->fadeoutSpeed = 0; } } @@ -1412,71 +1426,73 @@ static void fixaEnvelopeVibrato(stmTyp *ch) { // *** VOLUME ENVELOPE *** envVal = 0; - if (ins->envVTyp & 1) + if (ins->volEnvFlags & ENV_ENABLED) { envDidInterpolate = false; - envPos = ch->envVPos; + envPos = ch->volEnvPos; - if (++ch->envVCnt == ins->envVP[envPos][0]) + if (++ch->volEnvTick == ins->volEnvPoints[envPos][0]) { - ch->envVAmp = ins->envVP[envPos][1] << 16; + ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16; envPos++; - if (ins->envVTyp & 4) + if (ins->volEnvFlags & ENV_LOOP) { envPos--; - if (envPos == ins->envVRepE) + if (envPos == ins->volEnvLoopEnd) { - if (!(ins->envVTyp & 2) || envPos != ins->envVSust || ch->envSustainActive) + if (!(ins->volEnvFlags & ENV_SUSTAIN) || envPos != ins->volEnvSustain || ch->envSustainActive) { - envPos = ins->envVRepS; - ch->envVCnt = ins->envVP[envPos][0]; - ch->envVAmp = ins->envVP[envPos][1] << 16; + envPos = ins->volEnvLoopStart; + ch->volEnvTick = ins->volEnvPoints[envPos][0]; + ch->volEnvValue = (int8_t)(ins->volEnvPoints[envPos][1] & 0xFF) << 16; } } envPos++; } - if (envPos < ins->envVPAnt) + if (envPos < ins->volEnvLength) { envInterpolateFlag = true; - if ((ins->envVTyp & 2) && ch->envSustainActive) + if ((ins->volEnvFlags & ENV_SUSTAIN) && ch->envSustainActive) { - if (envPos-1 == ins->envVSust) + if (envPos-1 == ins->volEnvSustain) { envPos--; - ch->envVIPValue = 0; + ch->volEnvDelta = 0; envInterpolateFlag = false; } } if (envInterpolateFlag) { - ch->envVPos = envPos; + ch->volEnvPos = envPos; - ch->envVIPValue = 0; - if (ins->envVP[envPos][0] > ins->envVP[envPos-1][0]) + ch->volEnvDelta = 0; + if (ins->volEnvPoints[envPos][0] > ins->volEnvPoints[envPos-1][0]) { - ch->envVIPValue = ((ins->envVP[envPos][1] - ins->envVP[envPos-1][1]) << 16) / (ins->envVP[envPos][0] - ins->envVP[envPos-1][0]); + int32_t delta = (int8_t)((ins->volEnvPoints[envPos][1] - ins->volEnvPoints[envPos-1][1]) & 0xFF) << 16; + delta /= (ins->volEnvPoints[envPos][0]-ins->volEnvPoints[envPos-1][0]); + ch->volEnvDelta = delta; - envVal = ch->envVAmp; + envVal = ch->volEnvValue; envDidInterpolate = true; } } } else { - ch->envVIPValue = 0; + ch->volEnvDelta = 0; } } if (!envDidInterpolate) { - ch->envVAmp += ch->envVIPValue; + ch->volEnvValue += ch->volEnvDelta; - envVal = ch->envVAmp; + envVal = ch->volEnvValue; if (envVal > 64<<16) { if (envVal > 128<<16) @@ -1484,104 +1500,104 @@ static void fixaEnvelopeVibrato(stmTyp *ch) else envVal = 0; - ch->envVIPValue = 0; + ch->volEnvDelta = 0; } } - const int32_t vol = song.globVol * ch->outVol * ch->fadeOutAmp; + const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol; - dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0)); - dVol *= (int32_t)envVal * (1.0 / (64.0 * (1 << 16))); + fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f)); + fVol *= (int32_t)envVal * (1.0f / (64.0f * (1 << 16))); // volume envelope value - ch->status |= IS_Vol; // update vol every tick because vol envelope is enabled + ch->status |= IS_Vol; // update mixer vol every tick when vol envelope is enabled } else { - const int32_t vol = song.globVol * ch->outVol * ch->fadeOutAmp; - dVol = vol * (1.0 / (64.0 * 64.0 * 32768.0)); + const int32_t vol = song.globalVolume * ch->outVol * ch->fadeoutVol; + fVol = vol * (1.0f / (64.0f * 64.0f * 32768.0f)); } - if (dVol > 1.0) // shouldn't happen, but just in case... - dVol = 1.0; - - ch->dFinalVol = dVol; - ch->finalVol = (uint8_t)(int32_t)((ch->dFinalVol * 255) + 0.5); // 0.0 .. 1.0 -> 0..255 rounded, used for visuals + /* FT2 doesn't clamp here, but it's actually important if you + ** move envelope points with the mouse while playing the instrument. + */ + ch->fFinalVol = CLAMP(fVol, 0.0f, 1.0f); } else { - ch->dFinalVol = 0.0; - ch->finalVol = 0; + ch->fFinalVol = 0.0f; } // *** PANNING ENVELOPE *** envVal = 0; - if (ins->envPTyp & 1) + if (ins->panEnvFlags & ENV_ENABLED) { envDidInterpolate = false; - envPos = ch->envPPos; + envPos = ch->panEnvPos; - if (++ch->envPCnt == ins->envPP[envPos][0]) + if (++ch->panEnvTick == ins->panEnvPoints[envPos][0]) { - ch->envPAmp = ins->envPP[envPos][1] << 16; + ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16; envPos++; - if (ins->envPTyp & 4) + if (ins->panEnvFlags & ENV_LOOP) { envPos--; - if (envPos == ins->envPRepE) + if (envPos == ins->panEnvLoopEnd) { - if (!(ins->envPTyp & 2) || envPos != ins->envPSust || ch->envSustainActive) + if (!(ins->panEnvFlags & ENV_SUSTAIN) || envPos != ins->panEnvSustain || ch->envSustainActive) { - envPos = ins->envPRepS; + envPos = ins->panEnvLoopStart; - ch->envPCnt = ins->envPP[envPos][0]; - ch->envPAmp = ins->envPP[envPos][1] << 16; + ch->panEnvTick = ins->panEnvPoints[envPos][0]; + ch->panEnvValue = (int8_t)(ins->panEnvPoints[envPos][1] & 0xFF) << 16; } } envPos++; } - if (envPos < ins->envPPAnt) + if (envPos < ins->panEnvLength) { envInterpolateFlag = true; - if ((ins->envPTyp & 2) && ch->envSustainActive) + if ((ins->panEnvFlags & ENV_SUSTAIN) && ch->envSustainActive) { - if (envPos-1 == ins->envPSust) + if (envPos-1 == ins->panEnvSustain) { envPos--; - ch->envPIPValue = 0; + ch->panEnvDelta = 0; envInterpolateFlag = false; } } if (envInterpolateFlag) { - ch->envPPos = envPos; + ch->panEnvPos = envPos; - ch->envPIPValue = 0; - if (ins->envPP[envPos][0] > ins->envPP[envPos-1][0]) + ch->panEnvDelta = 0; + if (ins->panEnvPoints[envPos][0] > ins->panEnvPoints[envPos-1][0]) { - ch->envPIPValue = ((ins->envPP[envPos][1] - ins->envPP[envPos-1][1]) << 16) / (ins->envPP[envPos][0] - ins->envPP[envPos-1][0]); + int32_t delta = (int8_t)((ins->panEnvPoints[envPos][1] - ins->panEnvPoints[envPos-1][1]) & 0xFF) << 16; + delta /= (ins->panEnvPoints[envPos][0]-ins->panEnvPoints[envPos-1][0]); + ch->panEnvDelta = delta; - envVal = ch->envPAmp; + envVal = ch->panEnvValue; envDidInterpolate = true; } } } else { - ch->envPIPValue = 0; + ch->panEnvDelta = 0; } } if (!envDidInterpolate) { - ch->envPAmp += ch->envPIPValue; + ch->panEnvValue += ch->panEnvDelta; - envVal = ch->envPAmp; + envVal = ch->panEnvValue; if (envVal > 64<<16) { if (envVal > 128<<16) @@ -1589,7 +1605,7 @@ static void fixaEnvelopeVibrato(stmTyp *ch) else envVal = 0; - ch->envPIPValue = 0; + ch->panEnvDelta = 0; } } @@ -1597,7 +1613,7 @@ static void fixaEnvelopeVibrato(stmTyp *ch) const int32_t pan = 128 - ABS(ch->outPan-128); const int32_t panAdd = (pan * envVal) >> (16+5); - ch->finalPan = (uint8_t)CLAMP(ch->outPan + panAdd, 0, 255); + ch->finalPan = (uint8_t)(ch->outPan + panAdd); ch->status |= IS_Pan; // update pan every tick because pan envelope is enabled } @@ -1642,9 +1658,9 @@ static void fixaEnvelopeVibrato(stmTyp *ch) #endif ch->eVibPos += ins->vibRate; - if (ins->vibTyp == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square - else if (ins->vibTyp == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up - else if (ins->vibTyp == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down + if (ins->vibType == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square + else if (ins->vibType == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up + else if (ins->vibType == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down else autoVibVal = vibSineTab[ch->eVibPos]; // sine autoVibVal <<= 2; @@ -1677,11 +1693,11 @@ static void fixaEnvelopeVibrato(stmTyp *ch) } // for arpeggio and portamento (semitone-slide mode) -static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch) +static uint16_t relocateTon(uint16_t period, uint8_t arpNote, channel_t *ch) { int32_t tmpPeriod; - const int32_t fineTune = ((int8_t)ch->fineTune >> 3) + 16; + const int32_t fineTune = ((int8_t)ch->finetune >> 3) + 16; /* FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues. ** You can only achieve such high notes by having a high relative note setting. @@ -1711,7 +1727,7 @@ static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch) return note2Period[tmpPeriod]; } -static void vibrato2(stmTyp *ch) +static void vibrato2(channel_t *ch) { uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F; @@ -1744,11 +1760,11 @@ static void vibrato2(stmTyp *ch) ch->vibPos += ch->vibSpeed; } -static void arp(stmTyp *ch, uint8_t param) +static void arp(channel_t *ch, uint8_t param) { uint8_t note; - const uint8_t tick = arpTab[song.timer & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!) + const uint8_t tick = arpTab[song.tick & 0xFF]; // non-FT2 protection (we have 248 extra overflow bytes in LUT, but not more!) if (tick == 0) { ch->outPeriod = ch->realPeriod; @@ -1766,7 +1782,7 @@ static void arp(stmTyp *ch, uint8_t param) ch->status |= IS_Period; } -static void portaUp(stmTyp *ch, uint8_t param) +static void portaUp(channel_t *ch, uint8_t param) { if (param == 0) param = ch->portaUpSpeed; @@ -1781,7 +1797,7 @@ static void portaUp(stmTyp *ch, uint8_t param) ch->status |= IS_Period; } -static void portaDown(stmTyp *ch, uint8_t param) +static void portaDown(channel_t *ch, uint8_t param) { if (param == 0) param = ch->portaDownSpeed; @@ -1796,17 +1812,17 @@ static void portaDown(stmTyp *ch, uint8_t param) ch->status |= IS_Period; } -static void tonePorta(stmTyp *ch, uint8_t param) +static void tonePorta(channel_t *ch, uint8_t param) { - if (ch->portaDir == 0) + if (ch->portaDirection == 0) return; - if (ch->portaDir > 1) + if (ch->portaDirection > 1) { ch->realPeriod -= ch->portaSpeed; if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod) { - ch->portaDir = 1; + ch->portaDirection = 1; ch->realPeriod = ch->wantPeriod; } } @@ -1815,7 +1831,7 @@ static void tonePorta(stmTyp *ch, uint8_t param) ch->realPeriod += ch->portaSpeed; if (ch->realPeriod >= ch->wantPeriod) { - ch->portaDir = 1; + ch->portaDirection = 1; ch->realPeriod = ch->wantPeriod; } } @@ -1830,11 +1846,11 @@ static void tonePorta(stmTyp *ch, uint8_t param) (void)param; } -static void vibrato(stmTyp *ch, uint8_t param) +static void vibrato(channel_t *ch, uint8_t param) { uint8_t tmp8; - if (ch->eff > 0) + if (param > 0) { tmp8 = param & 0x0F; if (tmp8 > 0) @@ -1848,7 +1864,7 @@ static void vibrato(stmTyp *ch, uint8_t param) vibrato2(ch); } -static void tonePlusVol(stmTyp *ch, uint8_t param) +static void tonePlusVol(channel_t *ch, uint8_t param) { tonePorta(ch, 0); // the last parameter is actually not used in tonePorta() volume(ch, param); @@ -1856,7 +1872,7 @@ static void tonePlusVol(stmTyp *ch, uint8_t param) (void)param; } -static void vibratoPlusVol(stmTyp *ch, uint8_t param) +static void vibratoPlusVol(channel_t *ch, uint8_t param) { vibrato2(ch); volume(ch, param); @@ -1864,7 +1880,7 @@ static void vibratoPlusVol(stmTyp *ch, uint8_t param) (void)param; } -static void tremolo(stmTyp *ch, uint8_t param) +static void tremolo(channel_t *ch, uint8_t param) { uint8_t tmp8; int16_t tremVol; @@ -1919,7 +1935,7 @@ static void tremolo(stmTyp *ch, uint8_t param) ch->tremPos += ch->tremSpeed; } -static void volume(stmTyp *ch, uint8_t param) // volume slide +static void volume(channel_t *ch, uint8_t param) // volume slide { if (param == 0) param = ch->volSlideSpeed; @@ -1946,14 +1962,14 @@ static void volume(stmTyp *ch, uint8_t param) // volume slide ch->status |= IS_Vol; } -static void globalVolSlide(stmTyp *ch, uint8_t param) +static void globalVolSlide(channel_t *ch, uint8_t param) { if (param == 0) param = ch->globVolSlideSpeed; ch->globVolSlideSpeed = param; - uint8_t newVol = (uint8_t)song.globVol; + uint8_t newVol = (uint8_t)song.globalVolume; if ((param & 0xF0) == 0) { newVol -= param; @@ -1969,20 +1985,20 @@ static void globalVolSlide(stmTyp *ch, uint8_t param) newVol = 64; } - song.globVol = newVol; + song.globalVolume = newVol; - stmTyp *c = stm; - for (int32_t i = 0; i < song.antChn; i++, c++) // update all voice volumes + channel_t *c = channel; + for (int32_t i = 0; i < song.numChannels; i++, c++) // update all voice volumes c->status |= IS_Vol; } -static void keyOffCmd(stmTyp *ch, uint8_t param) +static void keyOffCmd(channel_t *ch, uint8_t param) { - if ((uint8_t)(song.tempo-song.timer) == (param & 31)) + if ((uint8_t)(song.speed-song.tick) == (param & 31)) keyOff(ch); } -static void panningSlide(stmTyp *ch, uint8_t param) +static void panningSlide(channel_t *ch, uint8_t param) { if (param == 0) param = ch->panningSlideSpeed; @@ -2009,7 +2025,7 @@ static void panningSlide(stmTyp *ch, uint8_t param) ch->status |= IS_Pan; } -static void tremor(stmTyp *ch, uint8_t param) +static void tremor(channel_t *ch, uint8_t param) { if (param == 0) param = ch->tremorSave; @@ -2039,46 +2055,46 @@ static void tremor(stmTyp *ch, uint8_t param) ch->status |= IS_Vol + IS_QuickVol; } -static void retrigNote(stmTyp *ch, uint8_t param) +static void retrigNote(channel_t *ch, uint8_t param) { if (param == 0) // E9x with a param of zero is handled in getNewNote() return; - if ((song.tempo-song.timer) % param == 0) + if ((song.speed-song.tick) % param == 0) { startTone(0, 0, 0, ch); retrigEnvelopeVibrato(ch); } } -static void noteCut(stmTyp *ch, uint8_t param) +static void noteCut(channel_t *ch, uint8_t param) { - if ((uint8_t)(song.tempo-song.timer) == param) + if ((uint8_t)(song.speed-song.tick) == param) { ch->outVol = ch->realVol = 0; ch->status |= IS_Vol + IS_QuickVol; } } -static void noteDelay(stmTyp *ch, uint8_t param) +static void noteDelay(channel_t *ch, uint8_t param) { - if ((uint8_t)(song.tempo-song.timer) == param) + if ((uint8_t)(song.speed-song.tick) == param) { - startTone(ch->tonTyp & 0xFF, 0, 0, ch); + startTone(ch->noteData & 0xFF, 0, 0, ch); - if ((ch->tonTyp & 0xFF00) > 0) + if ((ch->noteData & 0xFF00) > 0) retrigVolume(ch); retrigEnvelopeVibrato(ch); - if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) + if (ch->volColumnVol >= 0x10 && ch->volColumnVol <= 0x50) { - ch->outVol = ch->volKolVol - 16; + ch->outVol = ch->volColumnVol - 16; ch->realVol = ch->outVol; } - else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) + else if (ch->volColumnVol >= 0xC0 && ch->volColumnVol <= 0xCF) { - ch->outPan = (ch->volKolVol & 0x0F) << 4; + ch->outPan = (ch->volColumnVol & 0x0F) << 4; } } } @@ -2103,7 +2119,7 @@ static const efxRoutine EJumpTab_TickNonZero[16] = dummy // F }; -static void E_Effects_TickNonZero(stmTyp *ch, uint8_t param) +static void E_Effects_TickNonZero(channel_t *ch, uint8_t param) { EJumpTab_TickNonZero[param >> 4](ch, param & 0xF); } @@ -2148,27 +2164,27 @@ static const efxRoutine JumpTab_TickNonZero[36] = dummy // Z }; -static void handleEffects_TickNonZero(stmTyp *ch) +static void handleEffects_TickNonZero(channel_t *ch) { - if (ch->stOff) + if (ch->channelOff) return; // muted // volume column effects - VJumpTab_TickNonZero[ch->volKolVol >> 4](ch); + VJumpTab_TickNonZero[ch->volColumnVol >> 4](ch); // normal effects - if ((ch->eff == 0 && ch->effTyp == 0) || ch->effTyp > 35) + if ((ch->efx == 0 && ch->efxData == 0) || ch->efx > 35) return; // no effect - JumpTab_TickNonZero[ch->effTyp](ch, ch->eff); + JumpTab_TickNonZero[ch->efx](ch, ch->efxData); } static void getNextPos(void) { - if (song.timer != 1) + if (song.tick != 1) return; - song.pattPos++; + song.row++; if (song.pattDelTime > 0) { @@ -2180,18 +2196,18 @@ static void getNextPos(void) { song.pattDelTime2--; if (song.pattDelTime2 > 0) - song.pattPos--; + song.row--; } if (song.pBreakFlag) { song.pBreakFlag = false; - song.pattPos = song.pBreakPos; + song.row = song.pBreakPos; } - if (song.pattPos >= song.pattLen || song.posJumpFlag) + if (song.row >= song.currNumRows || song.posJumpFlag) { - song.pattPos = song.pBreakPos; + song.row = song.pBreakPos; song.pBreakPos = 0; song.posJumpFlag = false; @@ -2202,15 +2218,15 @@ static void getNextPos(void) song.songPos = 0; bxxOverflow = false; } - else if (++song.songPos >= song.len) + else if (++song.songPos >= song.songLength) { editor.wavReachedEndFlag = true; - song.songPos = song.repS; + song.songPos = song.songLoopStart; } assert(song.songPos <= 255); - song.pattNr = song.songTab[song.songPos & 0xFF]; - song.pattLen = pattLens[song.pattNr & 0xFF]; + song.pattNum = song.orders[song.songPos & 0xFF]; + song.currNumRows = patternNumRows[song.pattNum & 0xFF]; } } } @@ -2230,62 +2246,57 @@ void resumeMusic(void) // starts reading pattern data void tickReplayer(void) // periodically called from audio callback { int32_t i; - stmTyp *c; + channel_t *ch; if (musicPaused || !songPlaying) { - c = stm; - for (i = 0; i < song.antChn; i++, c++) - fixaEnvelopeVibrato(c); + ch = channel; + for (i = 0; i < song.numChannels; i++, ch++) + updateChannel(ch); return; } // for song playback counter (hh:mm:ss) - if (song.speed >= MIN_BPM && song.speed <= MAX_BPM) - song.musicTime64 += musicTimeTab64[song.speed]; + if (song.BPM >= MIN_BPM && song.BPM <= MAX_BPM) + song.musicTime64 += musicTimeTab64[song.BPM-MIN_BPM]; bool tickZero = false; - if (--song.timer == 0) + if (--song.tick == 0) { - song.timer = song.tempo; + song.tick = song.speed; tickZero = true; } - song.curReplayerTimer = (uint8_t)song.timer; // for audio/video syncing (and recording) + song.curReplayerTick = (uint8_t)song.tick; // for audio/video syncing (and recording) const bool readNewNote = tickZero && (song.pattDelTime2 == 0); if (readNewNote) { // set audio/video syncing variables - song.curReplayerPattPos = (uint8_t)song.pattPos; - song.curReplayerPattNr = (uint8_t)song.pattNr; + song.curReplayerRow = (uint8_t)song.row; + song.curReplayerPattNum = (uint8_t)song.pattNum; song.curReplayerSongPos = (uint8_t)song.songPos; // ---------------------------------------------- - const tonTyp *pattPtr = nilPatternLine; - if (patt[song.pattNr] != NULL) - { - assert(song.pattNr >= 0 && song.pattNr < MAX_PATTERNS && - song.pattPos >= 0 && song.pattPos < MAX_PATT_LEN); - - pattPtr = &patt[song.pattNr][song.pattPos * MAX_VOICES]; - } + const note_t *p = nilPatternLine; + if (pattern[song.pattNum] != NULL) + p = &pattern[song.pattNum][song.row * MAX_CHANNELS]; - c = stm; - for (i = 0; i < song.antChn; i++, c++, pattPtr++) + ch = channel; + for (i = 0; i < song.numChannels; i++, ch++, p++) { - getNewNote(c, pattPtr); - fixaEnvelopeVibrato(c); + getNewNote(ch, p); + updateChannel(ch); } } else { - c = stm; - for (i = 0; i < song.antChn; i++, c++) + ch = channel; + for (i = 0; i < song.numChannels; i++, ch++) { - handleEffects_TickNonZero(c); - fixaEnvelopeVibrato(c); + handleEffects_TickNonZero(ch); + updateChannel(ch); } } @@ -2298,7 +2309,7 @@ void resetMusic(void) if (audioWasntLocked) lockAudio(); - song.timer = 1; + song.tick = 1; stopVoices(); if (audioWasntLocked) @@ -2308,12 +2319,12 @@ void resetMusic(void) if (!songPlaying) { - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5); setScrollBarPos(SB_POS_ED, 0, false); } } -void setPos(int16_t songPos, int16_t pattPos, bool resetTimer) +void setPos(int16_t songPos, int16_t row, bool resetTimer) { const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) @@ -2322,117 +2333,118 @@ void setPos(int16_t songPos, int16_t pattPos, bool resetTimer) if (songPos > -1) { song.songPos = songPos; - if (song.len > 0 && song.songPos >= song.len) - song.songPos = song.len - 1; + if (song.songLength > 0 && song.songPos >= song.songLength) + song.songPos = song.songLength - 1; - song.pattNr = song.songTab[song.songPos]; - assert(song.pattNr < MAX_PATTERNS); - song.pattLen = pattLens[song.pattNr]; + song.pattNum = song.orders[song.songPos]; + assert(song.pattNum < MAX_PATTERNS); + song.currNumRows = patternNumRows[song.pattNum]; checkMarkLimits(); // non-FT2 safety } - if (pattPos > -1) + if (row > -1) { - song.pattPos = pattPos; - if (song.pattPos >= song.pattLen) - song.pattPos = song.pattLen-1; + song.row = row; + if (song.row >= song.currNumRows) + song.row = song.currNumRows-1; } // if not playing, update local position variables if (!songPlaying) { - if (pattPos > -1) + if (row > -1) { - editor.pattPos = (uint8_t)pattPos; + editor.row = (uint8_t)row; ui.updatePatternEditor = true; } if (songPos > -1) { - editor.editPattern = (uint8_t)song.pattNr; + editor.editPattern = (uint8_t)song.pattNum; editor.songPos = song.songPos; ui.updatePosSections = true; } } if (resetTimer) - song.timer = 1; + song.tick = 1; if (audioWasntLocked) unlockAudio(); } -void delta2Samp(int8_t *p, int32_t len, uint8_t typ) +void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags) { - if (typ & 16) len >>= 1; // 16-bit - if (typ & 32) len >>= 1; // stereo + bool sample16Bit = !!(smpFlags & SAMPLE_16BIT); + bool stereo = !!(smpFlags & SAMPLE_STEREO); - if (typ & 32) + if (stereo) { - if (typ & 16) + length >>= 1; + + if (sample16Bit) { - int16_t *p16 = (int16_t *)p; + int16_t *p16L = (int16_t *)p; + int16_t *p16R = (int16_t *)p + length; int16_t olds16L = 0; int16_t olds16R = 0; - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) { - const int16_t news16L = p16[i] + olds16L; - p16[i] = news16L; + const int16_t news16L = p16L[i] + olds16L; + p16L[i] = news16L; olds16L = news16L; - const int16_t news16R = p16[len+i] + olds16R; - p16[len+i] = news16R; + const int16_t news16R = p16R[i] + olds16R; + p16R[i] = news16R; olds16R = news16R; - const int32_t tmp32 = olds16L + olds16R; - p16[i] = (int16_t)(tmp32 >> 1); + p16L[i] = (int16_t)((olds16L + olds16R) >> 1); } } - else + else // 8-bit { - int8_t *p8 = (int8_t *)p; - + int8_t *p8L = (int8_t *)p; + int8_t *p8R = (int8_t *)p + length; int8_t olds8L = 0; int8_t olds8R = 0; - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) { - const int8_t news8L = p8[i] + olds8L; - p8[i] = news8L; + const int8_t news8L = p8L[i] + olds8L; + p8L[i] = news8L; olds8L = news8L; - const int8_t news8R = p8[len+i] + olds8R; - p8[len+i] = news8R; + const int8_t news8R = p8R[i] + olds8R; + p8R[i] = news8R; olds8R = news8R; - const int16_t tmp16 = olds8L + olds8R; - p8[i] = (int8_t)(tmp16 >> 1); + p8L[i] = (int8_t)((olds8L + olds8R) >> 1); } } } - else + else // mono (normal sample) { - if (typ & 16) + if (sample16Bit) { int16_t *p16 = (int16_t *)p; int16_t olds16L = 0; - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) { const int16_t news16 = p16[i] + olds16L; p16[i] = news16; olds16L = news16; } } - else + else // 8-bit { int8_t *p8 = (int8_t *)p; int8_t olds8L = 0; - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) { const int8_t news8 = p8[i] + olds8L; p8[i] = news8; @@ -2442,51 +2454,48 @@ void delta2Samp(int8_t *p, int32_t len, uint8_t typ) } } -void samp2Delta(int8_t *p, int32_t len, uint8_t typ) +void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags) { - if (typ & 16) - len >>= 1; // 16-bit - - if (typ & 16) + if (smpFlags & SAMPLE_16BIT) { int16_t *p16 = (int16_t *)p; - int16_t news16 = 0; - for (int32_t i = 0; i < len; i++) + int16_t newS16 = 0; + for (int32_t i = 0; i < length; i++) { - const int16_t olds16 = p16[i]; - p16[i] -= news16; - news16 = olds16; + const int16_t oldS16 = p16[i]; + p16[i] -= newS16; + newS16 = oldS16; } } - else + else // 8-bit { int8_t *p8 = (int8_t *)p; - int8_t news8 = 0; - for (int32_t i = 0; i < len; i++) + int8_t newS8 = 0; + for (int32_t i = 0; i < length; i++) { - const int8_t olds8 = p8[i]; - p8[i] -= news8; - news8 = olds8; + const int8_t oldS8 = p8[i]; + p8[i] -= newS8; + newS8 = oldS8; } } } -bool allocateInstr(int16_t nr) +bool allocateInstr(int16_t insNum) { - if (instr[nr] != NULL) + if (instr[insNum] != NULL) return false; // already allocated - instrTyp *p = (instrTyp *)malloc(sizeof (instrTyp)); + instr_t *p = (instr_t *)malloc(sizeof (instr_t)); if (p == NULL) return false; - memset(p, 0, sizeof (instrTyp)); + memset(p, 0, sizeof (instr_t)); for (int32_t i = 0; i < MAX_SMP_PER_INST; i++) { - p->samp[i].pan = 128; - p->samp[i].vol = 64; + p->smp[i].panning = 128; + p->smp[i].volume = 64; } setStdEnvelope(p, 0, 3); @@ -2495,7 +2504,7 @@ bool allocateInstr(int16_t nr) if (audioWasntLocked) lockAudio(); - instr[nr] = p; + instr[insNum] = p; if (audioWasntLocked) unlockAudio(); @@ -2503,22 +2512,19 @@ bool allocateInstr(int16_t nr) return true; } -void freeInstr(int32_t nr) +void freeInstr(int32_t insNum) { - if (instr[nr] == NULL) + if (instr[insNum] == NULL) return; // not allocated pauseAudio(); // channel instrument pointers are now cleared - sampleTyp *s = instr[nr]->samp; + sample_t *s = instr[insNum]->smp; for (int32_t i = 0; i < MAX_SMP_PER_INST; i++, s++) // free sample data - { - if (s->origPek != NULL) - free(s->origPek); - } + freeSmpData(s); - free(instr[nr]); - instr[nr] = NULL; + free(instr[insNum]); + instr[insNum] = NULL; resumeAudio(); } @@ -2530,12 +2536,9 @@ void freeAllInstr(void) { if (instr[i] != NULL) { - sampleTyp *s = instr[i]->samp; + sample_t *s = instr[i]->smp; for (int32_t j = 0; j < MAX_SMP_PER_INST; j++, s++) // free sample data - { - if (s->origPek != NULL) - free(s->origPek); - } + freeSmpData(s); free(instr[i]); instr[i] = NULL; @@ -2544,21 +2547,19 @@ void freeAllInstr(void) resumeAudio(); } -void freeSample(int16_t nr, int16_t nr2) +void freeSample(int16_t insNum, int16_t smpNum) { - if (instr[nr] == NULL) + if (instr[insNum] == NULL) return; // instrument not allocated pauseAudio(); // voice sample pointers are now cleared - sampleTyp *s = &instr[nr]->samp[nr2]; - if (s->origPek != NULL) - free(s->origPek); - - memset(s, 0, sizeof (sampleTyp)); + sample_t *s = &instr[insNum]->smp[smpNum]; + freeSmpData(s); - s->pan = 128; - s->vol = 64; + memset(s, 0, sizeof (sample_t)); + s->panning = 128; + s->volume = 64; resumeAudio(); } @@ -2568,87 +2569,87 @@ void freeAllPatterns(void) pauseAudio(); for (int32_t i = 0; i < MAX_PATTERNS; i++) { - if (patt[i] != NULL) + if (pattern[i] != NULL) { - free(patt[i]); - patt[i] = NULL; + free(pattern[i]); + pattern[i] = NULL; } } resumeAudio(); } -void setStdEnvelope(instrTyp *ins, int16_t i, uint8_t typ) +void setStdEnvelope(instr_t *ins, int16_t i, uint8_t type) { if (ins == NULL) return; pauseMusic(); - if (typ & 1) + if (type & 1) { - memcpy(ins->envVP, config.stdEnvP[i][0], 2*2*12); - ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[i]; - ins->envVSust = (uint8_t)config.stdVolEnvSust[i]; - ins->envVRepS = (uint8_t)config.stdVolEnvRepS[i]; - ins->envVRepE = (uint8_t)config.stdVolEnvRepE[i]; - ins->fadeOut = config.stdFadeOut[i]; + memcpy(ins->volEnvPoints, config.stdEnvPoints[i][0], 2*2*12); + ins->volEnvLength = (uint8_t)config.stdVolEnvLength[i]; + ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[i]; + ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[i]; + ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[i]; + ins->fadeout = config.stdFadeout[i]; ins->vibRate = (uint8_t)config.stdVibRate[i]; ins->vibDepth = (uint8_t)config.stdVibDepth[i]; ins->vibSweep = (uint8_t)config.stdVibSweep[i]; - ins->vibTyp = (uint8_t)config.stdVibTyp[i]; - ins->envVTyp = (uint8_t)config.stdVolEnvTyp[i]; + ins->vibType = (uint8_t)config.stdVibType[i]; + ins->volEnvFlags = (uint8_t)config.stdVolEnvFlags[i]; } - if (typ & 2) + if (type & 2) { - memcpy(ins->envPP, config.stdEnvP[i][1], 2*2*12); - ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[0]; - ins->envPSust = (uint8_t)config.stdPanEnvSust[0]; - ins->envPRepS = (uint8_t)config.stdPanEnvRepS[0]; - ins->envPRepE = (uint8_t)config.stdPanEnvRepE[0]; - ins->envPTyp = (uint8_t)config.stdPanEnvTyp[0]; + memcpy(ins->panEnvPoints, config.stdEnvPoints[i][1], 2*2*12); + ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0]; + ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0]; + ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0]; + ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0]; + ins->panEnvFlags = (uint8_t)config.stdPanEnvFlags[0]; } resumeMusic(); } -void setNoEnvelope(instrTyp *ins) +void setNoEnvelope(instr_t *ins) { if (ins == NULL) return; pauseMusic(); - memcpy(ins->envVP, config.stdEnvP[0][0], 2*2*12); - ins->envVPAnt = (uint8_t)config.stdVolEnvAnt[0]; - ins->envVSust = (uint8_t)config.stdVolEnvSust[0]; - ins->envVRepS = (uint8_t)config.stdVolEnvRepS[0]; - ins->envVRepE = (uint8_t)config.stdVolEnvRepE[0]; - ins->envVTyp = 0; - - memcpy(ins->envPP, config.stdEnvP[0][1], 2*2*12); - ins->envPPAnt = (uint8_t)config.stdPanEnvAnt[0]; - ins->envPSust = (uint8_t)config.stdPanEnvSust[0]; - ins->envPRepS = (uint8_t)config.stdPanEnvRepS[0]; - ins->envPRepE = (uint8_t)config.stdPanEnvRepE[0]; - ins->envPTyp = 0; - - ins->fadeOut = 0; + memcpy(ins->volEnvPoints, config.stdEnvPoints[0][0], 2*2*12); + ins->volEnvLength = (uint8_t)config.stdVolEnvLength[0]; + ins->volEnvSustain = (uint8_t)config.stdVolEnvSustain[0]; + ins->volEnvLoopStart = (uint8_t)config.stdVolEnvLoopStart[0]; + ins->volEnvLoopEnd = (uint8_t)config.stdVolEnvLoopEnd[0]; + ins->volEnvFlags = 0; + + memcpy(ins->panEnvPoints, config.stdEnvPoints[0][1], 2*2*12); + ins->panEnvLength = (uint8_t)config.stdPanEnvLength[0]; + ins->panEnvSustain = (uint8_t)config.stdPanEnvSustain[0]; + ins->panEnvLoopStart = (uint8_t)config.stdPanEnvLoopStart[0]; + ins->panEnvLoopEnd = (uint8_t)config.stdPanEnvLoopEnd[0]; + ins->panEnvFlags = 0; + + ins->fadeout = 0; ins->vibRate = 0; ins->vibDepth = 0; ins->vibSweep = 0; - ins->vibTyp = 0; + ins->vibType = 0; resumeMusic(); } -bool patternEmpty(uint16_t nr) +bool patternEmpty(uint16_t pattNum) { - if (patt[nr] == NULL) + if (pattern[pattNum] == NULL) return true; - const uint8_t *scanPtr = (const uint8_t *)patt[nr]; - const uint32_t scanLen = pattLens[nr] * TRACK_WIDTH; + const uint8_t *scanPtr = (const uint8_t *)pattern[pattNum]; + const uint32_t scanLen = patternNumRows[pattNum] * TRACK_WIDTH; for (uint32_t i = 0; i < scanLen; i++) { @@ -2661,21 +2662,21 @@ bool patternEmpty(uint16_t nr) void updateChanNums(void) { - assert(!(song.antChn & 1)); + assert(!(song.numChannels & 1)); const int32_t maxChannelsShown = getMaxVisibleChannels(); - int32_t channelsShown = song.antChn; + int32_t channelsShown = song.numChannels; if (channelsShown > maxChannelsShown) channelsShown = maxChannelsShown; ui.numChannelsShown = (uint8_t)channelsShown; - ui.pattChanScrollShown = (song.antChn > maxChannelsShown); + ui.pattChanScrollShown = (song.numChannels > maxChannelsShown); if (ui.patternEditorShown) { - if (ui.channelOffset > song.antChn-ui.numChannelsShown) - setScrollBarPos(SB_CHAN_SCROLL, song.antChn - ui.numChannelsShown, true); + if (ui.channelOffset > song.numChannels-ui.numChannelsShown) + setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true); } if (ui.pattChanScrollShown) @@ -2687,7 +2688,7 @@ void updateChanNums(void) showPushButton(PB_CHAN_SCROLL_RIGHT); } - setScrollBarEnd(SB_CHAN_SCROLL, song.antChn); + setScrollBarEnd(SB_CHAN_SCROLL, song.numChannels); setScrollBarPageLength(SB_CHAN_SCROLL, ui.numChannelsShown); } else @@ -2705,51 +2706,48 @@ void updateChanNums(void) cursor.ch = ui.channelOffset+ui.numChannelsShown - 1; } -void conv8BitSample(int8_t *p, int32_t len, bool stereo) +void conv8BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign { if (stereo) { - len /= 2; + length >>= 1; - int8_t *p2 = &p[len]; - for (int32_t i = 0; i < len; i++) + int8_t *p2 = &p[length]; + for (int32_t i = 0; i < length; i++) { const int8_t l = p[i] ^ 0x80; const int8_t r = p2[i] ^ 0x80; - int16_t tmp16 = l + r; - p[i] = (int8_t)(tmp16 >> 1); + p[i] = (int8_t)((l + r) >> 1); } } else { - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) p[i] ^= 0x80; } } -void conv16BitSample(int8_t *p, int32_t len, bool stereo) +void conv16BitSample(int8_t *p, int32_t length, bool stereo) // changes sample sign { int16_t *p16_1 = (int16_t *)p; - len /= 2; if (stereo) { - len /= 2; + length >>= 1; - int16_t *p16_2 = (int16_t *)&p[len * 2]; - for (int32_t i = 0; i < len; i++) + int16_t *p16_2 = (int16_t *)p + length; + for (int32_t i = 0; i < length; i++) { const int16_t l = p16_1[i] ^ 0x8000; const int16_t r = p16_2[i] ^ 0x8000; - int32_t tmp32 = l + r; - p16_1[i] = (int16_t)(tmp32 >> 1); + p16_1[i] = (int16_t)((l + r) >> 1); } } else { - for (int32_t i = 0; i < len; i++) + for (int32_t i = 0; i < length; i++) p16_1[i] ^= 0x8000; } } @@ -2785,23 +2783,22 @@ void closeReplayer(void) bool setupReplayer(void) { for (int32_t i = 0; i < MAX_PATTERNS; i++) - pattLens[i] = 64; + patternNumRows[i] = 64; playMode = PLAYMODE_IDLE; songPlaying = false; // unmute all channels (must be done before resetChannels() call) - for (int32_t i = 0; i < MAX_VOICES; i++) + for (int32_t i = 0; i < MAX_CHANNELS; i++) editor.chnMode[i] = 1; resetChannels(); - song.len = 1; - song.antChn = 8; - editor.speed = song.speed = 125; - editor.tempo = song.tempo = 6; - editor.globalVol = song.globVol = 64; - song.initialTempo = song.tempo; + song.songLength = 1; + song.numChannels = 8; + editor.BPM = song.BPM = 125; + editor.speed = song.initialSpeed = song.speed = 6; + editor.globalVolume = song.globalVolume = 64; audio.linearPeriodsFlag = true; note2Period = linearPeriods; @@ -2820,14 +2817,14 @@ bool setupReplayer(void) showErrorMsgBox("Not enough memory!"); return false; } - instr[0]->samp[0].vol = 0; + instr[0]->smp[0].volume = 0; if (!allocateInstr(130)) { showErrorMsgBox("Not enough memory!"); return false; } - memset(instr[130], 0, sizeof (instrTyp)); + memset(instr[130], 0, sizeof (instr_t)); if (!allocateInstr(131)) // Instr. Ed. display instrument for unallocated/empty instruments { @@ -2835,9 +2832,9 @@ bool setupReplayer(void) return false; } - memset(instr[131], 0, sizeof (instrTyp)); + memset(instr[131], 0, sizeof (instr_t)); for (int32_t i = 0; i < 16; i++) - instr[131]->samp[i].pan = 128; + instr[131]->smp[i].panning = 128; editor.tmpPattern = 65535; // pattern editor update/redraw kludge return true; @@ -2860,10 +2857,10 @@ void startPlaying(int8_t mode, int16_t row) resetPlaybackTime(); // non-FT2 fix: If song speed was 0, set it back to initial speed on play - if (song.tempo == 0) - song.tempo = song.initialTempo; + if (song.speed == 0) + song.speed = song.initialSpeed; - audio.dTickSampleCounter = 0.0; // zero tick sample counter so that it will instantly initiate a tick + audio.tickSampleCounter64 = 0; // zero tick sample counter so that it will instantly initiate a tick unlockMixerCallback(); @@ -2885,13 +2882,13 @@ void stopPlaying(void) } else { - for (uint8_t i = 0; i < MAX_VOICES; i++) - playTone(i, 0, 97, -1, 0, 0); + for (uint8_t i = 0; i < MAX_CHANNELS; i++) + playTone(i, 0, NOTE_OFF, -1, 0, 0); } - // if song was playing, update local pattPos (fixes certain glitches) + // if song was playing, update local row (fixes certain glitches) if (songWasPlaying) - editor.pattPos = song.pattPos; + editor.row = song.row; #ifdef HAS_MIDI midi.currMIDIVibDepth = 0; @@ -2900,54 +2897,59 @@ void stopPlaying(void) memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab)); + // kludge to prevent UI from being out of sync with replayer vars on stop + song.songPos = editor.songPos; + song.row = editor.row; + song.pattNum = editor.editPattern; + ui.updatePosSections = true; ui.updatePatternEditor = true; // certain non-FT2 fixes - song.timer = editor.timer = 1; - song.globVol = editor.globalVol = 64; + song.tick = editor.tick = 1; + song.globalVolume = editor.globalVolume = 64; ui.drawGlobVolFlag = true; } // from keyboard/smp. ed. -void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch) +void playTone(uint8_t chNum, uint8_t insNum, uint8_t note, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch) { - instrTyp *ins = instr[inst]; + instr_t *ins = instr[insNum]; if (ins == NULL) return; - assert(stmm < MAX_VOICES && inst <= MAX_INST && ton <= 97); - stmTyp *ch = &stm[stmm]; + assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && note <= NOTE_OFF); + channel_t *ch = &channel[chNum]; // FT2 bugfix: Don't play tone if certain requirements are not met - if (ton != 97) + if (note != NOTE_OFF) { - if (ton == 0 || ton > 96) + if (note == 0 || note > 96) return; - sampleTyp *s = &ins->samp[ins->ta[ton-1] & 0xF]; + sample_t *s = &ins->smp[ins->note2SampleLUT[note-1] & 0xF]; - int16_t newTon = (int16_t)ton + s->relTon; - if (s->pek == NULL || s->len == 0 || newTon <= 0 || newTon >= 12*10) + int16_t finalNote = (int16_t)note + s->relativeNote; + if (s->dataPtr == NULL || s->length == 0 || finalNote <= 0 || finalNote >= 12*10) return; } // ------------------- lockAudio(); - if (inst != 0 && ton != 97) + if (insNum != 0 && note != NOTE_OFF) { - ch->tonTyp = (inst << 8) | (ch->tonTyp & 0xFF); - ch->instrNr = inst; + ch->noteData = (insNum << 8) | (ch->noteData & 0xFF); + ch->instrNum = insNum; } - ch->tonTyp = (ch->tonTyp & 0xFF00) | ton; - ch->effTyp = 0; - ch->eff = 0; + ch->noteData = (ch->noteData & 0xFF00) | note; + ch->efx = 0; + ch->efxData = 0; - startTone(ton, 0, 0, ch); + startTone(note, 0, 0, ch); - if (ton != 97) + if (note != NOTE_OFF) { retrigVolume(ch); retrigEnvelopeVibrato(ch); @@ -2963,39 +2965,39 @@ void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midi ch->midiVibDepth = midiVibDepth; ch->midiPitch = midiPitch; - fixaEnvelopeVibrato(ch); + updateChannel(ch); unlockAudio(); } // smp. ed. -void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch) +void playSample(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch) { - if (instr[inst] == NULL) + if (instr[insNum] == NULL) return; // for sampling playback line in Smp. Ed. - lastChInstr[stmm].instrNr = 255; - lastChInstr[stmm].sampleNr = 255; + lastChInstr[chNum].instrNum = 255; + lastChInstr[chNum].smpNum = 255; editor.curPlayInstr = 255; editor.curPlaySmp = 255; - assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97); - stmTyp *ch = &stm[stmm]; + assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF); + channel_t *ch = &channel[chNum]; - memcpy(&instr[130]->samp[0], &instr[inst]->samp[smpNr], sizeof (sampleTyp)); + memcpy(&instr[130]->smp[0], &instr[insNum]->smp[smpNum], sizeof (sample_t)); - uint8_t vol = instr[inst]->samp[smpNr].vol; + uint8_t vol = instr[insNum]->smp[smpNum].volume; lockAudio(); - ch->instrNr = 130; - ch->tonTyp = (ch->instrNr << 8) | ton; - ch->effTyp = 0; + ch->instrNum = 130; + ch->noteData = (ch->instrNum << 8) | note; + ch->efx = 0; - startTone(ton, 0, 0, ch); + startTone(note, 0, 0, ch); - if (ton != 97) + if (note != NOTE_OFF) { retrigVolume(ch); retrigEnvelopeVibrato(ch); @@ -3008,11 +3010,11 @@ void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t ch->midiVibDepth = midiVibDepth; ch->midiPitch = midiPitch; - fixaEnvelopeVibrato(ch); + updateChannel(ch); unlockAudio(); - while (ch->status & IS_NyTon); // wait for sample to latch in mixer + while (ch->status & IS_Trigger); // wait for sample to latch in mixer // for sampling playback line in Smp. Ed. editor.curPlayInstr = editor.curInstr; @@ -3020,52 +3022,45 @@ void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t } // smp. ed. -void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch, int32_t offs, int32_t len) +void playRange(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch, int32_t smpOffset, int32_t length) { - if (instr[inst] == NULL) + if (instr[insNum] == NULL) return; // for sampling playback line in Smp. Ed. - lastChInstr[stmm].instrNr = 255; - lastChInstr[stmm].sampleNr = 255; + lastChInstr[chNum].instrNum = 255; + lastChInstr[chNum].smpNum = 255; editor.curPlayInstr = 255; editor.curPlaySmp = 255; - assert(stmm < MAX_VOICES && inst <= MAX_INST && smpNr < MAX_SMP_PER_INST && ton <= 97); + assert(chNum < MAX_CHANNELS && insNum <= MAX_INST && smpNum < MAX_SMP_PER_INST && note <= NOTE_OFF); - stmTyp *ch = &stm[stmm]; - sampleTyp *s = &instr[130]->samp[0]; + channel_t *ch = &channel[chNum]; + sample_t *s = &instr[130]->smp[0]; - memcpy(s, &instr[inst]->samp[smpNr], sizeof (sampleTyp)); + memcpy(s, &instr[insNum]->smp[smpNum], sizeof (sample_t)); - uint8_t vol = instr[inst]->samp[smpNr].vol; - - if (s->typ & 16) - { - offs &= 0xFFFFFFFE; - len &= 0xFFFFFFFE; - } + uint8_t vol = instr[insNum]->smp[smpNum].volume; lockAudio(); - s->len = offs + len; - s->repS = 0; - s->repL = 0; - s->typ &= 16; // only keep 8-bit/16-bit flag (disable loop) + s->length = smpOffset + length; + s->loopStart = 0; + s->loopLength = 0; + DISABLE_LOOP(s->flags); // disable loop on sample #129 (placeholder) - int32_t samplePlayOffset = offs; - if (s->typ & 16) - samplePlayOffset >>= 1; + int32_t samplePlayOffset = smpOffset; - ch->instrNr = 130; - ch->tonTyp = (ch->instrNr << 8) | ton; - ch->effTyp = 0; + ch->instrNum = 130; + ch->noteData = (ch->instrNum << 8) | note; + ch->efx = 0; + ch->efxData = 0; - startTone(ton, 0, 0, ch); + startTone(note, 0, 0, ch); ch->smpStartPos = samplePlayOffset; - if (ton != 97) + if (note != NOTE_OFF) { retrigVolume(ch); retrigEnvelopeVibrato(ch); @@ -3078,11 +3073,11 @@ void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t ch->midiVibDepth = midiVibDepth; ch->midiPitch = midiPitch; - fixaEnvelopeVibrato(ch); + updateChannel(ch); unlockAudio(); - while (ch->status & IS_NyTon); // wait for sample to latch in mixer + while (ch->status & IS_Trigger); // wait for sample to latch in mixer // for sampling playback line in Smp. Ed. editor.curPlayInstr = editor.curInstr; @@ -3095,29 +3090,30 @@ void stopVoices(void) if (audioWasntLocked) lockAudio(); - stmTyp *ch = stm; - for (int32_t i = 0; i < MAX_VOICES; i++, ch++) + channel_t *ch = channel; + for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) { - lastChInstr[i].sampleNr = 255; - lastChInstr[i].instrNr = 255; + lastChInstr[i].smpNum = 255; + lastChInstr[i].instrNum = 255; - ch->tonTyp = 0; - ch->relTonNr = 0; - ch->instrNr = 0; + ch->noteData = 0; + ch->relativeNote = 0; + ch->smpNum = 0; + ch->smpPtr = NULL; + ch->instrNum = 0; ch->instrPtr = instr[0]; // important: set instrument pointer to instr 0 (placeholder instrument) ch->status = IS_Vol; ch->realVol = 0; ch->outVol = 0; ch->oldVol = 0; - ch->dFinalVol = 0.0; + ch->fFinalVol = 0.0f; ch->oldPan = 128; ch->outPan = 128; ch->finalPan = 128; ch->vibDepth = 0; ch->midiVibDepth = 0; ch->midiPitch = 0; - ch->smpPtr = NULL; - ch->portaDir = 0; // FT2 bugfix: weird 3xx behavior if not used with note + ch->portaDirection = 0; // FT2 bugfix: weird 3xx behavior if not used with note stopVoice(i); } @@ -3129,7 +3125,6 @@ void stopVoices(void) stopAllScopes(); resetAudioDither(); resetCachedMixerVars(); - resetCachedScopeVars(); // wait for scope thread to finish, so that we know pointers aren't deprecated while (editor.scopeThreadMutex); @@ -3147,10 +3142,10 @@ void resetReplayerState(void) if (songPlaying) { - song.globVol = 64; + song.globalVolume = 64; - stmTyp *ch = stm; - for (int32_t i = 0; i < song.antChn; i++, ch++) + channel_t *ch = channel; + for (int32_t i = 0; i < song.numChannels; i++, ch++) ch->status |= IS_Vol; } } @@ -3161,8 +3156,8 @@ void setNewSongPos(int32_t pos) setPos((int16_t)pos, 0, true); // non-FT2 fix: If song speed was 0, set it back to initial speed - if (song.tempo == 0) - song.tempo = song.initialTempo; + if (song.speed == 0) + song.speed = song.initialSpeed; } void decSongPos(void) @@ -3183,14 +3178,14 @@ void decSongPos(void) void incSongPos(void) { - if (song.songPos == song.len-1) + if (song.songPos == song.songLength-1) return; const bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (song.songPos < song.len-1) + if (song.songPos < song.songLength-1) setNewSongPos(song.songPos + 1); if (audioWasntLocked) @@ -3287,7 +3282,7 @@ void pbRecPtn(void) void setSyncedReplayerVars(void) { - uint8_t scopeUpdateStatus[MAX_VOICES]; + uint8_t scopeUpdateStatus[MAX_CHANNELS]; pattSyncEntry = NULL; chSyncEntry = NULL; @@ -3308,7 +3303,7 @@ void setSyncedReplayerVars(void) if (chSyncEntry == NULL) break; - for (int32_t i = 0; i < song.antChn; i++) + for (int32_t i = 0; i < song.numChannels; i++) scopeUpdateStatus[i] |= chSyncEntry->channels[i].status; // yes, OR the status if (!chQueuePop()) @@ -3356,23 +3351,23 @@ void setSyncedReplayerVars(void) // we have a new tick - editor.timer = pattSyncEntry->timer; + editor.tick = pattSyncEntry->tick; - if (editor.speed != pattSyncEntry->speed) + if (editor.BPM != pattSyncEntry->BPM) { - editor.speed = pattSyncEntry->speed; + editor.BPM = pattSyncEntry->BPM; ui.drawBPMFlag = true; } - if (editor.tempo != pattSyncEntry->tempo) + if (editor.speed != pattSyncEntry->speed) { - editor.tempo = pattSyncEntry->tempo; + editor.speed = pattSyncEntry->speed; ui.drawSpeedFlag = true; } - if (editor.globalVol != pattSyncEntry->globalVol) + if (editor.globalVolume != pattSyncEntry->globalVolume) { - editor.globalVol = pattSyncEntry->globalVol; + editor.globalVolume = pattSyncEntry->globalVolume; ui.drawGlobVolFlag = true; } @@ -3383,15 +3378,15 @@ void setSyncedReplayerVars(void) } // somewhat of a kludge... - if (editor.tmpPattern != pattSyncEntry->pattern || editor.pattPos != pattSyncEntry->patternPos) + if (editor.tmpPattern != pattSyncEntry->pattNum || editor.row != pattSyncEntry->row) { // set pattern number - editor.editPattern = editor.tmpPattern = pattSyncEntry->pattern; + editor.editPattern = editor.tmpPattern = pattSyncEntry->pattNum; checkMarkLimits(); ui.drawPattNumLenFlag = true; // set row - editor.pattPos = (uint8_t)pattSyncEntry->patternPos; + editor.row = (uint8_t)pattSyncEntry->row; ui.updatePatternEditor = true; } } diff --git a/src/ft2_replayer.h b/src/ft2_replayer.h @@ -4,13 +4,14 @@ #include <stdbool.h> #include "ft2_unicode.h" #include "mixer/ft2_windowed_sinc.h" +#include "ft2_cpu.h" enum { // voice flags IS_Vol = 1, // set volume IS_Period = 2, // set resampling rate - IS_NyTon = 4, // trigger new sample + IS_Trigger = 4, // trigger sample IS_Pan = 8, // set panning IS_QuickVol = 16, // 5ms volramp instead of tick ms @@ -37,12 +38,15 @@ enum CURSOR_EFX2 = 7 }; -// DO NOT TOUCH! -#define MIN_BPM 1 -#define MAX_BPM 999 -#define MAX_VOICES 32 -#define TRACK_WIDTH (5 * MAX_VOICES) +// do not touch these! +#define MIN_BPM 32 +#define MAX_BPM 255 +#define MAX_SPEED 31 +#define MAX_CHANNELS 32 +#define TRACK_WIDTH (5 * MAX_CHANNELS) #define MAX_FRQ 32000 +#define C4_FREQ 8363 +#define NOTE_OFF 97 #define MAX_NOTES (10*12*16+16) #define MAX_PATTERNS 256 #define MAX_PATT_LEN 256 @@ -55,6 +59,29 @@ enum #define MAX_SAMPLE_LEN 0x3FFFFFFF #define PROG_NAME_STR "Fasttracker II clone" +enum // sample flags +{ + LOOP_OFF = 0, + LOOP_FWD = 1, + LOOP_BIDI = 2, + SAMPLE_16BIT = 16, + SAMPLE_STEREO = 32, + SAMPLE_ADPCM = 64, // not an existing flag, but used by loader +}; + +enum // envelope flags +{ + ENV_ENABLED = 1, + ENV_SUSTAIN = 2, + ENV_LOOP = 4 +}; + +#define GET_LOOPTYPE(smpFlags) ((smpFlags) & (LOOP_FWD | LOOP_BIDI)) +#define DISABLE_LOOP(smpFlags) ((smpFlags) &= ~(LOOP_FWD | LOOP_BIDI)) +#define SAMPLE_LENGTH_BYTES(smp) (smp->length << !!(smp->flags & SAMPLE_16BIT)) +#define FINETUNE_MOD2XM(f) (((uint8_t)(f) & 0x0F) << 4) +#define FINETUNE_XM2MOD(f) ((uint8_t)(f) >> 4) + /* Some of the following structs MUST be packed! ** Please do NOT edit these structs unless you ** absolutely know what you are doing! @@ -64,113 +91,112 @@ enum #pragma pack(push) #pragma pack(1) #endif -typedef struct songHeaderTyp_t +typedef struct xmHdr_t { - char sig[17], name[21], progName[20]; - uint16_t ver; + char ID[17], name[20], x1A, progName[20]; + uint16_t version; int32_t headerSize; - uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed; - uint8_t songTab[256]; + uint16_t numOrders, songLoopStart, numChannels, numPatterns; + uint16_t numInstr, flags, speed, BPM; + uint8_t orders[256]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songHeaderTyp; +xmHdr_t; -typedef struct patternHeaderTyp_t +typedef struct xmPatHdr_t { - int32_t patternHeaderSize; - uint8_t typ; - int16_t pattLen; - uint16_t dataLen; + int32_t headerSize; + uint8_t type; + int16_t numRows; + uint16_t dataSize; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -patternHeaderTyp; +xmPatHdr_t; -typedef struct songMODInstrHeaderTyp_t +typedef struct modSmpHdr_t { char name[22]; - uint16_t len; - uint8_t fine, vol; - uint16_t repS, repL; + uint16_t length; + uint8_t finetune, volume; + uint16_t loopStart, loopLength; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songMODInstrHeaderTyp; +modSmpHdr_t; -typedef struct songMOD31HeaderTyp_t +typedef struct modHdr_t { char name[20]; - songMODInstrHeaderTyp instr[31]; - uint8_t len, repS, songTab[128]; - char sig[4]; + modSmpHdr_t smp[31]; + uint8_t numOrders, songLoopStart, orders[128]; + char ID[4]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songMOD31HeaderTyp; +modHdr_t; -typedef struct sampleHeaderTyp_t +typedef struct xmSmpHdr_t { - int32_t len, repS, repL; - uint8_t vol; - int8_t fine; - uint8_t typ, pan; - int8_t relTon; - uint8_t nameLen; + uint32_t length, loopStart, loopLength; + uint8_t volume; + int8_t finetune; + uint8_t flags, panning; + int8_t relativeNote; + uint8_t nameLength; // only handled before saving (ignored under load) char name[22]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -sampleHeaderTyp; +xmSmpHdr_t; -typedef struct instrHeaderTyp_t +typedef struct xmInsHdr_t { uint32_t instrSize; char name[22]; - uint8_t typ; - int16_t antSamp; + uint8_t type; + int16_t numSamples; int32_t sampleSize; - uint8_t ta[96]; - int16_t envVP[12][2], envPP[12][2]; - uint8_t envVPAnt, envPPAnt; - uint8_t envVSust, envVRepS, envVRepE; - uint8_t envPSust, envPRepS, envPRepE; - uint8_t envVTyp, envPTyp; - uint8_t vibTyp, vibSweep, vibDepth, vibRate; - uint16_t fadeOut; + uint8_t note2SampleLUT[96]; + int16_t volEnvPoints[12][2], panEnvPoints[12][2]; + uint8_t volEnvLength, panEnvLength; + uint8_t volEnvSustain, volEnvLoopStart, volEnvLoopEnd; + uint8_t panEnvSustain, panEnvLoopStart, panEnvLoopEnd; + uint8_t volEnvFlags, panEnvFlags; + uint8_t vibType, vibSweep, vibDepth, vibRate; + uint16_t fadeout; uint8_t midiOn, midiChannel; int16_t midiProgram, midiBend; int8_t mute; - uint8_t reserved[15]; - sampleHeaderTyp samp[16]; + uint8_t junk[15]; + xmSmpHdr_t smp[16]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -instrHeaderTyp; +xmInsHdr_t; -typedef struct tonTyp_t // must be packed on some systems, even though it consists of bytes only +typedef struct pattNote_t // must be packed! { - uint8_t ton, instr, vol, effTyp, eff; + uint8_t note, instr, vol, efx, efxData; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -tonTyp; +note_t; typedef struct syncedChannel_t // used for audio/video sync queue (pack to save RAM) { - uint8_t status; - uint8_t pianoNoteNr; - uint8_t sampleNr, instrNr; - uint16_t smpStartPos; - uint8_t vol; - double dHz; + uint8_t status, pianoNoteNum, smpNum, instrNum; + int32_t smpStartPos; + uint8_t scopeVolume; + uintCPUWord_t scopeDelta; } #ifdef __GNUC__ __attribute__ ((packed)) @@ -181,88 +207,87 @@ syncedChannel_t; #pragma pack(pop) #endif -typedef struct sampleTyp_t +typedef struct sample_t { char name[22+1]; - bool fixed; - int8_t fine, relTon, *pek, *origPek; - uint8_t vol, typ, pan; - int32_t len, repS, repL; + bool isFixed; + int8_t finetune, relativeNote, *dataPtr, *origDataPtr; + uint8_t volume, flags, panning; + int32_t length, loopStart, loopLength; // fix for resampling interpolation taps int8_t leftEdgeTapSamples8[SINC_TAPS+SINC_LEFT_TAPS]; int16_t leftEdgeTapSamples16[SINC_TAPS+SINC_LEFT_TAPS]; int16_t fixedSmp[SINC_RIGHT_TAPS]; int32_t fixedPos; -} sampleTyp; +} sample_t; -typedef struct instrTyp_t +typedef struct instr_t { bool midiOn, mute; - uint8_t midiChannel, ta[96]; - uint8_t envVPAnt, envPPAnt; - uint8_t envVSust, envVRepS, envVRepE; - uint8_t envPSust, envPRepS, envPRepE; - uint8_t envVTyp, envPTyp; - uint8_t vibTyp, vibSweep, vibDepth, vibRate; - uint16_t fadeOut; - int16_t envVP[12][2], envPP[12][2], midiProgram, midiBend; - int16_t antSamp; // used by loader only - sampleTyp samp[16]; -} instrTyp; - -typedef struct stmTyp_t + uint8_t midiChannel, note2SampleLUT[96]; + uint8_t volEnvLength, panEnvLength; + uint8_t volEnvSustain, volEnvLoopStart, volEnvLoopEnd; + uint8_t panEnvSustain, panEnvLoopStart, panEnvLoopEnd; + uint8_t volEnvFlags, panEnvFlags; + uint8_t vibType, vibSweep, vibDepth, vibRate; + uint16_t fadeout; + int16_t volEnvPoints[12][2], panEnvPoints[12][2], midiProgram, midiBend; + int16_t numSamples; // used by loader only + sample_t smp[16]; +} instr_t; + +typedef struct channel_t { - bool envSustainActive, stOff, mute; + bool envSustainActive, channelOff, mute; volatile uint8_t status, tmpStatus; - int8_t relTonNr, fineTune; - uint8_t sampleNr, instrNr, effTyp, eff, smpOffset, tremorSave, tremorPos; - uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDir; + int8_t relativeNote, finetune; + uint8_t smpNum, instrNum, efxData, efx, smpOffset, tremorSave, tremorPos; + uint8_t globVolSlideSpeed, panningSlideSpeed, waveCtrl, portaDirection; uint8_t glissFunk, vibPos, tremPos, vibSpeed, vibDepth, tremSpeed, tremDepth; - uint8_t pattPos, loopCnt, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed; + uint8_t jumpToRow, patLoopCounter, volSlideSpeed, fVolSlideUpSpeed, fVolSlideDownSpeed; uint8_t fPortaUpSpeed, fPortaDownSpeed, ePortaUpSpeed, ePortaDownSpeed; uint8_t portaUpSpeed, portaDownSpeed, retrigSpeed, retrigCnt, retrigVol; - uint8_t volKolVol, tonNr, envPPos, eVibPos, envVPos, realVol, oldVol, outVol; + uint8_t volColumnVol, noteNum, panEnvPos, eVibPos, volEnvPos, realVol, oldVol, outVol; uint8_t oldPan, outPan, finalPan; int16_t midiPitch; - uint16_t outPeriod, realPeriod, finalPeriod, tonTyp, wantPeriod, portaSpeed; - uint16_t envVCnt, envPCnt, eVibAmp, eVibSweep; - uint16_t fadeOutAmp, fadeOutSpeed, midiVibDepth; - int32_t envVIPValue, envPIPValue, envVAmp, envPAmp; + uint16_t outPeriod, realPeriod, finalPeriod, noteData, wantPeriod, portaSpeed; + uint16_t volEnvTick, panEnvTick, eVibAmp, eVibSweep; + uint16_t fadeoutVol, fadeoutSpeed, midiVibDepth; + int32_t volEnvDelta, panEnvDelta, volEnvValue, panEnvValue; int32_t oldFinalPeriod, smpStartPos; - uint8_t finalVol; // 0..255 (for visuals) - double dFinalVol; // 0.0 .. 1.0 (for mixer) + float fFinalVol; // 0.0f .. 1.0f - sampleTyp *smpPtr; - instrTyp *instrPtr; -} stmTyp; + sample_t *smpPtr; + instr_t *instrPtr; +} channel_t; -typedef struct songTyp_t +typedef struct song_t { bool pBreakFlag, posJumpFlag, isModified; char name[20+1], instrName[1+MAX_INST][22+1]; - uint8_t curReplayerTimer, curReplayerPattPos, curReplayerSongPos, curReplayerPattNr; // used for audio/video sync queue - uint8_t pattDelTime, pattDelTime2, pBreakPos, songTab[MAX_ORDERS]; - int16_t songPos, pattNr, pattPos, pattLen; - int32_t antChn; - uint16_t len, repS, speed, tempo, globVol, timer, ver, initialTempo; + uint8_t curReplayerTick, curReplayerRow, curReplayerSongPos, curReplayerPattNum; // used for audio/video sync queue + uint8_t pattDelTime, pattDelTime2, pBreakPos, orders[MAX_ORDERS]; + int16_t songPos, pattNum, row, currNumRows; + uint16_t songLength, songLoopStart, BPM, speed, initialSpeed, globalVolume, tick; + int32_t numChannels; uint64_t musicTime64; -} songTyp; +} song_t; -double getSampleC4Rate(sampleTyp *s); +double getSampleC4Rate(sample_t *s); void setNewSongPos(int32_t pos); void resetReplayerState(void); void fixString(char *str, int32_t lastChrPos); // removes leading spaces and 0x1A chars void fixSongName(void); -void fixInstrAndSampleNames(int16_t nr); +void fixInstrAndSampleNames(int16_t insNum); void calcReplayerVars(int32_t rate); // used on external sample load and during sample loading in some module formats -void tuneSample(sampleTyp *s, const int32_t midCFreq, bool linearPeriodsFlag); +void tuneSample(sample_t *s, const int32_t midCFreq, bool linearPeriodsFlag); void calcReplayerLogTab(void); @@ -272,10 +297,10 @@ double dPeriod2Hz(int32_t period); int32_t getPianoKey(uint16_t period, int8_t finetune, int8_t relativeNote); // for piano in Instr. Ed. -bool allocateInstr(int16_t nr); -void freeInstr(int32_t nr); +bool allocateInstr(int16_t insNum); +void freeInstr(int32_t insNum); void freeAllInstr(void); -void freeSample(int16_t nr, int16_t nr2); +void freeSample(int16_t insNum, int16_t smpNum); void freeAllPatterns(void); void updateChanNums(void); @@ -285,28 +310,28 @@ void resetMusic(void); void startPlaying(int8_t mode, int16_t row); void stopPlaying(void); void stopVoices(void); -void setPos(int16_t songPos, int16_t pattPos, bool resetTimer); +void setPos(int16_t songPos, int16_t row, bool resetTimer); void pauseMusic(void); // stops reading pattern data void resumeMusic(void); // starts reading pattern data void setSongModifiedFlag(void); void removeSongModifiedFlag(void); -void playTone(uint8_t stmm, uint8_t inst, uint8_t ton, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch); -void playSample(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch); -void playRange(uint8_t stmm, uint8_t inst, uint8_t smpNr, uint8_t ton, uint16_t midiVibDepth, uint16_t midiPitch, int32_t offs, int32_t len); -void keyOff(stmTyp *ch); -void conv8BitSample(int8_t *p, int32_t len, bool stereo); -void conv16BitSample(int8_t *p, int32_t len, bool stereo); -void delta2Samp(int8_t *p, int32_t len, uint8_t typ); -void samp2Delta(int8_t *p, int32_t len, uint8_t typ); -void setPatternLen(uint16_t nr, int16_t len); -void setFrqTab(bool linear); +void playTone(uint8_t chNum, uint8_t insNum, uint8_t note, int8_t vol, uint16_t midiVibDepth, uint16_t midiPitch); +void playSample(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch); +void playRange(uint8_t chNum, uint8_t insNum, uint8_t smpNum, uint8_t note, uint16_t midiVibDepth, uint16_t midiPitch, int32_t smpOffset, int32_t length); +void keyOff(channel_t *ch); +void conv8BitSample(int8_t *p, int32_t length, bool stereo); // changes sample sign +void conv16BitSample(int8_t *p, int32_t length, bool stereo); // changes sample sign +void delta2Samp(int8_t *p, int32_t length, uint8_t smpFlags); +void samp2Delta(int8_t *p, int32_t length, uint8_t smpFlags); +void setPatternLen(uint16_t pattNum, int16_t numRows); +void setFrequencyTable(bool linearPeriodsFlag); void tickReplayer(void); // periodically called from audio callback void resetChannels(void); -bool patternEmpty(uint16_t nr); -int16_t getUsedSamples(int16_t nr); -int16_t getRealUsedSamples(int16_t nr); -void setStdEnvelope(instrTyp *ins, int16_t i, uint8_t typ); -void setNoEnvelope(instrTyp *ins); +bool patternEmpty(uint16_t pattNum); +int16_t getUsedSamples(int16_t smpNum); +int16_t getRealUsedSamples(int16_t smpNum); +void setStdEnvelope(instr_t *ins, int16_t i, uint8_t type); +void setNoEnvelope(instr_t *ins); void setSyncedReplayerVars(void); void decSongPos(void); void incSongPos(void); @@ -324,8 +349,8 @@ extern int8_t playMode; extern bool songPlaying, audioPaused, musicPaused; extern volatile bool replayerBusy; extern const uint16_t *note2Period; -extern int16_t pattLens[MAX_PATTERNS]; -extern stmTyp stm[MAX_VOICES]; -extern songTyp song; -extern instrTyp *instr[132]; -extern tonTyp *patt[MAX_PATTERNS]; +extern int16_t patternNumRows[MAX_PATTERNS]; +extern channel_t channel[MAX_CHANNELS]; +extern song_t song; +extern instr_t *instr[132]; +extern note_t *pattern[MAX_PATTERNS]; diff --git a/src/ft2_sample_ed.c b/src/ft2_sample_ed.c @@ -18,7 +18,7 @@ #include "ft2_audio.h" #include "ft2_pattern_ed.h" #include "ft2_gui.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_video.h" #include "ft2_inst_ed.h" #include "ft2_sample_ed.h" @@ -27,6 +27,7 @@ #include "ft2_diskop.h" #include "ft2_keyboard.h" #include "ft2_structs.h" +#include "ft2_replayer.h" #include "mixer/ft2_windowed_sinc.h" // SINC_TAPS, SINC_NEGATIVE_TAPS static const char sharpNote1Char[12] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' }; @@ -36,112 +37,205 @@ static const char flatNote2Char[12] = { '-', 'b', '-', 'b', '-', '-', 'b', '-', static char smpEd_SysReqText[64]; static int8_t *smpCopyBuff; -static bool updateLoopsOnMouseUp, writeSampleFlag; +static bool updateLoopsOnMouseUp, writeSampleFlag, smpCopyDidCopyWholeSample; static int32_t smpEd_OldSmpPosLine = -1; static int32_t smpEd_ViewSize, smpEd_ScrPos, smpCopySize, smpCopyBits; static int32_t old_Rx1, old_Rx2, old_ViewSize, old_SmpScrPos; -static int32_t lastMouseX, lastMouseY, lastDrawX, lastDrawY, mouseXOffs, curSmpRepS, curSmpRepL; +static int32_t lastMouseX, lastMouseY, lastDrawX, lastDrawY, mouseXOffs, curSmpLoopStart, curSmpLoopLength; static double dScrPosScaled, dPos2ScrMul, dScr2SmpPosMul; +static sample_t smpCopySample; static SDL_Thread *thread; // globals int32_t smpEd_Rx1 = 0, smpEd_Rx2 = 0; // allocs sample with proper alignment and padding for branchless resampling interpolation -bool allocateTmpSmpData(sampleTyp *s, int32_t length) +bool allocateSmpData(sample_t *s, int32_t length, bool sample16Bit) { - s->origPek = (int8_t *)malloc(length + LOOP_FIX_LEN); - if (s->origPek == NULL) + if (sample16Bit) + length <<= 1; + + s->origDataPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); + if (s->origDataPtr == NULL) { - s->pek = NULL; + s->dataPtr = NULL; return false; } - s->pek = s->origPek + SMP_DAT_OFFSET; + s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET; + return true; +} + +bool allocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit) +{ + if (sample16Bit) + length <<= 1; + + int8_t *newPtr = (int8_t *)malloc(length + SAMPLE_PAD_LENGTH); + if (newPtr == NULL) + return false; + + sp->origPtr = newPtr; + + sp->ptr = sp->origPtr + SMP_DAT_OFFSET; return true; } // reallocs sample with proper alignment and padding for branchless resampling interpolation -bool reallocateTmpSmpData(sampleTyp *s, int32_t length) +bool reallocateSmpData(sample_t *s, int32_t length, bool sample16Bit) { - if (s->origPek == NULL) - return allocateTmpSmpData(s, length); + if (s->origDataPtr == NULL) + return allocateSmpData(s, length, sample16Bit); - s->origPek = (int8_t *)realloc(s->origPek, length + LOOP_FIX_LEN); - if (s->origPek == NULL) - { - s->pek = NULL; + if (sample16Bit) + length <<= 1; + + int8_t *newPtr = (int8_t *)realloc(s->origDataPtr, length + SAMPLE_PAD_LENGTH); + if (newPtr == NULL) + return false; + + s->origDataPtr = newPtr; + s->dataPtr = s->origDataPtr + SMP_DAT_OFFSET; + + return true; +} + +// reallocs sample with proper alignment and padding for branchless resampling interpolation +bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit) +{ + if (sp->origPtr == NULL) + return allocateSmpDataPtr(sp, length, sample16Bit); + + if (sample16Bit) + length <<= 1; + + int8_t *newPtr = (int8_t *)realloc(sp->origPtr, length + SAMPLE_PAD_LENGTH); + if (newPtr == NULL) return false; + + sp->origPtr = newPtr; + sp->ptr = sp->origPtr + SMP_DAT_OFFSET; + + return true; +} + +void setSmpDataPtr(sample_t *s, smpPtr_t *sp) +{ + s->origDataPtr = sp->origPtr; + s->dataPtr = sp->ptr; +} + +void freeSmpDataPtr(smpPtr_t *sp) +{ + if (sp->origPtr != NULL) + { + free(sp->origPtr); + sp->origPtr = NULL; + } + + sp->ptr = NULL; +} + +void freeSmpData(sample_t *s) +{ + if (s->origDataPtr != NULL) + { + free(s->origDataPtr); + s->origDataPtr = NULL; + } + + s->dataPtr = NULL; + s->isFixed = false; +} + +bool cloneSample(sample_t *src, sample_t *dst) +{ + smpPtr_t sp; + + freeSmpData(dst); + memcpy(dst, src, sizeof (sample_t)); + + dst->origDataPtr = dst->dataPtr = NULL; + dst->isFixed = false; + + if (src->length > 0 && src->dataPtr != NULL) + { + bool sample16Bit = !!(src->flags & SAMPLE_16BIT); + if (!allocateSmpDataPtr(&sp, src->length, sample16Bit)) + return false; + + memcpy(sp.ptr, src->dataPtr, src->length); + setSmpDataPtr(dst, &sp); + fixSample(dst); } - s->pek = s->origPek + SMP_DAT_OFFSET; return true; } -sampleTyp *getCurSample(void) +sample_t *getCurSample(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL) return NULL; - return &instr[editor.curInstr]->samp[editor.curSmp]; + return &instr[editor.curInstr]->smp[editor.curSmp]; } -void checkSampleRepeat(sampleTyp *s) +void sanitizeSample(sample_t *s) { - if (s->repS < 0) s->repS = 0; - if (s->repL < 0) s->repL = 0; - if (s->repS > s->len) s->repS = s->len; - if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; + if (s == NULL) + return; + + // if a sample has both forward loop and pingpong loop set, it means pingpong loop (FT2 mixer behavior) + if (GET_LOOPTYPE(s->flags) == (LOOP_FWD | LOOP_BIDI)) + s->flags &= ~LOOP_FWD; // remove forward loop flag + + if (s->volume > 64) + s->volume = 64; - if (s->repL == 0) // non-FT2 fix: disable loop if loop length is 0 + s->relativeNote = CLAMP(s->relativeNote, -48, 71); + s->length = CLAMP(s->length, 0, MAX_SAMPLE_LEN); + + if (s->loopStart < 0 || s->loopLength <= 0 || s->loopStart+s->loopLength > s->length) { - s->repS = 0; - s->typ &= ~3; + s->loopStart = 0; + s->loopLength = 0; + DISABLE_LOOP(s->flags); } } +static int32_t myMod(int32_t a, int32_t b) // works on negative numbers! +{ + int32_t c = a % b; + return (c < 0) ? (c + b) : c; +} + // modifies samples before index 0, and after loop/end (for branchless mixer interpolation (kinda)) -void fixSample(sampleTyp *s) +void fixSample(sample_t *s) { int32_t pos; bool backwards; assert(s != NULL); - if (s->origPek == NULL || s->pek == NULL) + if (s->dataPtr == NULL || s->length <= 0) { - s->fixed = false; + s->isFixed = false; s->fixedPos = 0; return; // empty sample } - const bool sample16Bit = (s->typ & 16) ? true : false; - int16_t *ptr16 = (int16_t *)s->pek; - uint8_t loopType = s->typ & 3; - int32_t len = s->len; - int32_t loopStart = s->repS; - int32_t loopLen = s->repL; - int32_t loopEnd = s->repS + s->repL; - - if (sample16Bit) - { - len >>= 1; - loopStart >>= 1; - loopLen >>= 1; - loopEnd >>= 1; - } - - if (len < 1) - { - s->fixed = false; - s->fixedPos = 0; - return; // empty sample - } + const bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + int16_t *ptr16 = (int16_t *)s->dataPtr; + uint8_t loopType = GET_LOOPTYPE(s->flags); + int32_t length = s->length; + int32_t loopStart = s->loopStart; + int32_t loopLength = s->loopLength; + int32_t loopEnd = s->loopStart + s->loopLength; // treat loop as disabled if loopLen == 0 (FT2 does this) - if (loopType != 0 && loopLen <= 0) + if (loopType != 0 && loopLength <= 0) { loopType = 0; - loopStart = loopLen = loopEnd = 0; + loopStart = loopLength = loopEnd = 0; } /* All negative taps should be equal to the first sample point when at sampling @@ -158,336 +252,253 @@ void fixSample(sampleTyp *s) else { for (int32_t i = 0; i < SINC_LEFT_TAPS; i++) - s->pek[i-SINC_LEFT_TAPS] = s->pek[0]; + s->dataPtr[i-SINC_LEFT_TAPS] = s->dataPtr[0]; } - // no loop - if (loopType == 0) + if (loopType == LOOP_OFF) // no loop { if (sample16Bit) { for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) - ptr16[len+i] = ptr16[len-1]; + ptr16[length+i] = ptr16[length-1]; } else { for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) - s->pek[len+i] = s->pek[len-1]; + s->dataPtr[length+i] = s->dataPtr[length-1]; } s->fixedPos = 0; // this value is not used for non-looping samples, set to zero - - s->fixed = false; // no fixed samples inside actual sample data + s->isFixed = false; // no fixed samples inside actual sample data return; } - if (s->fixed) - return; // already fixed - s->fixedPos = loopEnd; - s->fixed = true; - - // special-case for loop-lengt of 1 - if (loopLen == 1) - { - if (sample16Bit) - { - for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++) - s->leftEdgeTapSamples16[i] = ptr16[loopStart]; - - for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) - { - s->fixedSmp[i] = ptr16[loopEnd+i]; - ptr16[loopEnd+i] = ptr16[loopStart]; - } - } - else - { - for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++) - s->leftEdgeTapSamples8[i] = s->pek[loopStart]; - - for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) - { - s->fixedSmp[i] = s->pek[loopEnd+i]; - s->pek[loopEnd+i] = s->pek[loopStart]; - } - } + s->isFixed = true; - return; - } - - if (loopType == 1) + if (loopType == LOOP_FWD) // forward loop { - // forward loop if (sample16Bit) { - // left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples) - for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++) + // left edge (we need SINC_TAPS amount of taps starting from the center tap) + for (int32_t i = -SINC_LEFT_TAPS; i < SINC_TAPS; i++) { - if (i < SINC_LEFT_TAPS) // negative taps - { - pos = loopEnd-i; - if (pos <= loopStart) // XXX: Correct? - pos += loopLen; - } - else // positive taps - { - pos = loopStart + ((i-SINC_LEFT_TAPS) % loopLen); - } - - s->leftEdgeTapSamples16[i] = ptr16[pos]; + pos = loopStart + myMod(i, loopLength); + s->leftEdgeTapSamples16[SINC_LEFT_TAPS+i] = ptr16[pos]; } // right edge (change actual sample data since data after loop is never used) + pos = loopStart; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) { s->fixedSmp[i] = ptr16[loopEnd+i]; - ptr16[loopEnd+i] = ptr16[loopStart + (i % loopLen)]; + ptr16[loopEnd+i] = ptr16[pos]; + + if (++pos >= loopEnd) + pos -= loopLength; } } - else + else // 8-bit { - // left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples) - for (int32_t i = 0; i < SINC_TAPS+SINC_LEFT_TAPS; i++) + // left edge (we need SINC_TAPS amount of taps starting from the center tap) + for (int32_t i = -SINC_LEFT_TAPS; i < SINC_TAPS; i++) { - if (i < SINC_LEFT_TAPS) // negative taps - { - pos = loopEnd-i; - if (pos <= loopStart) // XXX: Correct? - pos += loopLen; - } - else // positive taps - { - pos = loopStart + ((i-SINC_LEFT_TAPS) % loopLen); - } - - s->leftEdgeTapSamples8[i] = s->pek[pos]; + pos = loopStart + myMod(i, loopLength); + s->leftEdgeTapSamples8[SINC_LEFT_TAPS+i] = s->dataPtr[pos]; } // right edge (change actual sample data since data after loop is never used) + pos = loopStart; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) { - s->fixedSmp[i] = s->pek[loopEnd+i]; - s->pek[loopEnd+i] = s->pek[loopStart + (i % loopLen)]; + s->fixedSmp[i] = s->dataPtr[loopEnd+i]; + s->dataPtr[loopEnd+i] = s->dataPtr[pos]; + + if (++pos >= loopEnd) + pos -= loopLength; } } } - else + else // pingpong loop { - // pingpong loop if (sample16Bit) { - // left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples) + // left edge (positive taps, we need SINC_TAPS amount of taps starting from the center tap) pos = loopStart; backwards = false; - for (int32_t i = 0; i < SINC_LEFT_TAPS; i++) + for (int32_t i = 0; i < SINC_TAPS; i++) { if (backwards) { - if (--pos < loopStart) + if (pos < loopStart) { pos = loopStart; backwards = false; } } - else + else if (pos >= loopEnd) // forwards { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } + pos = loopEnd-1; + backwards = true; } - s->leftEdgeTapSamples16[3-i] = ptr16[pos]; - } + s->leftEdgeTapSamples16[SINC_LEFT_TAPS+i] = ptr16[pos]; - pos = loopStart; - backwards = true; - for (int32_t i = 3; i < SINC_TAPS+SINC_LEFT_TAPS; i++) - { if (backwards) - { - if (--pos < loopStart) - { - pos = loopStart; - backwards = false; - } - } + pos--; else - { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } - } - - s->leftEdgeTapSamples16[i] = ptr16[pos]; + pos++; } + // left edge (negative taps) + for (int32_t i = 0; i < SINC_LEFT_TAPS; i++) + s->leftEdgeTapSamples16[(SINC_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples16[SINC_LEFT_TAPS+1+i]; + // right edge (change actual sample data since data after loop is never used) pos = loopEnd-1; backwards = true; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) { - s->fixedSmp[i] = ptr16[loopEnd+i]; - - ptr16[loopEnd+i] = ptr16[pos]; if (backwards) { - if (--pos < loopStart) + if (pos < loopStart) { pos = loopStart; backwards = false; } } - else + else if (pos >= loopEnd) // forwards { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } + pos = loopEnd-1; + backwards = true; } + + s->fixedSmp[i] = ptr16[loopEnd+i]; + ptr16[loopEnd+i] = ptr16[pos]; + + if (backwards) + pos--; + else + pos++; } } - else + else // 8-bit { - // left edge (we need SINC_LEFT_TAPS amount of extra unrolled samples) + // left edge (positive taps, we need SINC_TAPS amount of taps starting from the center tap) pos = loopStart; backwards = false; - for (int32_t i = 0; i < SINC_LEFT_TAPS; i++) + for (int32_t i = 0; i < SINC_TAPS; i++) { if (backwards) { - if (--pos < loopStart) + if (pos < loopStart) { pos = loopStart; backwards = false; } } - else + else if (pos >= loopEnd) // forwards { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } + pos = loopEnd-1; + backwards = true; } - s->leftEdgeTapSamples8[3-i] = s->pek[pos]; - } + s->leftEdgeTapSamples8[SINC_LEFT_TAPS+i] = s->dataPtr[pos]; - pos = loopStart; - backwards = true; - for (int32_t i = 3; i < SINC_TAPS+SINC_LEFT_TAPS; i++) - { if (backwards) - { - if (--pos < loopStart) - { - pos = loopStart; - backwards = false; - } - } + pos--; else - { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } - } - - s->leftEdgeTapSamples8[i] = s->pek[pos]; + pos++; } + // left edge (negative taps) + for (int32_t i = 0; i < SINC_LEFT_TAPS; i++) + s->leftEdgeTapSamples8[(SINC_LEFT_TAPS-1)-i] = s->leftEdgeTapSamples8[SINC_LEFT_TAPS+1+i]; + // right edge (change actual sample data since data after loop is never used) pos = loopEnd-1; backwards = true; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) { - s->fixedSmp[i] = s->pek[loopEnd+i]; - - s->pek[loopEnd+i] = s->pek[pos]; if (backwards) { - if (--pos < loopStart) + if (pos < loopStart) { pos = loopStart; backwards = false; } } - else + else if (pos >= loopEnd) // forwards { - if (++pos >= loopEnd) - { - pos = loopEnd-1; - backwards = true; - } + pos = loopEnd-1; + backwards = true; } + + s->fixedSmp[i] = s->dataPtr[loopEnd+i]; + s->dataPtr[loopEnd+i] = s->dataPtr[pos]; + + if (backwards) + pos--; + else + pos++; } } } } // restores interpolation tap samples after loop/end -void restoreSample(sampleTyp *s) +void unfixSample(sample_t *s) { assert(s != NULL); - if (s->origPek == NULL || s->pek == NULL || !s->fixed) + if (s->dataPtr == NULL || !s->isFixed) return; // empty sample or not fixed (f.ex. no loop) - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - // 16-bit sample - int16_t *ptr16 = (int16_t *)s->pek + s->fixedPos; + int16_t *ptr16 = (int16_t *)s->dataPtr + s->fixedPos; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) ptr16[i] = s->fixedSmp[i]; } - else + else // 8-bit { - // 8-bit sample - int8_t *ptr8 = s->pek + s->fixedPos; + int8_t *ptr8 = s->dataPtr + s->fixedPos; for (int32_t i = 0; i < SINC_RIGHT_TAPS; i++) ptr8[i] = (int8_t)s->fixedSmp[i]; } - s->fixed = false; + s->isFixed = false; } -int16_t getSampleValue(int8_t *ptr, uint8_t typ, int32_t pos) +double getSampleValue(int8_t *smpData, int32_t position, bool sample16Bit) { - assert(pos >= 0); - if (ptr == NULL) + if (smpData == NULL) return 0; - if (typ & 16) + if (sample16Bit) { - assert(!(pos & 1)); - return *(int16_t *)&ptr[pos]; + position <<= 1; + return *((int16_t *)&smpData[position]); } else { - return ptr[pos]; + return smpData[position]; } } -void putSampleValue(int8_t *ptr, uint8_t typ, int32_t pos, int16_t val) +void putSampleValue(int8_t *smpData, int32_t position, double dSample, bool sample16Bit) { - assert(pos >= 0); - if (ptr == NULL) - return; + DROUND(dSample); + int32_t sample = (int32_t)dSample; - if (typ & 16) + if (sample16Bit) { - assert(!(pos & 1)); - *(int16_t *)&ptr[pos] = val; + CLAMP16(sample); + *((int16_t *)&smpData[position<<1]) = (int16_t)sample; } else { - ptr[pos] = (int8_t)val; + CLAMP8(sample); + smpData[position] = (int8_t)sample; } } @@ -501,9 +512,10 @@ void clearCopyBuffer(void) smpCopySize = 0; smpCopyBits = 8; + smpCopyDidCopyWholeSample = false; } -int32_t getSampleMiddleCRate(sampleTyp *s) +int32_t getSampleMiddleCRate(sample_t *s) { return (int32_t)(getSampleC4Rate(s) + 0.5); // rounded } @@ -536,7 +548,7 @@ static void updateViewSize(void) static void updateScrPos(void) { - dScrPosScaled = trunc(smpEd_ScrPos * dPos2ScrMul); + dScrPosScaled = floor(smpEd_ScrPos * dPos2ScrMul); } // sample pos -> screen x pos (if outside of visible area, will return <0 or >=SCREEN_W) @@ -545,14 +557,14 @@ static int32_t smpPos2Scr(int32_t pos) if (smpEd_ViewSize <= 0) return -1; - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); if (s == NULL) return -1; - if (pos > s->len) - pos = s->len; + if (pos > s->length) + pos = s->length; - double dPos = (pos * dPos2ScrMul) + 0.5; // rounding is needed here (+ 0.5) + double dPos = (pos * dPos2ScrMul) + 0.5; // pre-rounding bias is needed here dPos -= dScrPosScaled; // this is important, or else the result can mess up in some cases @@ -568,7 +580,7 @@ static int32_t scr2SmpPos(int32_t x) if (smpEd_ViewSize <= 0) return 0; - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); if (s == NULL) return 0; @@ -576,67 +588,73 @@ static int32_t scr2SmpPos(int32_t x) x = 0; double dPos = (dScrPosScaled + x) * dScr2SmpPosMul; - x = (int32_t)dPos; - if (x > s->len) - x = s->len; - - if (s->typ & 16) - x &= 0xFFFFFFFE; + x = (int32_t)dPos; + if (x > s->length) + x = s->length; return x; } -static void fixRepeatGadgets(void) +static void hideLoopPinSprites(void) { - bool showLoopPins = true; - - sampleTyp *s = getCurSample(); - if (s == NULL || s->len <= 0 || s->pek == NULL || (s->typ & 3) == 0 || !ui.sampleEditorShown) - showLoopPins = false; + hideSprite(SPRITE_LEFT_LOOP_PIN); + hideSprite(SPRITE_RIGHT_LOOP_PIN); +} - if (ui.sampleEditorShown) +static void fixLoopGadgets(void) +{ + if (!ui.sampleEditorShown) { - // draw Repeat/Replen. numbers - hexOutBg(536, 375, PAL_FORGRND, PAL_DESKTOP, curSmpRepS, 8); - hexOutBg(536, 387, PAL_FORGRND, PAL_DESKTOP, curSmpRepL, 8); + hideLoopPinSprites(); + return; } + sample_t *s = getCurSample(); + + bool showLoopPins = true; + if (s == NULL || s->dataPtr == NULL || s->length <= 0 || GET_LOOPTYPE(s->flags) == LOOP_OFF) + showLoopPins = false; + + // draw Repeat/Replen. numbers + hexOutBg(536, 375, PAL_FORGRND, PAL_DESKTOP, curSmpLoopStart, 8); + hexOutBg(536, 387, PAL_FORGRND, PAL_DESKTOP, curSmpLoopLength, 8); + if (!showLoopPins) { - hideSprite(SPRITE_LEFT_LOOP_PIN); - hideSprite(SPRITE_RIGHT_LOOP_PIN); - return; + hideLoopPinSprites(); } + else + { + // draw sample loop points - // draw sample loop points - - int32_t repS = smpPos2Scr(curSmpRepS); - int32_t repE = smpPos2Scr(curSmpRepS+curSmpRepL); + const int32_t loopStart = smpPos2Scr(curSmpLoopStart); + const int32_t loopEnd = smpPos2Scr(curSmpLoopStart+curSmpLoopLength); - // do -8 test because part of the loop sprite sticks out on the left/right + // do -8 test because part of the loop sprite sticks out on the left/right - if (repS >= -8 && repS <= SAMPLE_AREA_WIDTH+8) - setSpritePos(SPRITE_LEFT_LOOP_PIN, (int16_t)(repS - 8), 174); - else - hideSprite(SPRITE_LEFT_LOOP_PIN); + if (loopStart >= -8 && loopStart <= SAMPLE_AREA_WIDTH+8) + setSpritePos(SPRITE_LEFT_LOOP_PIN, (int16_t)(loopStart - 8), 174); + else + hideSprite(SPRITE_LEFT_LOOP_PIN); - if (repE >= -8) - { - if (repE <= SAMPLE_AREA_WIDTH+8) - setSpritePos(SPRITE_RIGHT_LOOP_PIN, (int16_t)(repE - 8), 174); + if (loopEnd >= -8) + { + if (loopEnd <= SAMPLE_AREA_WIDTH+8) + setSpritePos(SPRITE_RIGHT_LOOP_PIN, (int16_t)(loopEnd - 8), 174); + else + hideSprite(SPRITE_RIGHT_LOOP_PIN); + } else + { hideSprite(SPRITE_RIGHT_LOOP_PIN); - } - else - { - hideSprite(SPRITE_RIGHT_LOOP_PIN); + } } } -static void fixSampleDrag(void) +static void fixSampleScrollbar(void) { - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); if (s == NULL) { setScrollBarPageLength(SB_SAMP_SCROLL, 0); @@ -646,11 +664,11 @@ static void fixSampleDrag(void) } setScrollBarPageLength(SB_SAMP_SCROLL, smpEd_ViewSize); - setScrollBarEnd(SB_SAMP_SCROLL, instr[editor.curInstr]->samp[editor.curSmp].len); + setScrollBarEnd(SB_SAMP_SCROLL, instr[editor.curInstr]->smp[editor.curSmp].length); setScrollBarPos(SB_SAMP_SCROLL, smpEd_ScrPos, false); } -static bool getCopyBuffer(int32_t size) +static bool getCopyBuffer(int32_t size, bool sample16Bit) { if (smpCopyBuff != NULL) free(smpCopyBuff); @@ -658,7 +676,7 @@ static bool getCopyBuffer(int32_t size) if (size > MAX_SAMPLE_LEN) size = MAX_SAMPLE_LEN; - smpCopyBuff = (int8_t *)malloc(size); + smpCopyBuff = (int8_t *)malloc(size << sample16Bit); if (smpCopyBuff == NULL) { smpCopySize = 0; @@ -671,50 +689,30 @@ static bool getCopyBuffer(int32_t size) static int32_t SDLCALL copySampleThread(void *ptr) { - bool error = false; - - int16_t destIns = editor.curInstr; - int16_t destSmp = editor.curSmp; - int16_t sourceIns = editor.srcInstr; - int16_t sourceSmp = editor.srcSmp; + sample_t *src = &instr[editor.srcInstr]->smp[editor.srcSmp]; + sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp]; pauseAudio(); - if (instr[destIns] == NULL) - error = !allocateInstr(destIns); - - if (!error) - { - freeSample(destIns, destSmp); - - sampleTyp *src = &instr[sourceIns]->samp[sourceSmp]; - sampleTyp *dst = &instr[destIns]->samp[destSmp]; + if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) + goto error; - if (instr[sourceIns] != NULL && src->origPek != NULL) - { - int8_t *p = (int8_t *)malloc(src->len + LOOP_FIX_LEN); - if (p != NULL) - { - memcpy(dst, src, sizeof (sampleTyp)); - memcpy(p, src->origPek, src->len + LOOP_FIX_LEN); - dst->origPek = p; - dst->pek = dst->origPek + SMP_DAT_OFFSET; - } - else error = true; - } - } + if (!cloneSample(src, dst)) + goto error; resumeAudio(); - if (error) - okBoxThreadSafe(0, "System message", "Not enough memory!"); - editor.updateCurSmp = true; setSongModifiedFlag(); setMouseBusy(false); return true; +error: + resumeAudio(); + okBoxThreadSafe(0, "System message", "Not enough memory!"); + return true; + (void)ptr; } @@ -743,11 +741,11 @@ void xchgSmp(void) // dstSmp <-> srcSmp return; } - sampleTyp *src = &instr[editor.curInstr]->samp[editor.srcSmp]; - sampleTyp *dst = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *src = &instr[editor.curInstr]->smp[editor.srcSmp]; + sample_t *dst = &instr[editor.curInstr]->smp[editor.curSmp]; lockMixerCallback(); - const sampleTyp dstTmp = *dst; + const sample_t dstTmp = *dst; *dst = *src; *src = dstTmp; unlockMixerCallback(); @@ -788,57 +786,51 @@ static void writeRange(void) } } -static int8_t getScaledSample(sampleTyp *s, int32_t index) // for drawing sample waveform in zoomed-in mode +static int32_t getScaledSample(sample_t *s, int32_t index) // for sample data viewer { - int8_t sample; - int32_t tmp32; + int32_t tmp32, sample; - const int32_t loopEnd = s->repS + s->repL; + const int32_t loopEnd = s->loopStart + s->loopLength; - if (s->pek == NULL || index < 0 || index >= s->len) + if (s->dataPtr == NULL || index < 0 || index >= s->length) return 0; - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - assert(!(index & 1)); - index >>= 1; - - int16_t *ptr16 = (int16_t *)s->pek; + int16_t *ptr16 = (int16_t *)s->dataPtr; // don't read fixed mixer interpolation samples, read the prestine ones instead - if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->len > loopEnd && s->fixed) + if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->length > loopEnd && s->isFixed) tmp32 = s->fixedSmp[index-s->fixedPos]; else tmp32 = ptr16[index]; sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 16); } - else + else // 8-bit { - // don't read fixed mixer interpolation samples, read the prestine ones instead - if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->len > loopEnd && s->fixed) + if (index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS && s->length > loopEnd && s->isFixed) tmp32 = s->fixedSmp[index-s->fixedPos]; else - tmp32 = s->pek[index]; + tmp32 = s->dataPtr[index]; sample = (int8_t)((tmp32 * SAMPLE_AREA_HEIGHT) >> 8); } - return sample; + return SAMPLE_AREA_Y_CENTER-sample; } -static void sampleLine(int16_t x1, int16_t x2, int16_t y1, int16_t y2) +void sampleLine(int32_t x1, int32_t x2, int32_t y1, int32_t y2) { - int16_t d; - - const int16_t dx = x2 - x1; - const int16_t ax = ABS(dx) * 2; - const int16_t sx = SGN(dx); - const int16_t dy = y2 - y1; - const int16_t ay = ABS(dy) * 2; - const int16_t sy = SGN(dy); - int16_t x = x1; - int16_t y = y1; + int32_t d; + const int32_t dx = x2 - x1; + const int32_t ax = ABS(dx) * 2; + const int32_t sx = SGN(dx); + const int32_t dy = y2 - y1; + const int32_t ay = ABS(dy) * 2; + const int32_t sy = SGN(dy); + int32_t x = x1; + int32_t y = y1; const uint32_t pal1 = video.palette[PAL_DESKTOP]; const uint32_t pal2 = video.palette[PAL_FORGRND]; const uint32_t pixVal = video.palette[PAL_PATTEXT]; @@ -1086,11 +1078,11 @@ static void getMinMax8(const void *p, uint32_t scanLen, int8_t *min8, int8_t *ma } // for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples) -static void getSpecialMinMax16(sampleTyp *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16) +static void getSpecialMinMax16(sample_t *s, int32_t index, int32_t scanEnd, int16_t *min16, int16_t *max16) { int16_t minVal2, maxVal2; - const int16_t *ptr16 = (const int16_t *)s->pek; + const int16_t *ptr16 = (const int16_t *)s->dataPtr; int16_t minVal = 32767; int16_t maxVal = -32768; @@ -1131,11 +1123,11 @@ static void getSpecialMinMax16(sampleTyp *s, int32_t index, int32_t scanEnd, int } // for scanning sample data peak where loopEnd+SINC_RIGHT_TAPS is within scan range (fixed interpolation tap samples) -static void getSpecialMinMax8(sampleTyp *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8) +static void getSpecialMinMax8(sample_t *s, int32_t index, int32_t scanEnd, int8_t *min8, int8_t *max8) { int8_t minVal2, maxVal2; - const int8_t *ptr8 = (const int8_t *)s->pek; + const int8_t *ptr8 = (const int8_t *)s->dataPtr; int8_t minVal = 127; int8_t maxVal = -128; @@ -1175,28 +1167,21 @@ static void getSpecialMinMax8(sampleTyp *s, int32_t index, int32_t scanEnd, int8 *max8 = maxVal; } -static void getSampleDataPeak(sampleTyp *s, int32_t index, int32_t numSamples, int16_t *outMin, int16_t *outMax) +static void getSampleDataPeak(sample_t *s, int32_t index, int32_t length, int16_t *outMin, int16_t *outMax) { int8_t min8, max8; int16_t min16, max16; - if (numSamples == 0 || s->pek == NULL || s->len <= 0) + if (length == 0 || s->dataPtr == NULL || s->length <= 0) { *outMin = SAMPLE_AREA_Y_CENTER; *outMax = SAMPLE_AREA_Y_CENTER; return; } - if (s->typ & 16) - { - assert(!(index & 1)); - index >>= 1; - numSamples >>= 1; - } - - if (s->fixed && s->len > s->repL+s->repS) + if (s->isFixed && s->length > s->loopLength+s->loopStart) { - const int32_t scanEnd = index + numSamples; + const int32_t scanEnd = index + length; /* If the scan area is including the fixed samples (for branchless mixer interpolation), ** do a special procedure to scan the original non-touched samples when needed. @@ -1204,16 +1189,14 @@ static void getSampleDataPeak(sampleTyp *s, int32_t index, int32_t numSamples, i const bool insideRange = index >= s->fixedPos && index < s->fixedPos+SINC_RIGHT_TAPS; if (insideRange || (index < s->fixedPos && scanEnd >= s->fixedPos)) { - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - // 16-bit sample getSpecialMinMax16(s, index, scanEnd, &min16, &max16); *outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16); *outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16); } - else + else // 8-bit { - // 8-bit sample getSpecialMinMax8(s, index, scanEnd, &min8, &max8); *outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8); *outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8); @@ -1223,18 +1206,16 @@ static void getSampleDataPeak(sampleTyp *s, int32_t index, int32_t numSamples, i } } - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - // 16-bit sample - const int16_t *smpPtr16 = (int16_t *)s->pek; - getMinMax16(&smpPtr16[index], numSamples, &min16, &max16); + const int16_t *smpPtr16 = (int16_t *)s->dataPtr; + getMinMax16(&smpPtr16[index], length, &min16, &max16); *outMin = SAMPLE_AREA_Y_CENTER - ((min16 * SAMPLE_AREA_HEIGHT) >> 16); *outMax = SAMPLE_AREA_Y_CENTER - ((max16 * SAMPLE_AREA_HEIGHT) >> 16); } - else + else // 8-bit { - // 8-bit sample - getMinMax8(&s->pek[index], numSamples, &min8, &max8); + getMinMax8(&s->dataPtr[index], length, &min8, &max8); *outMin = SAMPLE_AREA_Y_CENTER - ((min8 * SAMPLE_AREA_HEIGHT) >> 8); *outMax = SAMPLE_AREA_Y_CENTER - ((max8 * SAMPLE_AREA_HEIGHT) >> 8); } @@ -1242,8 +1223,6 @@ static void getSampleDataPeak(sampleTyp *s, int32_t index, int32_t numSamples, i static void writeWaveform(void) { - int16_t y1, y2, min, max; - // clear sample data area memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t)); @@ -1253,58 +1232,67 @@ static void writeWaveform(void) if (instr[editor.curInstr] == NULL || smpEd_ViewSize == 0) return; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->pek == NULL || s->len == 0) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->dataPtr == NULL || s->length <= 0) return; - y1 = SAMPLE_AREA_Y_CENTER - getScaledSample(s, scr2SmpPos(0)); - - uint32_t viewSizeSamples = smpEd_ViewSize; - if (s->typ & 16) - viewSizeSamples >>= 1; - - if (viewSizeSamples <= SAMPLE_AREA_WIDTH) + if (smpEd_ViewSize <= SAMPLE_AREA_WIDTH) // zoomed in (or 1:1) { - // 1:1 or zoomed in - for (int16_t x = 1; x < SAMPLE_AREA_WIDTH; x++) + for (int32_t x = 0; x <= SAMPLE_AREA_WIDTH; x++) { - y2 = SAMPLE_AREA_Y_CENTER - getScaledSample(s, scr2SmpPos(x)); - sampleLine(x - 1, x, y1, y2); - y1 = y2; + int32_t currSmpPos = scr2SmpPos(x+0); + int32_t nextSmpPos = scr2SmpPos(x+1); + + if (currSmpPos >= s->length) currSmpPos = s->length-1; + if (nextSmpPos >= s->length) nextSmpPos = s->length-1; + + int32_t x1 = smpPos2Scr(currSmpPos); + int32_t x2 = smpPos2Scr(nextSmpPos); + int32_t y1 = getScaledSample(s, currSmpPos); + int32_t y2 = getScaledSample(s, nextSmpPos); + + x1 = CLAMP(x1, 0, SAMPLE_AREA_WIDTH-1); + x2 = CLAMP(x2, 0, SAMPLE_AREA_WIDTH-1); + + // kludge: sometimes the last point wouldn't reach the end of the sample window + if (x == SAMPLE_AREA_WIDTH) + x2 = SAMPLE_AREA_WIDTH-1; + + sampleLine(x1, x2, y1, y2); } } - else + else // zoomed out { - // zoomed out + const int32_t firstSamplePoint = getScaledSample(s, scr2SmpPos(0)); - int16_t oldMin = y1; - int16_t oldMax = y1; + int32_t oldMin = firstSamplePoint; + int32_t oldMax = firstSamplePoint; - int32_t smpNumMin = (s->typ & 16) ? 2 : 1; for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) { - int32_t smpIdx = scr2SmpPos(x); + int32_t smpIdx = scr2SmpPos(x+0); int32_t smpNum = scr2SmpPos(x+1) - smpIdx; // prevent look-up overflow (yes, this can happen near the end of the sample) - if (smpIdx+smpNum > s->len) - smpNum = s->len - smpNum; - - if (smpNum < smpNumMin) - smpNum = smpNumMin; + if (smpIdx+smpNum > s->length) + smpNum = s->length - smpIdx; - getSampleDataPeak(s, smpIdx, smpNum, &min, &max); - - if (x != 0) + if (smpNum > 0) { - if (min > oldMax) sampleLine(x - 1, x, oldMax, min); - if (max < oldMin) sampleLine(x - 1, x, oldMin, max); - } + int16_t min, max; + getSampleDataPeak(s, smpIdx, smpNum, &min, &max); - sampleLine(x, x, max, min); + if (x != 0) + { + if (min > oldMax) sampleLine(x-1, x, oldMax, min); + if (max < oldMin) sampleLine(x-1, x, oldMin, max); + } + + sampleLine(x, x, max, min); - oldMin = min; - oldMax = max; + oldMin = min; + oldMax = max; + } } } } @@ -1312,17 +1300,17 @@ static void writeWaveform(void) void writeSample(bool forceSmpRedraw) { int32_t tmpRx1, tmpRx2; - sampleTyp *s; + sample_t *s; // update sample loop points for visuals if (instr[editor.curInstr] == NULL) - s = &instr[0]->samp[0]; + s = &instr[0]->smp[0]; else - s = &instr[editor.curInstr]->samp[editor.curSmp]; + s = &instr[editor.curInstr]->smp[editor.curSmp]; - curSmpRepS = s->repS; - curSmpRepL = s->repL; + curSmpLoopStart = s->loopStart; + curSmpLoopLength = s->loopLength; // exchange range variables if x1 is after x2 if (smpEd_Rx1 > smpEd_Rx2) @@ -1333,13 +1321,13 @@ void writeSample(bool forceSmpRedraw) } // clamp range - smpEd_Rx1 = CLAMP(smpEd_Rx1, 0, s->len); - smpEd_Rx2 = CLAMP(smpEd_Rx2, 0, s->len); + smpEd_Rx1 = CLAMP(smpEd_Rx1, 0, s->length); + smpEd_Rx2 = CLAMP(smpEd_Rx2, 0, s->length); // sanitize sample scroll position - if (smpEd_ScrPos+smpEd_ViewSize > s->len) + if (smpEd_ScrPos+smpEd_ViewSize > s->length) { - smpEd_ScrPos = s->len - smpEd_ViewSize; + smpEd_ScrPos = s->length - smpEd_ViewSize; updateScrPos(); } @@ -1348,9 +1336,9 @@ void writeSample(bool forceSmpRedraw) smpEd_ScrPos = 0; updateScrPos(); - if (smpEd_ViewSize > s->len) + if (smpEd_ViewSize > s->length) { - smpEd_ViewSize = s->len; + smpEd_ViewSize = s->length; updateViewSize(); } } @@ -1400,11 +1388,11 @@ void writeSample(bool forceSmpRedraw) old_Rx2 = smpEd_Rx2; } - fixRepeatGadgets(); + fixLoopGadgets(); } if (ui.sampleEditorShown) - fixSampleDrag(); + fixSampleScrollbar(); updateSampleEditor(); } @@ -1418,8 +1406,6 @@ static void setSampleRange(int32_t start, int32_t end) return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (start < 0) start = 0; @@ -1428,13 +1414,6 @@ static void setSampleRange(int32_t start, int32_t end) smpEd_Rx1 = scr2SmpPos(start); smpEd_Rx2 = scr2SmpPos(end); - - // 2-byte align if sample is 16-bit - if (s->typ & 16) - { - smpEd_Rx1 &= 0xFFFFFFFE; - smpEd_Rx2 &= 0xFFFFFFFE; - } } void updateSampleEditorSample(void) @@ -1448,7 +1427,7 @@ void updateSampleEditorSample(void) if (instr[editor.curInstr] == NULL) smpEd_ViewSize = 0; else - smpEd_ViewSize = instr[editor.curInstr]->samp[editor.curSmp].len; + smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length; updateViewSize(); @@ -1458,26 +1437,26 @@ void updateSampleEditorSample(void) void updateSampleEditor(void) { char noteChar1, noteChar2; - uint8_t typ; - int32_t sampleLen; + uint8_t flags; + int32_t sampleLength; if (!ui.sampleEditorShown) return; if (instr[editor.curInstr] == NULL) { - typ = 0; - sampleLen = 0; + flags = 0; + sampleLength = 0; } else { - typ = instr[editor.curInstr]->samp[editor.curSmp].typ; - sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len; + flags = instr[editor.curInstr]->smp[editor.curSmp].flags; + sampleLength = instr[editor.curInstr]->smp[editor.curSmp].length; } // sample bit depth radio buttons uncheckRadioButtonGroup(RB_GROUP_SAMPLE_DEPTH); - if (typ & 16) + if (flags & SAMPLE_16BIT) radioButtons[RB_SAMPLE_16BIT].state = RADIOBUTTON_CHECKED; else radioButtons[RB_SAMPLE_8BIT].state = RADIOBUTTON_CHECKED; @@ -1485,17 +1464,15 @@ void updateSampleEditor(void) // sample loop radio buttons uncheckRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); - if (typ & 3) - { - if (typ & 2) - radioButtons[RB_SAMPLE_PINGPONG_LOOP].state = RADIOBUTTON_CHECKED; - else - radioButtons[RB_SAMPLE_FORWARD_LOOP].state = RADIOBUTTON_CHECKED; - } - else - { + + uint8_t loopType = GET_LOOPTYPE(flags); + if (loopType == LOOP_OFF) radioButtons[RB_SAMPLE_NO_LOOP].state = RADIOBUTTON_CHECKED; - } + else if (loopType == LOOP_FWD) + radioButtons[RB_SAMPLE_FORWARD_LOOP].state = RADIOBUTTON_CHECKED; + else + radioButtons[RB_SAMPLE_PINGPONG_LOOP].state = RADIOBUTTON_CHECKED; + showRadioButtonGroup(RB_GROUP_SAMPLE_LOOP); // draw sample play note @@ -1521,7 +1498,7 @@ void updateSampleEditor(void) // draw sample display/length hexOutBg(536, 350, PAL_FORGRND, PAL_DESKTOP, smpEd_ViewSize, 8); - hexOutBg(536, 362, PAL_FORGRND, PAL_DESKTOP, sampleLen, 8); + hexOutBg(536, 362, PAL_FORGRND, PAL_DESKTOP, sampleLength, 8); } void sampPlayNoteUp(void) @@ -1549,15 +1526,15 @@ void scrollSampleDataLeft(void) if (instr[editor.curInstr] == NULL) sampleLen = 0; else - sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len; + sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) return; if (mouse.rightButtonPressed) - scrollAmount = smpEd_ViewSize / 14; // rounded from 16 (70Hz) + scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(16); else - scrollAmount = smpEd_ViewSize / 27; // rounded from 32 (70Hz) + scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(32); if (scrollAmount < 1) scrollAmount = 1; @@ -1576,15 +1553,15 @@ void scrollSampleDataRight(void) if (instr[editor.curInstr] == NULL) sampleLen = 0; else - sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len; + sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) return; if (mouse.rightButtonPressed) - scrollAmount = smpEd_ViewSize / 14; // was 16 (70Hz->60Hz) + scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(16); else - scrollAmount = smpEd_ViewSize / 27; // was 32 (70Hz->60Hz) + scrollAmount = smpEd_ViewSize / SCALE_VBLANK_DELTA(32); if (scrollAmount < 1) scrollAmount = 1; @@ -1603,12 +1580,12 @@ void scrollSampleData(uint32_t pos) if (instr[editor.curInstr] == NULL) sampleLen = 0; else - sampleLen = instr[editor.curInstr]->samp[editor.curSmp].len; + sampleLen = instr[editor.curInstr]->smp[editor.curSmp].length; if (smpEd_ViewSize == 0 || smpEd_ViewSize == sampleLen) return; - smpEd_ScrPos = (int32_t)pos; + smpEd_ScrPos = pos; updateScrPos(); } @@ -1632,23 +1609,15 @@ void showRange(void) if (editor.curInstr == 0 || instr[editor.curInstr] == NULL) return; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (s->pek == NULL) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (s->dataPtr == NULL) return; if (smpEd_Rx1 < smpEd_Rx2) { smpEd_ViewSize = smpEd_Rx2 - smpEd_Rx1; - - if (s->typ & 16) - { - if (smpEd_ViewSize < 4) - smpEd_ViewSize = 4; - } - else if (smpEd_ViewSize < 2) - { + if (smpEd_ViewSize < 2) smpEd_ViewSize = 2; - } updateViewSize(); @@ -1665,7 +1634,7 @@ void rangeAll(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -1678,23 +1647,22 @@ static void zoomSampleDataIn(int32_t step, int32_t x) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; - int32_t minViewSize = (s->typ & 16) ? 4 : 2; - if (old_ViewSize <= minViewSize) + if (old_ViewSize <= 2) return; if (step < 1) step = 1; smpEd_ViewSize = old_ViewSize - (step * 2); - if (smpEd_ViewSize < minViewSize) - smpEd_ViewSize = minViewSize; + if (smpEd_ViewSize < 2) + smpEd_ViewSize = 2; updateViewSize(); @@ -1705,10 +1673,10 @@ static void zoomSampleDataIn(int32_t step, int32_t x) step += tmp32; int64_t newScrPos64 = old_SmpScrPos + step; - if (newScrPos64+smpEd_ViewSize > s->len) - newScrPos64 = s->len - smpEd_ViewSize; + if (newScrPos64+smpEd_ViewSize > s->length) + newScrPos64 = s->length - smpEd_ViewSize; - smpEd_ScrPos = newScrPos64 & 0xFFFFFFFF; + smpEd_ScrPos = (uint32_t)newScrPos64; updateScrPos(); } @@ -1716,22 +1684,22 @@ static void zoomSampleDataOut(int32_t step, int32_t x) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (old_ViewSize == s->len) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (old_ViewSize == s->length) return; if (step < 1) step = 1; int64_t newViewSize64 = (int64_t)old_ViewSize + (step * 2); - if (newViewSize64 > s->len) + if (newViewSize64 > s->length) { - smpEd_ViewSize = s->len; + smpEd_ViewSize = s->length; smpEd_ScrPos = 0; } else @@ -1748,8 +1716,8 @@ static void zoomSampleDataOut(int32_t step, int32_t x) if (smpEd_ScrPos < 0) smpEd_ScrPos = 0; - if ((smpEd_ScrPos + smpEd_ViewSize) > s->len) - smpEd_ScrPos = s->len - smpEd_ViewSize; + if (smpEd_ScrPos+smpEd_ViewSize > s->length) + smpEd_ScrPos = s->length - smpEd_ViewSize; } updateViewSize(); @@ -1760,7 +1728,7 @@ void mouseZoomSampleDataIn(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -1772,7 +1740,7 @@ void mouseZoomSampleDataOut(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -1784,13 +1752,13 @@ void zoomOut(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - if (old_ViewSize == s->len) + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + if (old_ViewSize == s->length) return; int32_t tmp32 = old_ViewSize; @@ -1804,12 +1772,12 @@ void zoomOut(void) smpEd_ViewSize = old_ViewSize * 2; if (smpEd_ViewSize < old_ViewSize) { - smpEd_ViewSize = s->len; + smpEd_ViewSize = s->length; smpEd_ScrPos = 0; } - else if (smpEd_ViewSize+smpEd_ScrPos > s->len) + else if (smpEd_ViewSize+smpEd_ScrPos > s->length) { - smpEd_ViewSize = s->len - smpEd_ScrPos; + smpEd_ViewSize = s->length - smpEd_ScrPos; } updateViewSize(); @@ -1820,7 +1788,7 @@ void showAll(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -1828,7 +1796,7 @@ void showAll(void) smpEd_ScrPos = 0; updateScrPos(); - smpEd_ViewSize = instr[editor.curInstr]->samp[editor.curSmp].len; + smpEd_ViewSize = instr[editor.curInstr]->smp[editor.curSmp].length; updateViewSize(); } @@ -1836,12 +1804,12 @@ void saveRange(void) { if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } - if (smpEd_Rx1 >= smpEd_Rx2) + if (smpEd_Rx1 == smpEd_Rx2) { okBox(0, "System message", "No range specified!"); return; @@ -1879,33 +1847,41 @@ void saveRange(void) UNICHAR *filenameU = cp437ToUnichar(smpEd_SysReqText); if (filenameU == NULL) { - okBox(0, "System message", "Error converting string locale!"); + okBox(0, "System message", "Out of memory!"); return; } + if (fileExistsAnsi(smpEd_SysReqText)) + { + char buf[256]; + createFileOverwriteText(smpEd_SysReqText, buf); + if (okBox(2, "System request", buf) != 1) + return; + } + saveSample(filenameU, SAVE_RANGE); free(filenameU); } static bool cutRange(bool cropMode, int32_t r1, int32_t r2) { - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); if (s == NULL) return false; - assert(!(s->typ & 16) || (!(r1 & 1) && !(r2 & 1) && !(s->len & 1))); + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); if (!cropMode) { - if (editor.curInstr == 0 || s->pek == NULL || s->len == 0) + if (editor.curInstr == 0 || s->dataPtr == NULL || s->length == 0) return false; pauseAudio(); - restoreSample(s); + unfixSample(s); if (config.smpCutToBuffer) { - if (!getCopyBuffer(r2 - r1)) + if (!getCopyBuffer(r2-r1, sample16Bit)) { fixSample(s); resumeAudio(); @@ -1914,18 +1890,17 @@ static bool cutRange(bool cropMode, int32_t r1, int32_t r2) return false; } - memcpy(smpCopyBuff, &s->pek[r1], r2 - r1); - smpCopyBits = (s->typ & 16) ? 16 : 8; + memcpy(smpCopyBuff, &s->dataPtr[r1], (r2-r1) << sample16Bit); + smpCopyBits = sample16Bit ? 16 : 8; } } - memmove(&s->pek[r1], &s->pek[r2], s->len - r2); + memmove(&s->dataPtr[r1 << sample16Bit], &s->dataPtr[r2 << sample16Bit], (s->length-r2) << sample16Bit); - int32_t len = s->len - r2 + r1; - if (len > 0) + int32_t length = s->length - r2+r1; + if (length > 0) { - int8_t *newPtr = (int8_t *)realloc(s->origPek, len + LOOP_FIX_LEN); - if (newPtr == NULL) + if (!reallocateSmpData(s, length, sample16Bit)) { freeSample(editor.curInstr, editor.curSmp); editor.updateCurSmp = true; @@ -1937,44 +1912,34 @@ static bool cutRange(bool cropMode, int32_t r1, int32_t r2) return false; } - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; + s->length = length; - s->len = len; - - int32_t repE = s->repS + s->repL; - if (s->repS > r1) + int32_t loopEnd = s->loopStart + s->loopLength; + if (s->loopStart > r1) { - s->repS -= r2 - r1; - if (s->repS < r1) - s->repS = r1; + s->loopStart -= r2-r1; + if (s->loopStart < r1) + s->loopStart = r1; } - if (repE > r1) + if (loopEnd > r1) { - repE -= r2 - r1; - if (repE < r1) - repE = r1; + loopEnd -= r2-r1; + if (loopEnd < r1) + loopEnd = r1; } - s->repL = repE - s->repS; - if (s->repL < 0) - s->repL = 0; - - if (s->repS+s->repL > len) - s->repL = len - s->repS; + s->loopLength = loopEnd - s->loopStart; + if (s->loopLength < 0) + s->loopLength = 0; - // 2-byte align loop points if sample is 16-bit - if (s->typ & 16) - { - s->repL &= 0xFFFFFFFE; - s->repS &= 0xFFFFFFFE; - } + if (s->loopStart+s->loopLength > length) + s->loopLength = length - s->loopStart; - if (s->repL == 0) + if (s->loopLength <= 0) { - s->repS = 0; - s->typ &= ~3; // disable loop + s->loopStart = 0; + DISABLE_LOOP(s->flags); } if (!cropMode) @@ -2014,8 +1979,8 @@ static int32_t SDLCALL sampCutThread(void *ptr) void sampCut(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) return; mouseAnimOn(); @@ -2031,22 +1996,30 @@ void sampCut(void) static int32_t SDLCALL sampCopyThread(void *ptr) { - sampleTyp *s = getCurSample(); - assert(s != NULL && (!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1)))); + sample_t *s = getCurSample(); + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - if (!getCopyBuffer(smpEd_Rx2 - smpEd_Rx1)) + if (!getCopyBuffer(smpEd_Rx2- smpEd_Rx1, sample16Bit)) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return true; } - restoreSample(s); - memcpy(smpCopyBuff, &s->pek[smpEd_Rx1], smpEd_Rx2 - smpEd_Rx1); + unfixSample(s); + memcpy(smpCopyBuff, &s->dataPtr[smpEd_Rx1 << sample16Bit], (smpEd_Rx2-smpEd_Rx1) << sample16Bit); fixSample(s); - smpCopyBits = (s->typ & 16) ? 16 : 8; setMouseBusy(false); + // copy sample information (in case we paste over an empty sample) + if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length) + { + smpCopySample = *s; + smpCopyDidCopyWholeSample = true; + } + + smpCopyBits = sample16Bit? 16 : 8; return true; (void)ptr; @@ -2054,8 +2027,8 @@ static int32_t SDLCALL sampCopyThread(void *ptr) void sampCopy(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->origPek == NULL || s->len <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx2 == 0 || smpEd_Rx2 < smpEd_Rx1) return; mouseAnimOn(); @@ -2069,10 +2042,11 @@ void sampCopy(void) SDL_DetachThread(thread); } -static void pasteOverwrite(sampleTyp *s) +static void pasteOverwrite(sample_t *s) { - int8_t *p = (int8_t *)malloc(smpCopySize + LOOP_FIX_LEN); - if (p == NULL) + bool sample16Bit = (smpCopyBits == 16); + + if (!reallocateSmpData(s, smpCopySize, sample16Bit)) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return; @@ -2080,20 +2054,35 @@ static void pasteOverwrite(sampleTyp *s) pauseAudio(); - if (s->origPek != NULL) - free(s->origPek); - - memset(s, 0, sizeof (sampleTyp)); + memcpy(s->dataPtr, smpCopyBuff, smpCopySize << sample16Bit); - s->origPek = p; - s->pek = p + SMP_DAT_OFFSET; - - memcpy(s->pek, smpCopyBuff, smpCopySize); + if (smpCopyDidCopyWholeSample) + { + sample_t *src = &smpCopySample; + memcpy(s->name, src->name, 23); + s->length = src->length; + s->loopStart = src->loopStart; + s->loopLength = src->loopLength; + s->volume = src->volume; + s->panning = src->panning; + s->finetune = src->finetune; + s->relativeNote = src->relativeNote; + s->flags = src->flags; + } + else + { + s->name[0] = '\0'; + s->length = smpCopySize; + s->loopStart = 0; + s->loopLength = 0; + s->volume = 64; + s->panning = 128; + s->finetune = 0; + s->relativeNote = 0; + s->flags = (smpCopyBits == 16) ? SAMPLE_16BIT : 0; + } - s->len = smpCopySize; - s->vol = 64; - s->pan = 128; - s->typ = (smpCopyBits == 16) ? 16 : 0; + s->isFixed = false; fixSample(s); resumeAudio(); @@ -2103,40 +2092,34 @@ static void pasteOverwrite(sampleTyp *s) setMouseBusy(false); } -static void pasteCopiedData(int8_t *pek, int32_t offset, int32_t length, bool smpIs16Bit) +static void pasteCopiedData(int8_t *dataPtr, int32_t offset, int32_t length, bool sample16Bit) { - if (smpIs16Bit) + if (sample16Bit) // destination sample is 16-bits { - // destination sample = 16-bit - if (smpCopyBits == 16) { - // src/dst = equal bits, copy directly - memcpy(&pek[offset], smpCopyBuff, length); + // src/dst bits are equal, do direct copy + memcpy(&dataPtr[offset<<1], smpCopyBuff, length * sizeof (int16_t)); } else { - // convert copy data to 16-bit then paste - int16_t *ptr16 = (int16_t *)&pek[offset]; - int32_t len32 = length >> 1; - - for (int32_t i = 0; i < len32; i++) + // convert copied data to 16-bit then paste + int16_t *ptr16 = (int16_t *)dataPtr + offset; + for (int32_t i = 0; i < length; i++) ptr16[i] = smpCopyBuff[i] << 8; } } - else + else // destination sample is 8-bits { - // destination sample = 8-bit - if (smpCopyBits == 8) { - // src/dst = equal bits, copy directly - memcpy(&pek[offset], smpCopyBuff, length); + // src/dst bits are equal, do direct copy + memcpy(&dataPtr[offset], smpCopyBuff, length * sizeof (int8_t)); } else { - // convert copy data to 8-bit then paste - int8_t *ptr8 = (int8_t *)&pek[offset]; + // convert copied data to 8-bit then paste + int8_t *ptr8 = (int8_t *)&dataPtr[offset]; int16_t *ptr16 = (int16_t *)smpCopyBuff; for (int32_t i = 0; i < length; i++) @@ -2147,46 +2130,30 @@ static void pasteCopiedData(int8_t *pek, int32_t offset, int32_t length, bool sm static int32_t SDLCALL sampPasteThread(void *ptr) { + smpPtr_t sp; + if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return true; } - sampleTyp *s = getCurSample(); - if (smpEd_Rx2 == 0 || s == NULL || s->pek == NULL) + sample_t *s = getCurSample(); + if (smpEd_Rx2 == 0 || s == NULL || s->dataPtr == NULL) { pasteOverwrite(s); return true; } - bool smpIs16Bit = (s->typ >> 4) & 1; - assert(!smpIs16Bit || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1))); + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - if (s->len+smpCopySize > MAX_SAMPLE_LEN) + if (s->length+smpCopySize > MAX_SAMPLE_LEN) { okBoxThreadSafe(0, "System message", "Not enough room in sample!"); return true; } - int32_t realCopyLen = smpCopySize; - - if (smpIs16Bit) - { - // destination sample is 16-bit - - if (smpCopyBits == 8) // copy buffer is 8-bit, multiply length by 2 - realCopyLen <<= 1; - } - else - { - // destination sample is 8-bit - - if (smpCopyBits == 16) // copy buffer is 16-bit, divide length by 2 - realCopyLen >>= 1; - } - - int32_t newLength = s->len + realCopyLen - (smpEd_Rx2 - smpEd_Rx1); + int32_t newLength = s->length + smpCopySize - (smpEd_Rx2 - smpEd_Rx1); if (newLength <= 0) return true; @@ -2196,65 +2163,54 @@ static int32_t SDLCALL sampPasteThread(void *ptr) return true; } - int8_t *p = (int8_t *)malloc(newLength + LOOP_FIX_LEN); - if (p == NULL) + if (!allocateSmpDataPtr(&sp, newLength, sample16Bit)) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return true; } - int8_t *newPek = p + SMP_DAT_OFFSET; - pauseAudio(); - restoreSample(s); + unfixSample(s); // paste left part of original sample if (smpEd_Rx1 > 0) - memcpy(newPek, s->pek, smpEd_Rx1); + memcpy(sp.ptr, s->dataPtr, smpEd_Rx1 << sample16Bit); // paste copied data - pasteCopiedData(newPek, smpEd_Rx1, realCopyLen, smpIs16Bit); + pasteCopiedData(sp.ptr, smpEd_Rx1, smpCopySize, sample16Bit); // paste right part of original sample - if (smpEd_Rx2 < s->len) - memmove(&newPek[smpEd_Rx1+realCopyLen], &s->pek[smpEd_Rx2], s->len - smpEd_Rx2); + if (smpEd_Rx2 < s->length) + memmove(&sp.ptr[(smpEd_Rx1+smpCopySize) << sample16Bit], &s->dataPtr[smpEd_Rx2 << sample16Bit], (s->length-smpEd_Rx2) << sample16Bit); - free(s->origPek); + freeSmpData(s); + setSmpDataPtr(s, &sp); // adjust loop points if necessary - if (smpEd_Rx2-smpEd_Rx1 != realCopyLen) + if (smpEd_Rx2-smpEd_Rx1 != smpCopySize) { - int32_t loopAdjust = realCopyLen - (smpEd_Rx1 - smpEd_Rx2); + int32_t loopAdjust = smpCopySize - (smpEd_Rx1 - smpEd_Rx2); - if (s->repS > smpEd_Rx2) + if (s->loopStart > smpEd_Rx2) { - s->repS += loopAdjust; - s->repL -= loopAdjust; + s->loopStart += loopAdjust; + s->loopLength -= loopAdjust; } - if (s->repS+s->repL > smpEd_Rx2) - s->repL += loopAdjust; + if (s->loopStart+s->loopLength > smpEd_Rx2) + s->loopLength += loopAdjust; - if (s->repS > newLength) + if (s->loopStart > newLength) { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; } - if (s->repS+s->repL > newLength) - s->repL = newLength - s->repS; - - // align loop points if sample is 16-bit - if (smpIs16Bit) - { - s->repL &= 0xFFFFFFFE; - s->repS &= 0xFFFFFFFE; - } + if (s->loopStart+s->loopLength > newLength) + s->loopLength = newLength - s->loopStart; } - s->len = newLength; - s->origPek = p; - s->pek = s->origPek + SMP_DAT_OFFSET; + s->length = newLength; fixSample(s); resumeAudio(); @@ -2263,14 +2219,7 @@ static int32_t SDLCALL sampPasteThread(void *ptr) setMouseBusy(false); // set new range - smpEd_Rx2 = smpEd_Rx1 + realCopyLen; - - // align sample marking points if sample is 16-bit - if (smpIs16Bit) - { - smpEd_Rx1 &= 0xFFFFFFFE; - smpEd_Rx2 &= 0xFFFFFFFE; - } + smpEd_Rx2 = smpEd_Rx1 + smpCopySize; writeSampleFlag = true; return true; @@ -2285,8 +2234,8 @@ void sampPaste(void) if (smpEd_Rx2 == 0) // no sample data marked, overwrite sample with copy buffer { - sampleTyp *s = getCurSample(); - if (s != NULL && s->pek != NULL) + sample_t *s = getCurSample(); + if (s != NULL && s->dataPtr != NULL && s->length > 0) { if (okBox(2, "System request", "The current sample is not empty. Do you really want to overwrite it?") != 1) return; @@ -2306,16 +2255,15 @@ void sampPaste(void) static int32_t SDLCALL sampCropThread(void *ptr) { - sampleTyp *s = getCurSample(); - assert(!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1))); + sample_t *s = getCurSample(); int32_t r1 = smpEd_Rx1; int32_t r2 = smpEd_Rx2; pauseAudio(); - restoreSample(s); + unfixSample(s); - if (!cutRange(true, 0, r1) || !cutRange(true, r2 - r1, s->len)) + if (!cutRange(true, 0, r1) || !cutRange(true, r2-r1, s->length)) { fixSample(s); resumeAudio(); @@ -2326,18 +2274,15 @@ static int32_t SDLCALL sampCropThread(void *ptr) resumeAudio(); r1 = 0; - r2 = s->len; - - if (s->typ & 16) - r2 &= 0xFFFFFFFE; + r2 = s->length; setSongModifiedFlag(); setMouseBusy(false); smpEd_Rx1 = r1; smpEd_Rx2 = r2; - writeSampleFlag = true; + writeSampleFlag = true; return true; (void)ptr; @@ -2345,12 +2290,12 @@ static int32_t SDLCALL sampCropThread(void *ptr) void sampCrop(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0 || smpEd_Rx1 >= smpEd_Rx2) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0 || smpEd_Rx1 == smpEd_Rx2) return; - if (smpEd_Rx1 == 0 && smpEd_Rx2 >= s->len) - return; // no need to crop (the whole sample is marked) + if (smpEd_Rx1 == 0 && smpEd_Rx2 == s->length) + return; // nothing to crop (the whole sample is marked) mouseAnimOn(); thread = SDL_CreateThread(sampCropThread, NULL, NULL); @@ -2365,20 +2310,14 @@ void sampCrop(void) void sampXFade(void) { - int16_t c, d; - int32_t tmp32, i, y1, y2, a, b, d1, d2, d3, dist; - double dR, dS1, dS2, dS3, dS4; + int32_t y1, y2, d1, d2, d3; - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - assert(!(s->typ & 16) || (!(smpEd_Rx1 & 1) && !(smpEd_Rx2 & 1) && !(s->len & 1))); - - uint8_t t = s->typ; - // check if the sample has the loop flag enabled - if ((t & 3) == 0) + if (GET_LOOPTYPE(s->flags) == LOOP_OFF) { okBox(0, "System message", "X-Fade can only be used on a loop-enabled sample!"); return; @@ -2401,20 +2340,16 @@ void sampXFade(void) int32_t x1 = smpEd_Rx1; int32_t x2 = smpEd_Rx2; - bool is16Bit = (t & 16) ? true : false; + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - if ((t & 3) >= 2) + if (GET_LOOPTYPE(s->flags) == LOOP_BIDI) { - // pingpong loop - - y1 = s->repS; - if (x1 <= y1) + y1 = s->loopStart; + if (x1 <= y1) // first loop point { - // first loop point - - if (x2 <= y1 || x2 >= s->repS+s->repL) + if (x2 <= y1 || x2 >= s->loopStart+s->loopLength) { - okBox(0, "System message", "Invalid range!"); + okBox(0, "System message", "Error: No loop point found inside marked data."); return; } @@ -2425,61 +2360,59 @@ void sampXFade(void) d2 = y1 - x1; d3 = x2 - y1; - if (d1 < 2 || d2 < 2 || d3 < 2) + if (d1 < 1 || d2 < 1 || d3 < 1) { - okBox(0, "System message", "Invalid range!"); + okBox(0, "System message", "Invalid range! Try to mark more data."); return; } - if (y1-d1 < 0 || y1+d1 >= s->len) + if (y1-d1 < 0 || y1+d1 >= s->length) { okBox(0, "System message", "Not enough sample data outside loop!"); return; } - if (is16Bit) - { - y1 >>= 1; - d1 >>= 1; - d2 >>= 1; - d3 >>= 1; - } + const double dD2Mul = 1.0 / d2; + const double dD3Mul = 1.0 / d3; pauseAudio(); - restoreSample(s); + unfixSample(s); - i = 0; - while (i < d1) + for (int32_t i = 0; i < d1; i++) { - a = getSampleValue(s->pek, t, (y1 - i - 1) << is16Bit); - b = getSampleValue(s->pek, t, (y1 + i) << is16Bit); + const int32_t aIdx = y1-i-1; + const int32_t bIdx = y1+i; + const double dI = i; - dS1 = 1.0 - i / (double)d2; dS2 = 2.0 - dS1; - dS3 = 1.0 - i / (double)d3; dS4 = 2.0 - dS3; + const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); + const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); - tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2)); - c = (int16_t)tmp32; - - tmp32 = (int32_t)round((b * dS4 + a * dS3) / (dS3 + dS4)); - d = (int16_t)tmp32; - - if (i < d2) putSampleValue(s->pek, t, (y1 - i - 1) << is16Bit, c); - if (i < d3) putSampleValue(s->pek, t, (y1 + i) << is16Bit, d); + if (i < d2) + { + const double dS1 = 1.0 - (dI * dD2Mul); + const double dS2 = 2.0 - dS1; + double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2); + putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit); + } - i++; + if (i < d3) + { + const double dS1 = 1.0 - (dI * dD3Mul); + const double dS2 = 2.0 - dS1; + double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2); + putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit); + } } fixSample(s); resumeAudio(); } - else + else // last loop point { - // last loop point - - y1 += s->repL; - if (x1 >= y1 || x2 <= y1 || x2 >= s->len) + y1 += s->loopLength; + if (x1 >= y1 || x2 <= y1 || x2 >= s->length) { - okBox(0, "System message", "Invalid range!"); + okBox(0, "System message", "Error: No loop point found inside marked data."); return; } @@ -2490,138 +2423,130 @@ void sampXFade(void) d2 = y1 - x1; d3 = x2 - y1; - if (d1 < 2 || d2 < 2 || d3 < 2) + if (d1 < 1 || d2 < 1 || d3 < 1) { - okBox(0, "System message", "Invalid range!"); + okBox(0, "System message", "Invalid range! Try to mark more data."); return; } - if (y1-d1 < 0 || y1+d1 >= s->len) + if (y1-d1 < 0 || y1+d1 >= s->length) { okBox(0, "System message", "Not enough sample data outside loop!"); return; } - if (is16Bit) - { - y1 >>= 1; - d1 >>= 1; - d2 >>= 1; - d3 >>= 1; - } + const double dD2Mul = 1.0 / d2; + const double dD3Mul = 1.0 / d3; pauseAudio(); - restoreSample(s); + unfixSample(s); - i = 0; - while (i < d1) + for (int32_t i = 0; i < d1; i++) { - a = getSampleValue(s->pek, t, (y1 - i - 1) << is16Bit); - b = getSampleValue(s->pek, t, (y1 + i) << is16Bit); + const int32_t aIdx = y1-i-1; + const int32_t bIdx = y1+i; + const double dI = i; - dS1 = 1.0 - i / (double)d2; dS2 = 2.0 - dS1; - dS3 = 1.0 - i / (double)d3; dS4 = 2.0 - dS3; + const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); + const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); - tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2)); - c = (int16_t)tmp32; - - tmp32 = (int32_t)round((b * dS4 + a * dS3) / (dS3 + dS4)); - d = (int16_t)tmp32; - - if (i < d2) putSampleValue(s->pek, t, (y1 - i - 1) << is16Bit, c); - if (i < d3) putSampleValue(s->pek, t, (y1 + i) << is16Bit, d); + if (i < d2) + { + const double dS1 = 1.0 - (dI * dD2Mul); + const double dS2 = 2.0 - dS1; + double dSample = (dA * dS2 + dB * dS1) / (dS1 + dS2); + putSampleValue(s->dataPtr, aIdx, dSample, sample16Bit); + } - i++; + if (i < d3) + { + const double dS1 = 1.0 - (dI * dD3Mul); + const double dS2 = 2.0 - dS1; + double dSample = (dB * dS2 + dA * dS1) / (dS1 + dS2); + putSampleValue(s->dataPtr, bIdx, dSample, sample16Bit); + } } fixSample(s); resumeAudio(); } } - else + else // forward loop { - // standard loop - - if (x1 > s->repS) + if (x1 > s->loopStart) { - x1 -= s->repL; - x2 -= s->repL; + x1 -= s->loopLength; + x2 -= s->loopLength; } - if (x1 < 0 || x2 <= x1 || x2 >= s->len) + if (x1 < 0 || x2 <= x1 || x2 >= s->length) { okBox(0, "System message", "Invalid range!"); return; } - i = (x2 - x1 + 1) >> 1; - y1 = s->repS - i; - y2 = s->repS + s->repL - i; + const int32_t length = x2 - x1; - if (t & 16) - { - y1 &= 0xFFFFFFFE; - y2 &= 0xFFFFFFFE; - } + int32_t x = (length + 1) >> 1; + y1 = s->loopStart - x; + y2 = s->loopStart+s->loopLength - x; - if (y1 < 0 || y2+(x2-x1) >= s->len) + if (y1 < 0 || y2+length >= s->length) { okBox(0, "System message", "Not enough sample data outside loop!"); return; } - d1 = x2 - x1; - d2 = s->repS - y1; - d3 = x2 - x1 - d2; + d1 = length; + d2 = s->loopStart - y1; + d3 = length - d2; - if (y1+(x2-x1) <= s->repS || d1 == 0 || d3 == 0 || d1 > s->repL) + if (y1+length <= s->loopStart || d1 == 0 || d3 == 0 || d1 > s->loopLength) { okBox(0, "System message", "Invalid range!"); return; } - dR = (s->repS - i) / (double)(x2 - x1); - dist = is16Bit ? 2 : 1; + const double dR = (s->loopStart - x) / (double)length; + const double dD1 = d1; + const double dD1Mul = 1.0 / d1; + const double dD2Mul = 1.0 / d2; + const double dD3Mul = 1.0 / d3; pauseAudio(); - restoreSample(s); + unfixSample(s); - i = 0; - while (i < x2-x1) + for (int32_t i = 0; i < length; i++) { - a = getSampleValue(s->pek, t, y1 + i); - b = getSampleValue(s->pek, t, y2 + i); + const int32_t aIdx = y1+i; + const int32_t bIdx = y2+i; + const double dI = i; - dS2 = i / (double)d1; - dS1 = 1.0 - dS2; + const double dA = getSampleValue(s->dataPtr, aIdx, sample16Bit); + const double dB = getSampleValue(s->dataPtr, bIdx, sample16Bit); + const double dS2 = dI * dD1Mul; + const double dS1 = 1.0 - dS2; - if (y1+i < s->repS) + double dC, dD; + if (y1+i < s->loopStart) { - dS3 = 1.0 - (1.0 - dR) * i / d2; - dS4 = dR * i / d2; - - tmp32 = (int32_t)round((a * dS3 + b * dS4) / (dS3 + dS4)); - c = (int16_t)tmp32; - - tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2)); - d = (int16_t)tmp32; + const double dS3 = 1.0 - (1.0 - dR) * dI * dD2Mul; + const double dS4 = dR * dI * dD2Mul; + + dC = (dA * dS3 + dB * dS4) / (dS3 + dS4); + dD = (dA * dS2 + dB * dS1) / (dS1 + dS2); } else { - dS3 = 1.0 - (1.0 - dR) * (d1 - i) / d3; - dS4 = dR * (d1 - i) / d3; - - tmp32 = (int32_t)round((a * dS2 + b * dS1) / (dS1 + dS2)); - c = (int16_t)tmp32; + const double dS3 = 1.0 - (1.0 - dR) * (dD1 - dI) * dD3Mul; + const double dS4 = dR * (dD1 - dI) * dD3Mul; - tmp32 = (int32_t)round((a * dS4 + b * dS3) / (dS3 + dS4)); - d = (int16_t)tmp32; + dC = (dA * dS2 + dB * dS1) / (dS1 + dS2); + dD = (dA * dS4 + dB * dS3) / (dS3 + dS4); } - putSampleValue(s->pek, t, y1 + i, c); - putSampleValue(s->pek, t, y2 + i, d); - - i += dist; + putSampleValue(s->dataPtr, aIdx, dC, sample16Bit); + putSampleValue(s->dataPtr, bIdx, dD, sample16Bit); } fixSample(s); @@ -2634,14 +2559,14 @@ void sampXFade(void) void rbSampleNoLoop(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; lockMixerCallback(); - restoreSample(s); + unfixSample(s); - s->typ &= ~3; + DISABLE_LOOP(s->flags); fixSample(s); unlockMixerCallback(); @@ -2653,18 +2578,20 @@ void rbSampleNoLoop(void) void rbSampleForwardLoop(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; lockMixerCallback(); - restoreSample(s); + unfixSample(s); + + DISABLE_LOOP(s->flags); + s->flags |= LOOP_FWD; - s->typ = (s->typ & ~3) | 1; - if (s->repL+s->repS == 0) + if (s->loopStart+s->loopLength == 0) { - s->repS = 0; - s->repL = s->len; + s->loopStart = 0; + s->loopLength = s->length; } fixSample(s); @@ -2677,18 +2604,20 @@ void rbSampleForwardLoop(void) void rbSamplePingpongLoop(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; lockMixerCallback(); - restoreSample(s); + unfixSample(s); + + DISABLE_LOOP(s->flags); + s->flags |= LOOP_BIDI; - s->typ = (s->typ & ~3) | 2; - if (s->repL+s->repS == 0) + if (s->loopStart+s->loopLength == 0) { - s->repS = 0; - s->repL = s->len; + s->loopStart = 0; + s->loopLength = s->length; } fixSample(s); @@ -2701,38 +2630,27 @@ void rbSamplePingpongLoop(void) static int32_t SDLCALL convSmp8Bit(void *ptr) { - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); + assert(s->dataPtr != NULL); pauseAudio(); - restoreSample(s); + unfixSample(s); - const int16_t *src16 = (const int16_t *)s->pek; - int32_t newLen = s->len >> 1; + const int16_t *src16 = (const int16_t *)s->dataPtr; + for (int32_t i = 0; i < s->length; i++) + s->dataPtr[i] = src16[i] >> 8; - for (int32_t i = 0; i < newLen; i++) - s->pek[i] = src16[i] >> 8; - - assert(s->origPek != NULL); - - int8_t *newPtr = (int8_t *)realloc(s->origPek, newLen + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } + reallocateSmpData(s, s->length, false); - s->repL >>= 1; - s->repS >>= 1; - s->len >>= 1; - s->typ &= ~16; // remove 16-bit flag + s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag fixSample(s); resumeAudio(); - editor.updateCurSmp = true; setSongModifiedFlag(); setMouseBusy(false); + editor.updateCurSmp = true; return true; (void)ptr; @@ -2740,8 +2658,8 @@ static int32_t SDLCALL convSmp8Bit(void *ptr) void rbSample8bit(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; if (okBox(2, "System request", "Convert sampledata?") == 1) @@ -2760,9 +2678,11 @@ void rbSample8bit(void) else { lockMixerCallback(); - restoreSample(s); + unfixSample(s); - s->typ &= ~16; // remove 16-bit flag + s->flags &= ~SAMPLE_16BIT; // remove 16-bit flag + s->length <<= 1; + // no need to call reallocateSmpData, number of bytes allocated is the same fixSample(s); unlockMixerCallback(); @@ -2775,41 +2695,30 @@ void rbSample8bit(void) static int32_t SDLCALL convSmp16Bit(void *ptr) { - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); pauseAudio(); - restoreSample(s); - - assert(s->origPek != NULL); + unfixSample(s); - int8_t *newPtr = (int8_t *)realloc(s->origPek, (s->len * 2) + LOOP_FIX_LEN); - if (newPtr == NULL) + if (!reallocateSmpData(s, s->length, true)) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return true; } - else - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } - int16_t *dst16 = (int16_t *)s->pek; - for (int32_t i = s->len-1; i >= 0; i--) - dst16[i] = s->pek[i] << 8; + int16_t *dst16 = (int16_t *)s->dataPtr; + for (int32_t i = s->length-1; i >= 0; i--) + dst16[i] = s->dataPtr[i] << 8; - s->len <<= 1; - s->repL <<= 1; - s->repS <<= 1; - s->typ |= 16; // add 16-bit flag + s->flags |= SAMPLE_16BIT; fixSample(s); resumeAudio(); - editor.updateCurSmp = true; setSongModifiedFlag(); setMouseBusy(false); + editor.updateCurSmp = true; return true; (void)ptr; @@ -2817,8 +2726,8 @@ static int32_t SDLCALL convSmp16Bit(void *ptr) void rbSample16bit(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; if (okBox(2, "System request", "Convert sampledata?") == 1) @@ -2837,14 +2746,11 @@ void rbSample16bit(void) else { lockMixerCallback(); - restoreSample(s); - - s->typ |= 16; // add 16-bit flag + unfixSample(s); - // make sure stuff is 2-byte aligned for 16-bit mode - s->repS &= 0xFFFFFFFE; - s->repL &= 0xFFFFFFFE; - s->len &= 0xFFFFFFFE; + s->flags |= SAMPLE_16BIT; + s->length >>= 1; + // no need to call reallocateSmpData, number of bytes allocated is the same fixSample(s); unlockMixerCallback(); @@ -2857,8 +2763,8 @@ void rbSample16bit(void) void clearSample(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; if (okBox(1, "System request", "Clear sample?") != 1) @@ -2869,125 +2775,112 @@ void clearSample(void) setSongModifiedFlag(); } -void sampMin(void) +void sampMinimize(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - if (okBox(1, "System request", "Minimize sample?") != 1) + const bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF; + if (!hasLoop) + { + okBox(0, "System message", "Only a looped sample can be minimized!"); return; + } - const bool hasLoop = s->typ & 3; - if (hasLoop && s->len > s->repS+s->repL && s->repL < s->len) + if (s->loopStart+s->loopLength >= s->length) { - lockMixerCallback(); + okBox(0, "System message", "The sample can't be minimized any further."); + return; + } - s->len = s->repS + s->repL; + if (okBox(1, "System request", "Minimize sample?") != 1) + return; + + lockMixerCallback(); - int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } + s->length = s->loopStart + s->loopLength; - // Note: we don't need to make a call to fixSample() + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + reallocateSmpData(s, s->length, sample16Bit); + // note: we don't need to make a call to fixSample() - unlockMixerCallback(); + unlockMixerCallback(); - updateSampleEditorSample(); - updateSampleEditor(); - setSongModifiedFlag(); - } + updateSampleEditorSample(); + updateSampleEditor(); + setSongModifiedFlag(); } void sampRepeatUp(void) { - int32_t addVal, lenSub; - - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - if (s->typ & 16) - { - lenSub = 4; - addVal = 2; - } - else - { - lenSub = 2; - addVal = 1; - } - - int32_t repS = curSmpRepS; - int32_t repL = curSmpRepL; + int32_t loopStart = curSmpLoopStart; + int32_t loopLength = curSmpLoopLength; - if (repS < s->len-lenSub) - repS += addVal; + if (loopStart < s->length-2) + loopStart++; - if (repS+repL > s->len) - repL = s->len - repS; + if (loopStart+loopLength > s->length) + loopLength = s->length - loopStart; - curSmpRepS = (s->typ & 16) ? (int32_t)(repS & 0xFFFFFFFE) : repS; - curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL; + curSmpLoopStart = loopStart; + curSmpLoopLength = loopLength; - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } void sampRepeatDown(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - int32_t repS = (s->typ & 16) ? curSmpRepS-2 : curSmpRepS-1; - if (repS < 0) - repS = 0; + int32_t loopStart = curSmpLoopStart - 1; + if (loopStart < 0) + loopStart = 0; - curSmpRepS = (s->typ & 16) ? (int32_t)(repS & 0xFFFFFFFE) : repS; + curSmpLoopStart = loopStart; - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } void sampReplenUp(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - int32_t repL = (s->typ & 16) ? curSmpRepL+2 : curSmpRepL+1; - if (curSmpRepS+repL > s->len) - repL = s->len - curSmpRepS; + int32_t loopLength = curSmpLoopLength + 1; + if (curSmpLoopStart+loopLength > s->length) + loopLength = s->length - curSmpLoopStart; - curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL; + curSmpLoopLength = loopLength; - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } void sampReplenDown(void) { - int32_t repL; + int32_t loopLength; - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - if (s->typ & 16) - repL = curSmpRepL - 2; - else - repL = curSmpRepL - 1; - - if (repL < 0) - repL = 0; + loopLength = curSmpLoopLength - 1; + if (loopLength < 0) + loopLength = 0; - curSmpRepL = (s->typ & 16) ? (int32_t)(repL & 0xFFFFFFFE) : repL; + curSmpLoopLength = loopLength; - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } @@ -3104,7 +2997,7 @@ void showSampleEditor(void) showScrollBar(SB_SAMP_SCROLL); - // clear two lines that are never written to when the sampler is open + // clear two lines in the sample data view that are never written to when the sampler is open hLine(0, 173, SAMPLE_AREA_WIDTH, PAL_BCKGRND); hLine(0, 328, SAMPLE_AREA_WIDTH, PAL_BCKGRND); @@ -3127,7 +3020,7 @@ void toggleSampleEditor(void) } } -static void writeSmpXORLine(int32_t x) +static void invertSamplePosLine(int32_t x) { if (x < 0 || x >= SCREEN_W) return; @@ -3141,18 +3034,18 @@ static void writeSamplePosLine(void) { uint8_t ins, smp; - assert(editor.curSmpChannel < MAX_VOICES); + assert(editor.curSmpChannel < MAX_CHANNELS); lastChInstr_t *c = &lastChInstr[editor.curSmpChannel]; - if (c->instrNr == 130) // "Play Wave/Range/Display" in Smp. Ed. + if (c->instrNum == 130) // "Play Wave/Range/Display" in Smp. Ed. { ins = editor.curPlayInstr; smp = editor.curPlaySmp; } else { - ins = c->instrNr; - smp = c->sampleNr; + ins = c->instrNum; + smp = c->smpNum; } if (editor.curInstr == ins && editor.curSmp == smp) @@ -3166,8 +3059,8 @@ static void writeSamplePosLine(void) { if (scrPos != smpEd_OldSmpPosLine) { - writeSmpXORLine(smpEd_OldSmpPosLine); // remove old line - writeSmpXORLine(scrPos); // write new line + invertSamplePosLine(smpEd_OldSmpPosLine); // remove old line + invertSamplePosLine(scrPos); // write new line } smpEd_OldSmpPosLine = scrPos; @@ -3177,7 +3070,7 @@ static void writeSamplePosLine(void) } if (smpEd_OldSmpPosLine != -1) - writeSmpXORLine(smpEd_OldSmpPosLine); + invertSamplePosLine(smpEd_OldSmpPosLine); smpEd_OldSmpPosLine = -1; } @@ -3204,61 +3097,52 @@ void handleSamplerRedrawing(void) static void setLeftLoopPinPos(int32_t x) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - int32_t newPos = scr2SmpPos(x) - curSmpRepS; - int32_t repS = curSmpRepS + newPos; - int32_t repL = curSmpRepL - newPos; + int32_t newPos = scr2SmpPos(x) - curSmpLoopStart; + int32_t loopStart = curSmpLoopStart + newPos; + int32_t loopLength = curSmpLoopLength - newPos; - if (repS < 0) + if (loopStart < 0) { - repL += repS; - repS = 0; + loopLength += loopStart; + loopStart = 0; } - if (repL < 0) + if (loopLength < 0) { - repL = 0; - repS = curSmpRepS + curSmpRepL; + loopLength = 0; + loopStart = curSmpLoopStart + curSmpLoopLength; } - if (s->typ & 16) - { - repS &= 0xFFFFFFFE; - repL &= 0xFFFFFFFE; - } - - curSmpRepS = repS; - curSmpRepL = repL; + curSmpLoopStart = loopStart; + curSmpLoopLength = loopLength; - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } static void setRightLoopPinPos(int32_t x) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; - int32_t repL = scr2SmpPos(x) - curSmpRepS; - if (repL < 0) - repL = 0; + int32_t loopLength = scr2SmpPos(x) - curSmpLoopStart; + if (loopLength < 0) + loopLength = 0; - if (repL+curSmpRepS > s->len) - repL = s->len - curSmpRepS; + if (loopLength+curSmpLoopStart > s->length) + loopLength = s->length - curSmpLoopStart; - if (repL < 0) - repL = 0; + if (loopLength < 0) + loopLength = 0; - if (s->typ & 16) - repL &= 0xFFFFFFFE; + curSmpLoopLength = loopLength; - curSmpRepL = repL; - - fixRepeatGadgets(); + fixLoopGadgets(); updateLoopsOnMouseUp = true; } @@ -3266,8 +3150,8 @@ static int32_t mouseYToSampleY(int32_t my) { my -= 174; // 0..SAMPLE_AREA_HEIGHT-1 - const double dTmp = round(my * (256.0 / SAMPLE_AREA_HEIGHT)); - const int32_t tmp32 = (const int32_t)dTmp; + const double dTmp = my * (256.0 / SAMPLE_AREA_HEIGHT); + const int32_t tmp32 = (const int32_t)(dTmp + 0.5); // rounded return 255 - CLAMP(tmp32, 0, 255); } @@ -3278,8 +3162,8 @@ static void editSampleData(bool mouseButtonHeld) int16_t *ptr16; int32_t tmp32, p, vl, tvl, r, rl, rvl, start, end; - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; int32_t mx = mouse.x; @@ -3291,12 +3175,10 @@ static void editSampleData(bool mouseButtonHeld) if (!mouseButtonHeld) { pauseAudio(); - restoreSample(s); + unfixSample(s); editor.editSampleFlag = true; lastDrawX = scr2SmpPos(mx); - if (s->typ & 16) - lastDrawX >>= 1; lastDrawY = mouseYToSampleY(my); @@ -3309,15 +3191,9 @@ static void editSampleData(bool mouseButtonHeld) } if (mx != lastMouseX) - { p = scr2SmpPos(mx); - if (s->typ & 16) - p >>= 1; - } else - { p = lastDrawX; - } if (!keyb.leftShiftPressed && my != lastMouseY) vl = mouseYToSampleY(my); @@ -3344,21 +3220,17 @@ static void editSampleData(bool mouseButtonHeld) vl = tmp32; } - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - // 16-bit - - ptr16 = (int16_t *)s->pek; + ptr16 = (int16_t *)s->dataPtr; start = p; - end = lastDrawX+1; - if (start < 0) start = 0; - tmp32 = s->len >> 1; - if (end > tmp32) - end = tmp32; + end = lastDrawX+1; + if (end > s->length) + end = s->length; if (p == lastDrawX) { @@ -3390,19 +3262,17 @@ static void editSampleData(bool mouseButtonHeld) } } } - else + else // 8-bit { - // 8-bit - - ptr8 = s->pek; + ptr8 = s->dataPtr; start = p; if (start < 0) start = 0; end = lastDrawX+1; - if (end > s->len) - end = s->len; + if (end > s->length) + end = s->length; if (p == lastDrawX) { @@ -3641,23 +3511,26 @@ static int32_t SDLCALL sampleBackwardsThread(void *ptr) { int8_t tmp8, *ptrStart, *ptrEnd; int16_t tmp16, *ptrStart16, *ptrEnd16; - sampleTyp *s = getCurSample(); - if (s->typ & 16) + const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2); + sample_t *s = getCurSample(); + const bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + + if (sample16Bit) { - if (smpEd_Rx1 >= smpEd_Rx2) + if (!sampleDataMarked) { - ptrStart16 = (int16_t *)s->pek; - ptrEnd16 = (int16_t *)&s->pek[s->len-2]; + ptrStart16 = (int16_t *)s->dataPtr; + ptrEnd16 = (int16_t *)s->dataPtr + (s->length-1); } else { - ptrStart16 = (int16_t *)&s->pek[smpEd_Rx1]; - ptrEnd16 = (int16_t *)&s->pek[smpEd_Rx2-2]; + ptrStart16 = (int16_t *)s->dataPtr + smpEd_Rx1; + ptrEnd16 = (int16_t *)s->dataPtr + (smpEd_Rx2-1); } pauseAudio(); - restoreSample(s); + unfixSample(s); while (ptrStart16 < ptrEnd16) { @@ -3671,19 +3544,19 @@ static int32_t SDLCALL sampleBackwardsThread(void *ptr) } else { - if (smpEd_Rx1 >= smpEd_Rx2) + if (!sampleDataMarked) { - ptrStart = s->pek; - ptrEnd = &s->pek[s->len-1]; + ptrStart = s->dataPtr; + ptrEnd = &s->dataPtr[s->length-1]; } else { - ptrStart = &s->pek[smpEd_Rx1]; - ptrEnd = &s->pek[smpEd_Rx2-1]; + ptrStart = &s->dataPtr[smpEd_Rx1]; + ptrEnd = &s->dataPtr[smpEd_Rx2-1]; } pauseAudio(); - restoreSample(s); + unfixSample(s); while (ptrStart < ptrEnd) { @@ -3698,8 +3571,8 @@ static int32_t SDLCALL sampleBackwardsThread(void *ptr) setSongModifiedFlag(); setMouseBusy(false); - writeSampleFlag = true; + writeSampleFlag = true; return true; (void)ptr; @@ -3707,8 +3580,8 @@ static int32_t SDLCALL sampleBackwardsThread(void *ptr) void sampleBackwards(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len < 2) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length < 2) return; mouseAnimOn(); @@ -3722,30 +3595,23 @@ void sampleBackwards(void) SDL_DetachThread(thread); } -static int32_t SDLCALL sampleConvThread(void *ptr) +static int32_t SDLCALL sampleChangeSignThread(void *ptr) { - int8_t *ptr8; - int16_t *ptr16; - int32_t i, len; - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); pauseAudio(); - restoreSample(s); + unfixSample(s); - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - len = s->len / 2; - ptr16 = (int16_t *)s->pek; - - for (i = 0; i < len; i++) + int16_t *ptr16 = (int16_t *)s->dataPtr; + for (int32_t i = 0; i < s->length; i++) ptr16[i] ^= 0x8000; } else { - len = s->len; - ptr8 = s->pek; - - for (i = 0; i < len; i++) + int8_t *ptr8 = s->dataPtr; + for (int32_t i = 0; i < s->length; i++) ptr8[i] ^= 0x80; } @@ -3754,21 +3620,21 @@ static int32_t SDLCALL sampleConvThread(void *ptr) setSongModifiedFlag(); setMouseBusy(false); - writeSampleFlag = true; + writeSampleFlag = true; return true; (void)ptr; } -void sampleConv(void) +void sampleChangeSign(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; mouseAnimOn(); - thread = SDL_CreateThread(sampleConvThread, NULL, NULL); + thread = SDL_CreateThread(sampleChangeSignThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); @@ -3778,17 +3644,19 @@ void sampleConv(void) SDL_DetachThread(thread); } -static int32_t SDLCALL sampleConvWThread(void *ptr) +static int32_t SDLCALL sampleByteSwapThread(void *ptr) { - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); pauseAudio(); - restoreSample(s); + unfixSample(s); - int32_t len = s->len / 2; - int8_t *ptr8 = s->pek; + int32_t length = s->length; + if (!(s->flags & SAMPLE_16BIT)) + length >>= 1; - for (int32_t i = 0; i < len; i++, ptr8 += 2) + int8_t *ptr8 = s->dataPtr; + for (int32_t i = 0; i < length; i++, ptr8 += 2) { const int8_t tmp = ptr8[0]; ptr8[0] = ptr8[1]; @@ -3800,21 +3668,27 @@ static int32_t SDLCALL sampleConvWThread(void *ptr) setSongModifiedFlag(); setMouseBusy(false); - writeSampleFlag = true; + writeSampleFlag = true; return true; (void)ptr; } -void sampleConvW(void) +void sampleByteSwap(void) { - sampleTyp *s = getCurSample(); - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; + if (!(s->flags & SAMPLE_16BIT)) + { + if (okBox(2, "System request", "Byte swapping only makes sense on a 16-bit sample. Continue?") != 1) + return; + } + mouseAnimOn(); - thread = SDL_CreateThread(sampleConvWThread, NULL, NULL); + thread = SDL_CreateThread(sampleByteSwapThread, NULL, NULL); if (thread == NULL) { okBox(0, "System message", "Couldn't create thread!"); @@ -3828,46 +3702,42 @@ static int32_t SDLCALL fixDCThread(void *ptr) { int8_t *ptr8; int16_t *ptr16; - int32_t i, len, smpSub, smp32; - sampleTyp *s = getCurSample(); + int32_t length; - int64_t averageDC = 0; + const bool sampleDataMarked = (smpEd_Rx1 != smpEd_Rx2); + sample_t *s = getCurSample(); - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - if (smpEd_Rx1 >= smpEd_Rx2) + if (!sampleDataMarked) { - assert(!(s->len & 1)); - - ptr16 = (int16_t *)s->pek; - len = s->len >> 1; + ptr16 = (int16_t *)s->dataPtr; + length = s->length; } else { - assert(!(smpEd_Rx1 & 1)); - assert(!(smpEd_Rx2 & 1)); - - ptr16 = (int16_t *)&s->pek[smpEd_Rx1]; - len = (smpEd_Rx2 - smpEd_Rx1) >> 1; + ptr16 = (int16_t *)&s->dataPtr + smpEd_Rx1; + length = smpEd_Rx2 - smpEd_Rx1; } - if (len < 0 || len > s->len>>1) + if (length < 0 || length > s->length) { setMouseBusy(false); return true; } pauseAudio(); - restoreSample(s); + unfixSample(s); - for (i = 0; i < len; i++) + int64_t averageDC = 0; + for (int32_t i = 0; i < length; i++) averageDC += ptr16[i]; - averageDC /= len; + averageDC = (averageDC + (length>>1)) / length; // rounded - smpSub = (int32_t)averageDC; - for (i = 0; i < len; i++) + const int32_t smpSub = (int32_t)averageDC; + for (int32_t i = 0; i < length; i++) { - smp32 = ptr16[i] - smpSub; + int32_t smp32 = ptr16[i] - smpSub; CLAMP16(smp32); ptr16[i] = (int16_t)smp32; } @@ -3875,36 +3745,37 @@ static int32_t SDLCALL fixDCThread(void *ptr) fixSample(s); resumeAudio(); } - else + else // 8-bit { - if (smpEd_Rx1 >= smpEd_Rx2) + if (!sampleDataMarked) { - ptr8 = s->pek; - len = s->len; + ptr8 = s->dataPtr; + length = s->length; } else { - ptr8 = &s->pek[smpEd_Rx1]; - len = smpEd_Rx2 - smpEd_Rx1; + ptr8 = &s->dataPtr[smpEd_Rx1]; + length = smpEd_Rx2 - smpEd_Rx1; } - if (len < 0 || len > s->len) + if (length < 0 || length > s->length) { setMouseBusy(false); return true; } pauseAudio(); - restoreSample(s); + unfixSample(s); - for (i = 0; i < len; i++) + int64_t averageDC = 0; + for (int32_t i = 0; i < length; i++) averageDC += ptr8[i]; - averageDC /= len; + averageDC = (averageDC + (length>>1)) / length; // rounded - smpSub = (int32_t)averageDC; - for (i = 0; i < len; i++) + const int32_t smpSub = (int32_t)averageDC; + for (int32_t i = 0; i < length; i++) { - smp32 = ptr8[i] - smpSub; + int32_t smp32 = ptr8[i] - smpSub; CLAMP8(smp32); ptr8[i] = (int8_t)smp32; } @@ -3913,10 +3784,10 @@ static int32_t SDLCALL fixDCThread(void *ptr) resumeAudio(); } - writeSampleFlag = true; setSongModifiedFlag(); setMouseBusy(false); + writeSampleFlag = true; return true; (void)ptr; @@ -3924,9 +3795,8 @@ static int32_t SDLCALL fixDCThread(void *ptr) void fixDC(void) { - sampleTyp *s = getCurSample(); - - if (s == NULL || s->pek == NULL || s->len <= 0) + sample_t *s = getCurSample(); + if (s == NULL || s->dataPtr == NULL || s->length <= 0) return; mouseAnimOn(); @@ -3953,23 +3823,20 @@ void testSmpEdMouseUp(void) // used for setting new loop points { updateLoopsOnMouseUp = false; - sampleTyp *s = getCurSample(); + sample_t *s = getCurSample(); if (s == NULL) return; - if (s->repS != curSmpRepS || s->repL != curSmpRepL) + if (s->loopStart != curSmpLoopStart || s->loopLength != curSmpLoopLength) { lockMixerCallback(); - restoreSample(s); - - setSongModifiedFlag(); - - s->repS = curSmpRepS; - s->repL = curSmpRepL; - + unfixSample(s); + s->loopStart = curSmpLoopStart; + s->loopLength = curSmpLoopLength; fixSample(s); unlockMixerCallback(); + setSongModifiedFlag(); writeSample(true); } } diff --git a/src/ft2_sample_ed.h b/src/ft2_sample_ed.h @@ -1,25 +1,32 @@ #pragma once #include <stdint.h> -#include "ft2_replayer.h" +#include "ft2_header.h" #define SAMPLE_AREA_HEIGHT 154 #define SAMPLE_AREA_WIDTH 632 #define SAMPLE_AREA_Y_CENTER 250 // allocs sample with proper alignment and padding for branchless resampling interpolation -bool allocateTmpSmpData(sampleTyp *s, int32_t length); +bool allocateSmpData(sample_t *s, int32_t length, bool sample16Bit); +bool allocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit); // reallocs sample with proper alignment and padding for branchless resampling interpolation -bool reallocateTmpSmpData(sampleTyp *s, int32_t length); +bool reallocateSmpData(sample_t *s, int32_t length, bool sample16Bit); +bool reallocateSmpDataPtr(smpPtr_t *sp, int32_t length, bool sample16Bit); -sampleTyp *getCurSample(void); -void checkSampleRepeat(sampleTyp *s); -void fixSample(sampleTyp *s); // modifies samples before index 0, and after loop/end (for branchless mixer interpolation) -void restoreSample(sampleTyp *s); // restores samples after loop/end +void setSmpDataPtr(sample_t *s, smpPtr_t *sp); +void freeSmpDataPtr(smpPtr_t *sp); +void freeSmpData(sample_t *s); + +bool cloneSample(sample_t *src, sample_t *dst); +sample_t *getCurSample(void); +void sanitizeSample(sample_t *s); +void fixSample(sample_t *s); // modifies samples before index 0, and after loop/end (for branchless mixer interpolation) +void unfixSample(sample_t *s); // restores samples after loop/end void clearSample(void); void clearCopyBuffer(void); -int32_t getSampleMiddleCRate(sampleTyp *s); +int32_t getSampleMiddleCRate(sample_t *s); int32_t getSampleRangeStart(void); int32_t getSampleRangeEnd(void); int32_t getSampleRangeLength(void); @@ -50,13 +57,13 @@ void rbSampleForwardLoop(void); void rbSamplePingpongLoop(void); void rbSample8bit(void); void rbSample16bit(void); -void sampMin(void); +void sampMinimize(void); void sampRepeatUp(void); void sampRepeatDown(void); void sampReplenUp(void); void sampReplenDown(void); -int16_t getSampleValue(int8_t *ptr, uint8_t typ, int32_t pos); -void putSampleValue(int8_t *ptr, uint8_t typ, int32_t pos, int16_t val); +double getSampleValue(int8_t *smpData, int32_t position, bool sample16Bit); +void putSampleValue(int8_t *smpData, int32_t position, double dSample, bool sample16Bit); void writeSample(bool forceSmpRedraw); void handleSampleDataMouseDown(bool mouseButtonHeld); void updateSampleEditorSample(void); @@ -72,10 +79,12 @@ void hideSampleEditorExt(void); void drawSampleEditorExt(void); void handleSampleEditorExtRedrawing(void); void sampleBackwards(void); -void sampleConv(void); -void sampleConvW(void); +void sampleChangeSign(void); +void sampleByteSwap(void); void fixDC(void); void smpEdStop(void); void testSmpEdMouseUp(void); +void sampleLine(int32_t x1, int32_t x2, int32_t y1, int32_t y2); + extern int32_t smpEd_Rx1, smpEd_Rx2; diff --git a/src/ft2_sample_ed_features.c b/src/ft2_sample_ed_features.c @@ -30,8 +30,9 @@ static volatile bool stopThread; static int8_t smpEd_RelReSmp, mix_Balance = 50; static bool echo_AddMemory, exitFlag, outOfMemory; -static int16_t vol_StartVol = 100, vol_EndVol = 100, echo_nEcho = 1, echo_VolChange = 30; +static int16_t echo_nEcho = 1, echo_VolChange = 30; static int32_t echo_Distance = 0x100; +static double dVol_StartVol = 100.0, dVol_EndVol = 100.0; static SDL_Thread *thread; static void pbExit(void) @@ -81,22 +82,22 @@ static void pbResampleTonesUp(void) static int32_t SDLCALL resampleThread(void *ptr) { + smpPtr_t sp; + if (instr[editor.curInstr] == NULL) return true; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); - const uint32_t mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; - const double dRatio = exp2(smpEd_RelReSmp / 12.0); + const double dRatio = exp2((int32_t)smpEd_RelReSmp / 12.0); - double dNewLen = s->len * dRatio; + double dNewLen = s->length * dRatio; if (dNewLen > (double)MAX_SAMPLE_LEN) dNewLen = (double)MAX_SAMPLE_LEN; - const uint32_t newLen = (int32_t)dNewLen & mask; - - int8_t *p2 = (int8_t *)malloc(newLen + LOOP_FIX_LEN); - if (p2 == NULL) + const uint32_t newLen = (int32_t)floor(dNewLen); + if (!allocateSmpDataPtr(&sp, newLen, sample16Bit)) { outOfMemory = true; setMouseBusy(false); @@ -104,15 +105,15 @@ static int32_t SDLCALL resampleThread(void *ptr) return true; } - int8_t *newPtr = p2 + SMP_DAT_OFFSET; - int8_t *p1 = s->pek; + int8_t *dst = sp.ptr; + int8_t *src = s->dataPtr; // 32.32 fixed-point logic const uint64_t delta64 = (const uint64_t)round((UINT32_MAX+1.0) / dRatio); uint64_t posFrac64 = 0; pauseAudio(); - restoreSample(s); + unfixSample(s); /* Nearest-neighbor resampling (no interpolation). ** @@ -123,57 +124,41 @@ static int32_t SDLCALL resampleThread(void *ptr) if (newLen > 0) { - if (s->typ & 16) + if (sample16Bit) { - const int16_t *src16 = (const int16_t *)p1; - int16_t *dst16 = (int16_t *)newPtr; + const int16_t *src16 = (const int16_t *)src; + int16_t *dst16 = (int16_t *)dst; - const uint32_t resampleLen = newLen >> 1; - for (uint32_t i = 0; i < resampleLen; i++) + for (uint32_t i = 0; i < newLen; i++) { - dst16[i] = src16[posFrac64 >> 32]; + const uint32_t position = posFrac64 >> 32; + dst16[i] = src16[position]; posFrac64 += delta64; } } - else + else // 8-bit { - const int8_t *src8 = p1; - int8_t *dst8 = newPtr; + const int8_t *src8 = src; + int8_t *dst8 = dst; for (uint32_t i = 0; i < newLen; i++) { - dst8[i] = src8[posFrac64 >> 32]; + const uint32_t position = posFrac64 >> 32; + dst8[i] = src8[position]; posFrac64 += delta64; } } } - free(s->origPek); - - s->relTon = CLAMP(s->relTon + smpEd_RelReSmp, -48, 71); - s->len = newLen & mask; - s->origPek = p2; - s->pek = s->origPek + SMP_DAT_OFFSET; - s->repS = (int32_t)(s->repS * dRatio); - s->repL = (int32_t)(s->repL * dRatio); - s->repS &= mask; - s->repL &= mask; - - if (s->repS >= s->len) - s->repS = s->len-1; - - if (s->repS+s->repL > s->len) - s->repL = s->len - s->repS; + freeSmpData(s); + setSmpDataPtr(s, &sp); - if (s->typ & 16) - { - s->len &= 0xFFFFFFFE; - s->repS &= 0xFFFFFFFE; - s->repL &= 0xFFFFFFFE; - } + s->relativeNote += smpEd_RelReSmp; + s->length = newLen; + s->loopStart = (int32_t)(s->loopStart * dRatio); + s->loopLength = (int32_t)(s->loopLength * dRatio); - if (s->repL <= 0) - s->typ &= ~3; // disable loop + sanitizeSample(s); fixSample(s); resumeAudio(); @@ -223,18 +208,17 @@ static void drawResampleBox(void) vLine(x + w - 3, y + 2, h - 4, PAL_BUTTON1); hLine(x + 2, y + h - 3, w - 4, PAL_BUTTON1); - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; - uint32_t mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; double dLenMul = exp2(smpEd_RelReSmp * (1.0 / 12.0)); - double dNewLen = s->len * dLenMul; + double dNewLen = s->length * dLenMul; if (dNewLen > (double)MAX_SAMPLE_LEN) dNewLen = (double)MAX_SAMPLE_LEN; textOutShadow(215, 236, PAL_FORGRND, PAL_BUTTON2, "Rel. h.tones"); textOutShadow(215, 250, PAL_FORGRND, PAL_BUTTON2, "New sample size"); - hexOut(361, 250, PAL_FORGRND, (uint32_t)dNewLen & mask, 8); + hexOut(361, 250, PAL_FORGRND, (int32_t)dNewLen, 8); if (smpEd_RelReSmp == 0) sign = ' '; else if (smpEd_RelReSmp < 0) sign = '-'; @@ -326,7 +310,7 @@ void pbSampleResample(void) if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -425,23 +409,24 @@ static void pbEchoFadeoutUp(void) static int32_t SDLCALL createEchoThread(void *ptr) { + smpPtr_t sp; + if (echo_nEcho < 1) { ui.sysReqShown = false; return true; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; - int32_t readLen = s->len; - int8_t *readPtr = s->pek; - bool is16Bit = (s->typ & 16) ? true : false; + int32_t readLen = s->length; + int8_t *readPtr = s->dataPtr; + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); int32_t distance = echo_Distance * 16; - double dVolChange = echo_VolChange / 100.0; // calculate real number of echoes - double dSmp = is16Bit ? 32768.0 : 128.0; + double dSmp = sample16Bit ? 32768.0 : 128.0; int32_t k = 0; while (k++ < echo_nEcho && dSmp >= 1.0) dSmp *= dVolChange; @@ -458,21 +443,15 @@ static int32_t SDLCALL createEchoThread(void *ptr) if (echo_AddMemory) { int64_t tmp64 = (int64_t)distance * (nEchoes - 1); - if (is16Bit) - tmp64 <<= 1; tmp64 += writeLen; if (tmp64 > MAX_SAMPLE_LEN) tmp64 = MAX_SAMPLE_LEN; - writeLen = (int32_t)tmp64; - - if (is16Bit) - writeLen &= 0xFFFFFFFE; + writeLen = (uint32_t)tmp64; } - int8_t *writePtr = (int8_t *)malloc(writeLen + LOOP_FIX_LEN); - if (writePtr == NULL) + if (!allocateSmpDataPtr(&sp, writeLen, sample16Bit)) { outOfMemory = true; setMouseBusy(false); @@ -481,17 +460,14 @@ static int32_t SDLCALL createEchoThread(void *ptr) } pauseAudio(); - restoreSample(s); + unfixSample(s); int32_t writeIdx = 0; - if (is16Bit) + if (sample16Bit) { const int16_t *readPtr16 = (const int16_t *)readPtr; - int16_t *writePtr16 = (int16_t *)&writePtr[SMP_DAT_OFFSET]; - - writeLen >>= 1; - readLen >>= 1; + int16_t *writePtr16 = (int16_t *)sp.ptr; while (writeIdx < writeLen) { @@ -513,20 +489,16 @@ static int32_t SDLCALL createEchoThread(void *ptr) break; } - // rounding (faster than calling round()) - if (dSmpOut < 0.0) dSmpOut -= 0.5; - else if (dSmpOut > 0.0) dSmpOut += 0.5; + DROUND(dSmpOut); - int32_t smpOut = (int32_t)dSmpOut; - CLAMP16(smpOut); - writePtr16[writeIdx++] = (int16_t)smpOut; + int32_t smp32 = (int32_t)dSmpOut; + CLAMP16(smp32); + writePtr16[writeIdx++] = (int16_t)smp32; } - - writeLen <<= 1; } - else + else // 8-bit { - int8_t *writePtr8 = writePtr + SMP_DAT_OFFSET; + int8_t *writePtr8 = sp.ptr; while (writeIdx < writeLen) { double dSmpOut = 0.0; @@ -547,50 +519,25 @@ static int32_t SDLCALL createEchoThread(void *ptr) break; } - // rounding (faster than calling round()) - if (dSmpOut < 0.0) dSmpOut -= 0.5; - else if (dSmpOut > 0.0) dSmpOut += 0.5; + DROUND(dSmpOut); - int32_t smpOut = (int32_t)dSmpOut; - if (smpOut < -128) smpOut = -128; - else if (smpOut > 127) smpOut = 127; - writePtr8[writeIdx++] = (int8_t)smpOut; + int32_t smp32 = (int32_t)dSmpOut; + CLAMP8(smp32); + writePtr8[writeIdx++] = (int8_t)smp32; } } - free(s->origPek); + freeSmpData(s); + setSmpDataPtr(s, &sp); - if (stopThread) + if (stopThread) // we stopped before echo was done, realloc length { writeLen = writeIdx; - - int8_t *newPtr = (int8_t *)realloc(writePtr, writeIdx + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } - else - { - if (writePtr != NULL) - free(writePtr); - - s->origPek = s->pek = NULL; - writeLen = 0; - } - + reallocateSmpData(s, writeLen, sample16Bit); editor.updateCurSmp = true; } - else - { - s->origPek = writePtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } - - if (is16Bit) - writeLen &= 0xFFFFFFFE; - s->len = writeLen; + s->length = writeLen; fixSample(s); resumeAudio(); @@ -651,7 +598,7 @@ static void drawEchoBox(void) charOut(315 + (3 * 7), 226, PAL_FORGRND, '0' + (echo_nEcho % 10)); assert(echo_Distance <= 0x4000); - hexOut(308, 240, PAL_FORGRND, (uint32_t)echo_Distance << 4, 5); + hexOut(308, 240, PAL_FORGRND, echo_Distance << 4, 5); assert(echo_VolChange <= 100); textOutFixed(312, 254, PAL_FORGRND, PAL_BUTTONS, dec3StrTab[echo_VolChange]); @@ -816,11 +763,9 @@ void handleEchoToolPanic(void) void pbSampleEcho(void) { - uint16_t i; - if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -845,15 +790,15 @@ void pbSampleEcho(void) setScrollBarPos(1, echo_Distance, false); setScrollBarPos(2, echo_VolChange, false); drawCheckBox(0); - for (i = 0; i < 8; i++) drawPushButton(i); - for (i = 0; i < 3; i++) drawScrollBar(i); + for (uint16_t i = 0; i < 8; i++) drawPushButton(i); + for (uint16_t i = 0; i < 3; i++) drawScrollBar(i); flipFrame(); } hideCheckBox(0); - for (i = 0; i < 8; i++) hidePushButton(i); - for (i = 0; i < 3; i++) hideScrollBar(i); + for (uint16_t i = 0; i < 8; i++) hidePushButton(i); + for (uint16_t i = 0; i < 3; i++) hideScrollBar(i); windowClose(echo_AddMemory ? false : true); @@ -863,16 +808,21 @@ void pbSampleEcho(void) static int32_t SDLCALL mixThread(void *ptr) { - int8_t *destPtr, *mixPtr; - uint8_t mixTyp, destTyp; - int32_t destLen, mixLen; + smpPtr_t sp; - int16_t destIns = editor.curInstr; - int16_t destSmp = editor.curSmp; + int8_t *dstPtr, *mixPtr; + uint8_t mixFlags, dstFlags; + int32_t dstLen, mixLen; + + int16_t dstIns = editor.curInstr; + int16_t dstSmp = editor.curSmp; int16_t mixIns = editor.srcInstr; int16_t mixSmp = editor.srcSmp; - if (destIns == mixIns && destSmp == mixSmp) + sample_t *s = &instr[dstIns]->smp[dstSmp]; + sample_t *sSrc = &instr[mixIns]->smp[mixSmp]; + + if (dstIns == mixIns && dstSmp == mixSmp) { setMouseBusy(false); ui.sysReqShown = false; @@ -883,65 +833,61 @@ static int32_t SDLCALL mixThread(void *ptr) { mixLen = 0; mixPtr = NULL; - mixTyp = 0; + mixFlags = 0; } else { - mixLen = instr[mixIns]->samp[mixSmp].len; - mixPtr = instr[mixIns]->samp[mixSmp].pek; - mixTyp = instr[mixIns]->samp[mixSmp].typ; + mixLen = sSrc->length; + mixPtr = sSrc->dataPtr; + mixFlags = sSrc->flags; if (mixPtr == NULL) { mixLen = 0; - mixTyp = 0; + mixFlags = 0; } } - if (instr[destIns] == NULL) + if (instr[dstIns] == NULL) { - destLen = 0; - destPtr = NULL; - destTyp = 0; + dstLen = 0; + dstPtr = NULL; + dstFlags = 0; } else { - destLen = instr[destIns]->samp[destSmp].len; - destPtr = instr[destIns]->samp[destSmp].pek; - destTyp = instr[destIns]->samp[destSmp].typ; + dstLen = s->length; + dstPtr = s->dataPtr; + dstFlags = s->flags; - if (destPtr == NULL) + if (dstPtr == NULL) { - destLen = 0; - destTyp = 0; + dstLen = 0; + dstFlags = 0; } } - bool src16Bits = (mixTyp >> 4) & 1; - bool dst16Bits = (destTyp >> 4) & 1; + bool src16Bits = !!(mixFlags & SAMPLE_16BIT); + bool dst16Bits = !!(dstFlags & SAMPLE_16BIT); - int32_t mix8Size = src16Bits ? (mixLen >> 1) : mixLen; - int32_t dest8Size = dst16Bits ? (destLen >> 1) : destLen; - int32_t max8Size = (dest8Size > mix8Size) ? dest8Size : mix8Size; - int32_t maxLen = dst16Bits ? (max8Size << 1) : max8Size; - - if (maxLen <= 0) + int32_t maxLen = (dstLen > mixLen) ? dstLen : mixLen; + if (maxLen == 0) { setMouseBusy(false); ui.sysReqShown = false; return true; } - int8_t *p = (int8_t *)calloc(maxLen + LOOP_FIX_LEN, sizeof (int8_t)); - if (p == NULL) + if (!allocateSmpDataPtr(&sp, maxLen, dst16Bits)) { outOfMemory = true; setMouseBusy(false); ui.sysReqShown = false; return true; } + memset(sp.ptr, 0, maxLen); - if (instr[destIns] == NULL && !allocateInstr(destIns)) + if (instr[dstIns] == NULL && !allocateInstr(dstIns)) { outOfMemory = true; setMouseBusy(false); @@ -950,65 +896,38 @@ static int32_t SDLCALL mixThread(void *ptr) } pauseAudio(); + unfixSample(s); - restoreSample(&instr[destIns]->samp[destSmp]); - - // restore source sample + // unfix source sample if (instr[mixIns] != NULL) - restoreSample(&instr[mixIns]->samp[mixSmp]); + unfixSample(sSrc); const double dAmp1 = mix_Balance / 100.0; const double dAmp2 = 1.0 - dAmp1; + const double dSmp1ScaleMul = src16Bits ? (1.0 / 32768.0) : (1.0 / 128.0); + const double dSmp2ScaleMul = dst16Bits ? (1.0 / 32768.0) : (1.0 / 128.0); + const double dNormalizeMul = dst16Bits ? 32768.0 : 128.0; - int8_t *destPek = p + SMP_DAT_OFFSET; - for (int32_t i = 0; i < max8Size; i++) + for (int32_t i = 0; i < maxLen; i++) { - int32_t index16 = i << 1; - - int32_t smp1 = (i >= mix8Size) ? 0 : getSampleValue(mixPtr, mixTyp, src16Bits ? index16 : i); - int32_t smp2 = (i >= dest8Size) ? 0 : getSampleValue(destPtr, destTyp, dst16Bits ? index16 : i); - - if (!src16Bits) smp1 <<= 8; - if (!dst16Bits) smp2 <<= 8; - - double dSmp = (smp1 * dAmp1) + (smp2 * dAmp2); - if (!dst16Bits) - dSmp *= 1.0 / 256.0; - - // rounding (faster than calling round()) - if (dSmp < 0.0) dSmp -= 0.5; - else if (dSmp > 0.0) dSmp += 0.5; - - int32_t smp32 = (int32_t)dSmp; - if (dst16Bits) - { - CLAMP16(smp32); - } - else - { - if (smp32 < -128) smp32 = -128; - else if (smp32 > 127) smp32 = 127; - } + double dSmp1 = (i >= mixLen) ? 0.0 : (getSampleValue(mixPtr, i, src16Bits) * dSmp1ScaleMul); // -1.0 .. 0.999inf + double dSmp2 = (i >= dstLen) ? 0.0 : (getSampleValue(dstPtr, i, dst16Bits) * dSmp2ScaleMul); // -1.0 .. 0.999inf - putSampleValue(destPek, destTyp, dst16Bits ? index16 : i, (int16_t)smp32); + const double dSmp = ((dSmp1 * dAmp1) + (dSmp2 * dAmp2)) * dNormalizeMul; + putSampleValue(sp.ptr, i, dSmp, dst16Bits); } - if (instr[destIns]->samp[destSmp].origPek != NULL) - free(instr[destIns]->samp[destSmp].origPek); - - instr[destIns]->samp[destSmp].origPek = p; - instr[destIns]->samp[destSmp].pek = instr[destIns]->samp[destSmp].origPek + SMP_DAT_OFFSET; - instr[destIns]->samp[destSmp].len = maxLen; - instr[destIns]->samp[destSmp].typ = destTyp; + freeSmpData(s); + setSmpDataPtr(s, &sp); - if (dst16Bits) - instr[destIns]->samp[destSmp].len &= 0xFFFFFFFE; + s->length = maxLen; + s->flags = dstFlags; - fixSample(&instr[destIns]->samp[destSmp]); + fixSample(s); - // fix source sample + // re-fix source sample again if (instr[mixIns] != NULL) - fixSample(&instr[mixIns]->samp[mixSmp]); + fixSample(sSrc); resumeAudio(); @@ -1187,64 +1106,60 @@ void pbSampleMix(void) static void sbSetStartVolPos(uint32_t pos) { - int16_t val = (int16_t)(pos - 500); - if (val != vol_StartVol) + int32_t val = (int32_t)(pos - 200); + if (val != (int32_t)dVol_StartVol) { if (ABS(val) < 10) val = 0; else if (ABS(val - 100) < 10) val = 100; - else if (ABS(val - 200) < 10) val = 200; - else if (ABS(val - 300) < 10) val = 300; - else if (ABS(val - 400) < 10) val = 400; else if (ABS(val + 100) < 10) val = -100; - else if (ABS(val + 200) < 10) val = -200; - else if (ABS(val + 300) < 10) val = -300; - else if (ABS(val + 400) < 10) val = -400; - vol_StartVol = val; + dVol_StartVol = (double)val; } } static void sbSetEndVolPos(uint32_t pos) { - int16_t val = (int16_t)(pos - 500); - if (val != vol_EndVol) + int32_t val = (int32_t)(pos - 200); + if (val != (int32_t)dVol_EndVol) { if (ABS(val) < 10) val = 0; else if (ABS(val - 100) < 10) val = 100; - else if (ABS(val - 200) < 10) val = 200; - else if (ABS(val - 300) < 10) val = 300; - else if (ABS(val - 400) < 10) val = 400; else if (ABS(val + 100) < 10) val = -100; - else if (ABS(val + 200) < 10) val = -200; - else if (ABS(val + 300) < 10) val = -300; - else if (ABS(val + 400) < 10) val = -400; - vol_EndVol = val; + dVol_EndVol = val; } } static void pbSampStartVolDown(void) { - if (vol_StartVol > -500) - vol_StartVol--; + if (dVol_StartVol > -200.0) + dVol_StartVol -= 1.0; + + dVol_StartVol = floor(dVol_StartVol); } static void pbSampStartVolUp(void) { - if (vol_StartVol < 500) - vol_StartVol++; + if (dVol_StartVol < 200.0) + dVol_StartVol += 1.0; + + dVol_StartVol = floor(dVol_StartVol); } static void pbSampEndVolDown(void) { - if (vol_EndVol > -500) - vol_EndVol--; + if (dVol_EndVol > -200.0) + dVol_EndVol -= 1.0; + + dVol_EndVol = floor(dVol_EndVol); } static void pbSampEndVolUp(void) { - if (vol_EndVol < 500) - vol_EndVol++; + if (dVol_EndVol < 200.0) + dVol_EndVol += 1.0; + + dVol_EndVol = floor(dVol_EndVol); } static int32_t SDLCALL applyVolumeThread(void *ptr) @@ -1254,84 +1169,94 @@ static int32_t SDLCALL applyVolumeThread(void *ptr) if (instr[editor.curInstr] == NULL) goto applyVolumeExit; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; - if (x2 > s->len) - x2 = s->len; + if (x2 > s->length) + x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) goto applyVolumeExit; - - if (s->typ & 16) - { - x1 &= 0xFFFFFFFE; - x2 &= 0xFFFFFFFE; - } } else { + // no mark, operate on whole sample x1 = 0; - x2 = s->len; - } - - if (s->typ & 16) - { - x1 >>= 1; - x2 >>= 1; + x2 = s->length; } const int32_t len = x2 - x1; if (len <= 0) goto applyVolumeExit; - const double dVol1 = vol_StartVol / 100.0; - const double dVol2 = vol_EndVol / 100.0; - const double dPosMul = (dVol2 - dVol1) / len; + bool mustInterpolate = (dVol_StartVol != dVol_EndVol); + const double dVol = dVol_StartVol / 100.0; + const double dPosMul = ((dVol_EndVol / 100.0) - dVol) / len; pauseAudio(); - restoreSample(s); - if (s->typ & 16) + unfixSample(s); + if (s->flags & SAMPLE_16BIT) { - int16_t *ptr16 = (int16_t *)s->pek + x1; - for (int32_t i = 0; i < len; i++) + int16_t *ptr16 = (int16_t *)s->dataPtr + x1; + if (mustInterpolate) { - const double dAmp = dVol1 + (i * dPosMul); // linear interpolation + for (int32_t i = 0; i < len; i++) + { + double dSmp = (int32_t)ptr16[i] * (dVol + (i * dPosMul)); // linear interpolation + DROUND(dSmp); - double dSmp = ptr16[i] * dAmp; + int32_t smp32 = (int32_t)dSmp; + CLAMP16(smp32); + ptr16[i] = (int16_t)smp32; + } - // rounding (faster than calling round()) - if (dSmp < 0.0) dSmp -= 0.5; - else if (dSmp > 0.0) dSmp += 0.5; + } + else // no interpolation needed + { + for (int32_t i = 0; i < len; i++) + { + double dSmp = (int32_t)ptr16[i] * dVol; + DROUND(dSmp); - int32_t amp32 = (int32_t)dSmp; - CLAMP16(amp32); - ptr16[i] = (int16_t)amp32; + int32_t smp32 = (int32_t)dSmp; + CLAMP16(smp32); + ptr16[i] = (int16_t)smp32; + } } } - else + else // 8-bit sample { - int8_t *ptr8 = s->pek + x1; - for (int32_t i = 0; i < len; i++) + int8_t *ptr8 = s->dataPtr + x1; + if (mustInterpolate) { - const double dAmp = dVol1 + (i * dPosMul); // linear interpolation - - double dSmp = ptr8[i] * dAmp; + for (int32_t i = 0; i < len; i++) + { + double dSmp = (int32_t)ptr8[i] * (dVol + (i * dPosMul)); // linear interpolation + DROUND(dSmp); - // rounding (faster than calling round()) - if (dSmp < 0.0) dSmp -= 0.5; - else if (dSmp > 0.0) dSmp += 0.5; + int32_t smp32 = (int32_t)dSmp; + CLAMP8(smp32); + ptr8[i] = (int8_t)smp32; + } + } + else // no interpolation needed + { + for (int32_t i = 0; i < len; i++) + { + double dSmp = (int32_t)ptr8[i] * dVol; + DROUND(dSmp); - int32_t amp32 = (int32_t)dSmp; - CLAMP8(amp32); - ptr8[i] = (int8_t)amp32; + int32_t smp32 = (int32_t)dSmp; + CLAMP8(smp32); + ptr8[i] = (int8_t)smp32; + } } } fixSample(s); @@ -1350,7 +1275,7 @@ applyVolumeExit: static void pbApplyVolume(void) { - if (vol_StartVol == 100 && vol_EndVol == 100) + if (dVol_StartVol == 100.0 && dVol_EndVol == 100.0) { ui.sysReqShown = false; return; // no volume change to be done @@ -1374,62 +1299,66 @@ static int32_t SDLCALL getMaxScaleThread(void *ptr) if (instr[editor.curInstr] == NULL) goto getScaleExit; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *s = &instr[editor.curInstr]->smp[editor.curSmp]; if (smpEd_Rx1 < smpEd_Rx2) { x1 = smpEd_Rx1; x2 = smpEd_Rx2; - if (x2 > s->len) - x2 = s->len; + if (x2 > s->length) + x2 = s->length; if (x1 < 0) x1 = 0; if (x2 <= x1) goto getScaleExit; - - if (s->typ & 16) - { - x1 &= 0xFFFFFFFE; - x2 &= 0xFFFFFFFE; - } } else { // no sample marking, operate on the whole sample x1 = 0; - x2 = s->len; + x2 = s->length; } uint32_t len = x2 - x1; - if (s->typ & 16) - len >>= 1; - if (len <= 0) { - vol_StartVol = 0; - vol_EndVol = 0; + dVol_StartVol = dVol_EndVol = 100.0; goto getScaleExit; } - restoreSample(s); + double dVolChange = 100.0; + + /* If sample is looped and the loopEnd point is inside the marked range, + ** we need to unfix the fixed interpolation sample before scanning, + ** and fix it again after we're done. + */ + bool hasLoop = GET_LOOPTYPE(s->flags) != LOOP_OFF; + const int32_t loopEnd = s->loopStart + s->loopLength; + bool fixedSampleInRange = hasLoop && (x1 <= loopEnd) && (x2 >= loopEnd); + + if (fixedSampleInRange) + unfixSample(s); int32_t maxAmp = 0; - if (s->typ & 16) + if (s->flags & SAMPLE_16BIT) { - const int16_t *ptr16 = (const int16_t *)&s->pek[x1]; + const int16_t *ptr16 = (const int16_t *)s->dataPtr + x1; for (uint32_t i = 0; i < len; i++) { const int32_t absSmp = ABS(ptr16[i]); if (absSmp > maxAmp) maxAmp = absSmp; } + + if (maxAmp > 0) + dVolChange = (32767.0 / maxAmp) * 100.0; } - else + else // 8-bit { - const int8_t *ptr8 = (const int8_t *)&s->pek[x1]; + const int8_t *ptr8 = (const int8_t *)&s->dataPtr[x1]; for (uint32_t i = 0; i < len; i++) { const int32_t absSmp = ABS(ptr8[i]); @@ -1437,25 +1366,17 @@ static int32_t SDLCALL getMaxScaleThread(void *ptr) maxAmp = absSmp; } - maxAmp <<= 8; + if (maxAmp > 0) + dVolChange = (127.0 / maxAmp) * 100.0; } - fixSample(s); + if (fixedSampleInRange) + fixSample(s); - if (maxAmp <= 0) - { - vol_StartVol = 0; - vol_EndVol = 0; - } - else - { - int32_t vol = (100 * 32768) / maxAmp; - if (vol > 500) - vol = 500; + if (dVolChange < 100.0) // yes, this can happen... + dVolChange = 100.0; - vol_StartVol = (int16_t)vol; - vol_EndVol = (int16_t)vol; - } + dVol_StartVol = dVol_EndVol = dVolChange; getScaleExit: setMouseBusy(false); @@ -1484,7 +1405,7 @@ static void drawSampleVolumeBox(void) const int16_t y = 230; const int16_t w = 301; const int16_t h = 52; - uint16_t val; + uint32_t val; // main fill fillRect(x + 1, y + 1, w - 2, h - 2, PAL_BUTTONS); @@ -1506,52 +1427,75 @@ static void drawSampleVolumeBox(void) charOutShadow(282, 236, PAL_FORGRND, PAL_BUTTON2, '%'); charOutShadow(282, 250, PAL_FORGRND, PAL_BUTTON2, '%'); - if (vol_StartVol == 0) sign = ' '; - else if (vol_StartVol < 0) sign = '-'; - else sign = '+'; + const int32_t startVol = (int32_t)dVol_StartVol; + const int32_t endVol = (int32_t)dVol_EndVol; - val = ABS(vol_StartVol); - if (val > 99) + if (startVol > 200) { - charOut(253, 236, PAL_FORGRND, sign); - charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100)); - charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); - charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); - } - else if (val > 9) - { - charOut(260, 236, PAL_FORGRND, sign); - charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10)); - charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); + charOut(253, 236, PAL_FORGRND, '>'); + charOut(260, 236, PAL_FORGRND, '2'); + charOut(267, 236, PAL_FORGRND, '0'); + charOut(274, 236, PAL_FORGRND, '0'); } else { - charOut(267, 236, PAL_FORGRND, sign); - charOut(274, 236, PAL_FORGRND, '0' + (char)val); - } - - if (vol_EndVol == 0) sign = ' '; - else if (vol_EndVol < 0) sign = '-'; - else sign = '+'; + if (startVol == 0) sign = ' '; + else if (startVol < 0) sign = '-'; + else sign = '+'; - val = ABS(vol_EndVol); - if (val > 99) - { - charOut(253, 250, PAL_FORGRND, sign); - charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100)); - charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10)); - charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); + val = ABS(startVol); + if (val > 99) + { + charOut(253, 236, PAL_FORGRND, sign); + charOut(260, 236, PAL_FORGRND, '0' + (char)(val / 100)); + charOut(267, 236, PAL_FORGRND, '0' + ((val / 10) % 10)); + charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); + } + else if (val > 9) + { + charOut(260, 236, PAL_FORGRND, sign); + charOut(267, 236, PAL_FORGRND, '0' + (char)(val / 10)); + charOut(274, 236, PAL_FORGRND, '0' + (val % 10)); + } + else + { + charOut(267, 236, PAL_FORGRND, sign); + charOut(274, 236, PAL_FORGRND, '0' + (char)val); + } } - else if (val > 9) + + if (endVol > 200) { - charOut(260, 250, PAL_FORGRND, sign); - charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10)); - charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); + charOut(253, 250, PAL_FORGRND, '>'); + charOut(260, 250, PAL_FORGRND, '2'); + charOut(267, 250, PAL_FORGRND, '0'); + charOut(274, 250, PAL_FORGRND, '0'); } else { - charOut(267, 250, PAL_FORGRND, sign); - charOut(274, 250, PAL_FORGRND, '0' + (char)val); + if (endVol == 0) sign = ' '; + else if (endVol < 0) sign = '-'; + else sign = '+'; + + val = ABS(endVol); + if (val > 99) + { + charOut(253, 250, PAL_FORGRND, sign); + charOut(260, 250, PAL_FORGRND, '0' + (char)(val / 100)); + charOut(267, 250, PAL_FORGRND, '0' + ((val / 10) % 10)); + charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); + } + else if (val > 9) + { + charOut(260, 250, PAL_FORGRND, sign); + charOut(267, 250, PAL_FORGRND, '0' + (char)(val / 10)); + charOut(274, 250, PAL_FORGRND, '0' + (val % 10)); + } + else + { + charOut(267, 250, PAL_FORGRND, sign); + charOut(274, 250, PAL_FORGRND, '0' + (char)val); + } } } @@ -1653,8 +1597,8 @@ static void setupVolumeBoxWidgets(void) s->callbackFunc = sbSetStartVolPos; s->visible = true; setScrollBarPageLength(0, 1); - setScrollBarEnd(0, 500 * 2); - setScrollBarPos(0, 500, false); + setScrollBarEnd(0, 200 * 2); + setScrollBarPos(0, 200, false); // volume end scrollbar s = &scrollBars[1]; @@ -1666,8 +1610,8 @@ static void setupVolumeBoxWidgets(void) s->callbackFunc = sbSetEndVolPos; s->visible = true; setScrollBarPageLength(1, 1); - setScrollBarEnd(1, 500 * 2); - setScrollBarPos(1, 500, false); + setScrollBarEnd(1, 200 * 2); + setScrollBarPos(1, 200, false); } void pbSampleVolume(void) @@ -1676,7 +1620,7 @@ void pbSampleVolume(void) if (editor.curInstr == 0 || instr[editor.curInstr] == NULL || - instr[editor.curInstr]->samp[editor.curSmp].pek == NULL) + instr[editor.curInstr]->smp[editor.curSmp].dataPtr == NULL) { return; } @@ -1701,8 +1645,12 @@ void pbSampleVolume(void) if (ui.setMouseIdle) mouseAnimOff(); drawSampleVolumeBox(); - setScrollBarPos(0, 500 + vol_StartVol, false); - setScrollBarPos(1, 500 + vol_EndVol, false); + + const int32_t startVol = (int32_t)dVol_StartVol; + const int32_t endVol = (int32_t)dVol_EndVol; + + setScrollBarPos(0, 200 + startVol, false); + setScrollBarPos(1, 200 + endVol, false); for (i = 0; i < 7; i++) drawPushButton(i); for (i = 0; i < 2; i++) drawScrollBar(i); diff --git a/src/ft2_sample_loader.c b/src/ft2_sample_loader.c @@ -16,6 +16,10 @@ #include "ft2_diskop.h" #include "ft2_structs.h" +#ifdef HAS_LIBFLAC +bool loadFLAC(FILE *f, uint32_t filesize); +#endif + bool loadAIFF(FILE *f, uint32_t filesize); bool loadIFF(FILE *f, uint32_t filesize); bool loadRAW(FILE *f, uint32_t filesize); @@ -26,14 +30,15 @@ enum FORMAT_UNKNOWN = 0, FORMAT_IFF = 1, FORMAT_WAV = 2, - FORMAT_AIFF = 3 + FORMAT_AIFF = 3, + FORMAT_FLAC = 4 }; // file extensions accepted by Disk Op. in sample mode char *supportedSmpExtensions[] = { "iff", "raw", "wav", "snd", "smp", "sam", "aif", "pat", - "aiff", + "aiff","flac", // IMPORTANT: Remember comma after last entry above "END_OF_LIST" // do NOT move, remove or edit this line! @@ -43,13 +48,13 @@ char *supportedSmpExtensions[] = bool loadAsInstrFlag, smpFilenameSet; char *smpFilename; uint8_t sampleSlot; -sampleTyp tmpSmp; +sample_t tmpSmp; // -------------------------- static volatile bool sampleIsLoading; static SDL_Thread *thread; -static void freeTmpSample(sampleTyp *s); +static void freeTmpSample(sample_t *s); // Crude sample detection routine. These aren't always accurate detections! static int8_t detectSample(FILE *f) @@ -62,6 +67,9 @@ static int8_t detectSample(FILE *f) fread(D, 1, sizeof (D), f); fseek(f, oldPos, SEEK_SET); + if (!memcmp("fLaC", &D[0], 4)) // XXX: Kinda lousy detection... + return FORMAT_FLAC; + if (!memcmp("FORM", &D[0], 4) && (!memcmp("8SVX", &D[8], 4) || !memcmp("16SV", &D[8], 4))) return FORMAT_IFF; @@ -105,6 +113,16 @@ static int32_t SDLCALL loadSampleThread(void *ptr) rewind(f); switch (format) { + case FORMAT_FLAC: + { +#ifdef HAS_LIBFLAC + sampleLoaded = loadFLAC(f, filesize); +#else + loaderMsgBox("Can't load sample: Program is not compiled with FLAC support!"); +#endif + } + break; + case FORMAT_IFF: sampleLoaded = loadIFF(f, filesize); break; case FORMAT_WAV: sampleLoaded = loadWAV(f, filesize); break; case FORMAT_AIFF: sampleLoaded = loadAIFF(f, filesize); break; @@ -116,7 +134,7 @@ static int32_t SDLCALL loadSampleThread(void *ptr) goto loadError; // sample loaded successfully! - + if (!smpFilenameSet) // if we didn't set a custom sample name in the loader, set it to its filename { char *tmpFilename = unicharToCp437(editor.tmpFilenameU, true); @@ -166,10 +184,13 @@ static int32_t SDLCALL loadSampleThread(void *ptr) goto loadError; } - sampleTyp *s = &instr[editor.curInstr]->samp[sampleSlot]; + sample_t *s = &instr[editor.curInstr]->smp[sampleSlot]; freeSample(editor.curInstr, sampleSlot); - memcpy(s, &tmpSmp, sizeof (sampleTyp)); + memcpy(s, &tmpSmp, sizeof (sample_t)); + + sanitizeSample(s); + fixSample(s); // prepares sample for branchless resampling interpolation fixInstrAndSampleNames(editor.curInstr); @@ -191,15 +212,9 @@ loadError: (void)ptr; } -static void freeTmpSample(sampleTyp *s) +static void freeTmpSample(sample_t *s) { - if (s->origPek != NULL) - { - free(s->origPek); - s->origPek = NULL; - } - - s->pek = NULL; + freeSmpData(s); } void removeSampleIsLoadingFlag(void) diff --git a/src/ft2_sample_loader.h b/src/ft2_sample_loader.h @@ -23,7 +23,7 @@ void removeSampleIsLoadingFlag(void); extern bool loadAsInstrFlag, smpFilenameSet; extern char *smpFilename; extern uint8_t sampleSlot; -extern sampleTyp tmpSmp; +extern sample_t tmpSmp; // -------------------------- // file extensions accepted by Disk Op. in sample mode diff --git a/src/ft2_sample_saver.c b/src/ft2_sample_saver.c @@ -52,38 +52,58 @@ static bool saveRangeFlag; static SDL_Thread *thread; // restores modified interpolation tap samples after loopEnd (for .RAW/.IFF/.WAV samples after save) -static void fileRestoreFixedSampleData(UNICHAR *filenameU, uint32_t sampleDataOffset, sampleTyp *s) +static void fileRestoreFixedSampleData(UNICHAR *filenameU, uint32_t sampleDataOffset, sample_t *s) { - if (!s->fixed) + if (!s->isFixed) return; // nothing to restore - FILE *f = UNICHAR_FOPEN(filenameU, "r+"); // open in read+update mode - if (f == NULL) - return; + int32_t sampleFixPos = s->fixedPos; + int32_t sampleFixOffset = 0; + int32_t samplesToWrite = SINC_RIGHT_TAPS; + + if (saveRangeFlag) + { + const int32_t markStart = getSampleRangeStart(); + const int32_t markEnd = getSampleRangeEnd(); - const bool sample16Bit = (s->typ & 16) ? true : false; + if (markStart > sampleFixPos+SINC_RIGHT_TAPS || markEnd < sampleFixPos) + return; // nothing to do here - uint32_t fixedPos = s->fixedPos; - if (sample16Bit) - fixedPos *= 2; + if (markStart > sampleFixPos) + { + sampleFixOffset += markStart-sampleFixPos; + samplesToWrite -= markStart-sampleFixPos; + } + + sampleFixPos -= markStart; + + if (sampleFixPos + samplesToWrite > markEnd) + samplesToWrite = markEnd - sampleFixPos; + + if (samplesToWrite < 0 || samplesToWrite > SINC_RIGHT_TAPS || sampleFixPos < 0 || sampleFixOffset < 0 || sampleFixOffset >= SINC_RIGHT_TAPS) + return; + + } - if (fixedPos >= (uint32_t)s->len) + FILE* f = UNICHAR_FOPEN(filenameU, "r+"); // open in read+update mode + if (f == NULL) return; - uint32_t bytesToWrite = SINC_RIGHT_TAPS * (sample16Bit+1); - if (fixedPos+bytesToWrite > (uint32_t)s->len) - bytesToWrite = s->len - fixedPos; + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + if (sample16Bit) + fseek(f, sampleDataOffset + (sampleFixPos * 2), SEEK_SET); + else + fseek(f, sampleDataOffset + sampleFixPos, SEEK_SET); - fseek(f, sampleDataOffset+fixedPos, SEEK_SET); if (sample16Bit) { - fwrite(s->fixedSmp, sizeof (int16_t), bytesToWrite / 2, f); + fwrite(&s->fixedSmp[sampleFixOffset], sizeof (int16_t), samplesToWrite, f); } else { - for (uint32_t i = 0; i < bytesToWrite; i++) + for (int32_t i = 0; i < samplesToWrite; i++) { - int8_t fixedSmp = (int8_t)s->fixedSmp[i]; + int8_t fixedSmp = (int8_t)s->fixedSmp[sampleFixOffset+i]; if (editor.sampleSaveMode == SMP_SAVE_MODE_WAV) // on 8-bit WAVs the sample data is unsigned fixedSmp ^= 0x80; // signed -> unsigned @@ -99,23 +119,25 @@ static bool saveRawSample(UNICHAR *filenameU, bool saveRangedData) int8_t *samplePtr; uint32_t sampleLen; - instrTyp *ins = instr[editor.curInstr]; - if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0) + instr_t *ins = instr[editor.curInstr]; + if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0) { okBoxThreadSafe(0, "System message", "The sample is empty!"); return false; } - sampleTyp *smp = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *smp = &instr[editor.curInstr]->smp[editor.curSmp]; + bool sample16Bit = !!(smp->flags & SAMPLE_16BIT); + if (saveRangedData) { - samplePtr = &smp->pek[getSampleRangeStart()]; + samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit]; sampleLen = getSampleRangeLength(); } else { - sampleLen = smp->len; - samplePtr = smp->pek; + sampleLen = smp->length; + samplePtr = smp->dataPtr; } FILE *f = UNICHAR_FOPEN(filenameU, "wb"); @@ -135,8 +157,8 @@ static bool saveRawSample(UNICHAR *filenameU, bool saveRangedData) fclose(f); // restore modified interpolation tap samples after loopEnd - const bool loopEnabled = (smp->typ & 3) ? true : false; - if (loopEnabled && smp->len > smp->repS+smp->repL) + bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF; + if (loopEnabled && smp->length > smp->loopStart+smp->loopLength) fileRestoreFixedSampleData(filenameU, 0, smp); editor.diskOpReadDir = true; // force diskop re-read @@ -181,14 +203,14 @@ static bool saveIFFSample(UNICHAR *filenameU, bool saveRangedData) int8_t *samplePtr; uint32_t sampleLen, smpNameLen, chunkLen; - instrTyp *ins = instr[editor.curInstr]; - if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0) + instr_t *ins = instr[editor.curInstr]; + if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0) { okBoxThreadSafe(0, "System message", "The sample is empty!"); return false; } - sampleTyp *smp = &instr[editor.curInstr]->samp[editor.curSmp]; + sample_t *smp = &instr[editor.curInstr]->smp[editor.curSmp]; FILE *f = UNICHAR_FOPEN(filenameU, "wb"); if (f == NULL) @@ -197,28 +219,30 @@ static bool saveIFFSample(UNICHAR *filenameU, bool saveRangedData) return false; } + bool sample16Bit = !!(smp->flags & SAMPLE_16BIT); + if (saveRangedData) { - samplePtr = &smp->pek[getSampleRangeStart()]; + samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit]; sampleLen = getSampleRangeLength(); } else { - sampleLen = smp->len; - samplePtr = smp->pek; + sampleLen = smp->length; + samplePtr = smp->dataPtr; } // "FORM" chunk iffWriteChunkHeader(f, "FORM", 0); // "FORM" chunk size is overwritten later - iffWriteUint32(f, (smp->typ & 16) ? 0x31365356 : 0x38535658); // bitdepth - "16SV" (16-bit) or "8SVX" (8-bit) + iffWriteUint32(f, sample16Bit ? 0x31365356 : 0x38535658); // bitdepth - "16SV" (16-bit) or "8SVX" (8-bit) // "VHDR" chunk iffWriteChunkHeader(f, "VHDR", 20); - if (!saveRangedData && (smp->typ & 3)) // loop enabled? + if (!saveRangedData && GET_LOOPTYPE(smp->flags) != LOOP_OFF) { - iffWriteUint32(f, smp->repS); // oneShotHiSamples - iffWriteUint32(f, smp->repL); // repeatHiSamples + iffWriteUint32(f, smp->loopStart << sample16Bit); // oneShotHiSamples + iffWriteUint32(f, smp->loopLength << sample16Bit); // repeatHiSamples } else { @@ -235,7 +259,7 @@ static bool saveIFFSample(UNICHAR *filenameU, bool saveRangedData) iffWriteUint8(f, 1); // ctOctave (number of samples) iffWriteUint8(f, 0); // sCompression - iffWriteUint32(f, smp->vol * 1024); // volume (max: 65536/0x10000) + iffWriteUint32(f, smp->volume * 1024); // volume (max: 65536/0x10000) // "NAME" chunk @@ -272,7 +296,7 @@ static bool saveIFFSample(UNICHAR *filenameU, bool saveRangedData) iffWriteChunkData(f, PROG_NAME_STR, chunkLen); // "BODY" chunk - chunkLen = sampleLen; + chunkLen = sampleLen << sample16Bit; iffWriteChunkHeader(f, "BODY", chunkLen); const uint32_t sampleDataPos = ftell(f); iffWriteChunkData(f, samplePtr, chunkLen); @@ -285,8 +309,8 @@ static bool saveIFFSample(UNICHAR *filenameU, bool saveRangedData) fclose(f); // restore modified interpolation tap samples after loopEnd - const bool loopEnabled = (smp->typ & 3) ? true : false; - if (loopEnabled && smp->len > smp->repS+smp->repL) + bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF; + if (loopEnabled && smp->length > smp->loopStart+smp->loopLength) fileRestoreFixedSampleData(filenameU, sampleDataPos, smp); editor.diskOpReadDir = true; // force diskop re-read @@ -304,14 +328,14 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) samplerChunk_t samplerChunk; mptExtraChunk_t mptExtraChunk; - instrTyp *ins = instr[editor.curInstr]; - if (ins == NULL || ins->samp[editor.curSmp].pek == NULL || ins->samp[editor.curSmp].len == 0) + instr_t *ins = instr[editor.curInstr]; + if (ins == NULL || ins->smp[editor.curSmp].dataPtr == NULL || ins->smp[editor.curSmp].length == 0) { okBoxThreadSafe(0, "System message", "The sample is empty!"); return false; } - sampleTyp *smp = &ins->samp[editor.curSmp]; + sample_t *smp = &ins->smp[editor.curSmp]; FILE *f = UNICHAR_FOPEN(filenameU, "wb"); if (f == NULL) @@ -320,18 +344,20 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) return false; } + bool sample16Bit = !!(smp->flags & SAMPLE_16BIT); + if (saveRangedData) { - samplePtr = &smp->pek[getSampleRangeStart()]; + samplePtr = &smp->dataPtr[getSampleRangeStart() << sample16Bit]; sampleLen = getSampleRangeLength(); } else { - sampleLen = smp->len; - samplePtr = smp->pek; + sampleLen = smp->length; + samplePtr = smp->dataPtr; } - const uint8_t sampleBitDepth = (smp->typ & 16) ? 16 : 8; + const uint8_t sampleBitDepth = sample16Bit ? 16 : 8; wavHeader.chunkID = 0x46464952; // "RIFF" wavHeader.chunkSize = 0; // is filled later @@ -345,7 +371,7 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) wavHeader.blockAlign = (wavHeader.numChannels * sampleBitDepth) / 8; wavHeader.bitsPerSample = sampleBitDepth; wavHeader.subchunk2ID = 0x61746164; // "data" - wavHeader.subchunk2Size = sampleLen; + wavHeader.subchunk2Size = sampleLen << sample16Bit; // write main header fwrite(&wavHeader, sizeof (wavHeader_t), 1, f); @@ -354,7 +380,7 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) const uint32_t sampleDataPos = ftell(f); if (sampleBitDepth == 16) { - fwrite((int16_t *)samplePtr, sizeof (int16_t), sampleLen / 2, f); + fwrite((int16_t *)samplePtr, sizeof (int16_t), sampleLen, f); } else { @@ -366,7 +392,7 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) fputc(0, f); // write pad byte if chunk size is uneven // write "smpl" chunk if loop is enabled - if (!saveRangedData && (smp->typ & 3)) + if (!saveRangedData && GET_LOOPTYPE(smp->flags) != LOOP_OFF) { memset(&samplerChunk, 0, sizeof (samplerChunk)); @@ -375,20 +401,9 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) samplerChunk.dwSamplePeriod = 1000000000 / wavHeader.sampleRate; samplerChunk.dwMIDIUnityNote = 60; // 60 = MIDI middle-C samplerChunk.cSampleLoops = 1; - samplerChunk.loop.dwType = (smp->typ & 3) - 1; // 0 = forward, 1 = ping-pong - - if (sampleBitDepth == 16) - { - // divide loop points by 2 to get samples insetad of bytes - samplerChunk.loop.dwStart = smp->repS / 2; - samplerChunk.loop.dwEnd = ((smp->repS + smp->repL) / 2) - 1; - } - else - { - // 8-bit sample - samplerChunk.loop.dwStart = smp->repS; - samplerChunk.loop.dwEnd = (smp->repS + smp->repL) - 1; - } + samplerChunk.loop.dwType = GET_LOOPTYPE(smp->flags)-1; // 0 = forward, 1 = ping-pong + samplerChunk.loop.dwStart = smp->loopStart; + samplerChunk.loop.dwEnd = (smp->loopStart + smp->loopLength) - 1; fwrite(&samplerChunk, sizeof (samplerChunk), 1, f); if (samplerChunk.chunkSize & 1) @@ -403,10 +418,10 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) mptExtraChunk.chunkID = 0x61727478; // "xtra" mptExtraChunk.chunkSize = sizeof (mptExtraChunk) - 4 - 4; mptExtraChunk.flags = 0x20; // set pan flag - mptExtraChunk.defaultPan = smp->pan; // 0..255 - mptExtraChunk.defaultVolume = smp->vol * 4; // 0..256 + mptExtraChunk.defaultPan = smp->panning; // 0..255 + mptExtraChunk.defaultVolume = smp->volume * 4; // 0..256 mptExtraChunk.globalVolume = 64; // 0..64 - mptExtraChunk.vibratoType = ins->vibTyp; // 0..3 0 = sine, 1 = square, 2 = ramp up, 3 = ramp down + mptExtraChunk.vibratoType = ins->vibType; // 0..3 0 = sine, 1 = square, 2 = ramp up, 3 = ramp down mptExtraChunk.vibratoSweep = ins->vibSweep; // 0..255 mptExtraChunk.vibratoDepth = ins->vibDepth; // 0..15 mptExtraChunk.vibratoRate= ins->vibRate; // 0..63 @@ -474,8 +489,8 @@ static bool saveWAVSample(UNICHAR *filenameU, bool saveRangedData) fclose(f); // restore modified interpolation tap samples after loopEnd - const bool loopEnabled = (smp->typ & 3) ? true : false; - if (loopEnabled && smp->len > smp->repS+smp->repL) + bool loopEnabled = GET_LOOPTYPE(smp->flags) != LOOP_OFF; + if (loopEnabled && smp->length > smp->loopStart+smp->loopLength) fileRestoreFixedSampleData(filenameU, sampleDataPos, smp); editor.diskOpReadDir = true; // force diskop re-read diff --git a/src/ft2_sampling.c b/src/ft2_sampling.c @@ -13,282 +13,281 @@ #include "ft2_sampling.h" #include "ft2_structs.h" -// these may very well change after opening the audio input device +#define STEREO_SAMPLE_HEIGHT (SAMPLE_AREA_HEIGHT/2) +#define SAMPLE_L_CENTER (SAMPLE_AREA_Y_CENTER - (STEREO_SAMPLE_HEIGHT/2)) +#define SAMPLE_R_CENTER (SAMPLE_AREA_Y_CENTER + (STEREO_SAMPLE_HEIGHT/2)) + +#define MONO_SAMPLE_HEIGHT SAMPLE_AREA_HEIGHT +#define SAMPLE_CENTER SAMPLE_AREA_Y_CENTER + +// this number may change when the audio input device is opened (must be 2^n) #define SAMPLING_BUFFER_SIZE 2048 +// (must be 2^n) +#if defined(_WIN32) || defined(__APPLE__) +#define PREVIEW_SAMPLES 2048 +#else +#define PREVIEW_SAMPLES 8192 +#endif + static bool sampleInStereo; static volatile bool drawSamplingBufferFlag, outOfMemoryFlag, noMoreRoomFlag; -static int16_t *currWriteBuf; -static int16_t displayBuffer1[SAMPLING_BUFFER_SIZE * 2], displayBuffer2[SAMPLING_BUFFER_SIZE * 2]; -static int32_t bytesSampled, samplingBufferBytes; +static int16_t previewBufL[2][PREVIEW_SAMPLES], previewBufR[2][PREVIEW_SAMPLES]; +static int32_t samplesSampled, samplingBufferSize, currPreviewBufNum, oldPreviewSmpPos, currSampleLen; static uint32_t samplingRate; -static volatile int32_t currSampleLen; +static sample_t *smpL, *smpR; static SDL_AudioDeviceID recordDev; -static int16_t rightChSmpSlot = -1; -static void SDLCALL samplingCallback(void *userdata, Uint8 *stream, int len) +static void SDLCALL stereoSamplingCallback(void *userdata, Uint8 *stream, int len) { - if (instr[editor.curInstr] == NULL || len < 0 || len > samplingBufferBytes) + const int32_t samples = len >> 2; + if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize) return; - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - - int8_t *newPtr = (int8_t *)realloc(s->origPek, (s->len + len) + LOOP_FIX_LEN); - if (newPtr == NULL) + if (!reallocateSmpData(smpL, smpL->length + samples, true) || !reallocateSmpData(smpR, smpR->length + samples, true)) { drawSamplingBufferFlag = false; outOfMemoryFlag = true; return; } - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; + const int16_t *src16 = (int16_t *)stream; + int16_t *dst16_L = (int16_t *)smpL->dataPtr + smpL->length; + int16_t *dst16_R = (int16_t *)smpR->dataPtr + smpR->length; + + for (int32_t i = 0; i < samples; i++) + { + dst16_L[i] = *src16++; + dst16_R[i] = *src16++; + } - memcpy(&s->pek[s->len], stream, len); + smpL->length += samples; + smpR->length += samples; - s->len += len; - if (s->len > MAX_SAMPLE_LEN) // length overflow + if (smpL->length > MAX_SAMPLE_LEN) // length overflow { - s->len -= len; + smpL->length -= samples; + smpR->length -= samples; noMoreRoomFlag = true; return; } - bytesSampled += len; - if (bytesSampled >= samplingBufferBytes) + currSampleLen = smpL->length; + + // if we have gathared enough samples, fill the current display buffer + + samplesSampled += samples; + if (samplesSampled >= PREVIEW_SAMPLES) { - bytesSampled -= samplingBufferBytes; + samplesSampled -= PREVIEW_SAMPLES; - currSampleLen = s->len - samplingBufferBytes; + if (oldPreviewSmpPos > 0) + { + int16_t *dstL = previewBufL[currPreviewBufNum^1]; + int16_t *dstR = previewBufR[currPreviewBufNum^1]; + const int16_t *srcL = (int16_t *)smpL->dataPtr + oldPreviewSmpPos; + const int16_t *srcR = (int16_t *)smpR->dataPtr + oldPreviewSmpPos; - // fill display buffer - memcpy(currWriteBuf, &s->pek[currSampleLen], samplingBufferBytes); + memcpy(dstL, srcL, PREVIEW_SAMPLES * sizeof (int16_t)); + memcpy(dstR, srcR, PREVIEW_SAMPLES * sizeof (int16_t)); - // swap write buffer (double-buffering) - if (currWriteBuf == displayBuffer1) - currWriteBuf = displayBuffer2; - else - currWriteBuf = displayBuffer1; + drawSamplingBufferFlag = true; + } - drawSamplingBufferFlag = true; + oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES; } (void)userdata; } -void stopSampling(void) +static void SDLCALL monoSamplingCallback(void *userdata, Uint8 *stream, int len) { - int8_t *newPtr; - int16_t *dst16, *src16; - int32_t i, len; - - resumeAudio(); - mouseAnimOff(); - - SDL_CloseAudioDevice(recordDev); - editor.samplingAudioFlag = false; - - sampleTyp *currSmp = NULL; - sampleTyp *nextSmp = NULL; - - if (instr[editor.curInstr] != NULL) - currSmp = &instr[editor.curInstr]->samp[editor.curSmp]; + const int32_t samples = len >> 1; + if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize) + return; - if (sampleInStereo) + if (!reallocateSmpData(smpL, smpL->length + samples, true)) { - // read right channel data - - if (currSmp->pek != NULL && rightChSmpSlot != -1) - { - nextSmp = &instr[editor.curInstr]->samp[rightChSmpSlot]; - - nextSmp->origPek = (int8_t *)malloc((currSmp->len >> 1) + LOOP_FIX_LEN); - if (nextSmp->origPek != NULL) - { - nextSmp->pek = nextSmp->origPek + SMP_DAT_OFFSET; - nextSmp->len = currSmp->len >> 1; - - src16 = (int16_t *)currSmp->pek; - dst16 = (int16_t *)nextSmp->pek; - - len = nextSmp->len >> 1; - for (i = 0; i < len; i++) - dst16[i] = src16[(i << 1) + 1]; - } - else - { - freeSample(editor.curInstr, rightChSmpSlot); - } - - currSmp->len >>= 1; - - // read left channel data by skipping every other sample + drawSamplingBufferFlag = false; + outOfMemoryFlag = true; + return; + } - dst16 = (int16_t *)currSmp->pek; + const int16_t *src16 = (int16_t *)stream; + int16_t *dst16 = (int16_t *)smpL->dataPtr + smpL->length; + memcpy(dst16, src16, samples * sizeof (int16_t)); - len = currSmp->len >> 1; - for (i = 0; i < len; i++) - dst16[i] = dst16[i << 1]; - } + smpL->length += samples; + if (smpL->length > MAX_SAMPLE_LEN) // length overflow + { + smpL->length -= samples; + noMoreRoomFlag = true; + return; } - if (currSmp->origPek != NULL) + // if we have gathared enough samples, fill the current display buffer + + samplesSampled += samples; + if (samplesSampled >= PREVIEW_SAMPLES) { - newPtr = (int8_t *)realloc(currSmp->origPek, currSmp->len + LOOP_FIX_LEN); - if (newPtr != NULL) + samplesSampled -= PREVIEW_SAMPLES; + + if (oldPreviewSmpPos > 0) { - currSmp->origPek = newPtr; - currSmp->pek = currSmp->origPek + SMP_DAT_OFFSET; + int16_t *dst = previewBufL[currPreviewBufNum^1]; + const int16_t *src = (int16_t *)smpL->dataPtr + oldPreviewSmpPos; + memcpy(dst, src, PREVIEW_SAMPLES * sizeof (int16_t)); + + drawSamplingBufferFlag = true; } - fixSample(currSmp); - } - else - { - freeSample(editor.curInstr, editor.curSmp); + oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES; } - if (nextSmp != NULL && nextSmp->origPek != NULL) - fixSample(nextSmp); + currSampleLen = smpL->length; - updateSampleEditorSample(); - editor.updateCurInstr = true; + (void)userdata; } -static uint8_t getDispBuffPeakMono(const int16_t *smpData, int32_t smpNum) +void stopSampling(void) { - uint32_t max = 0; - for (int32_t i = 0; i < smpNum; i++) - { - const int16_t smp16 = smpData[i]; + resumeAudio(); + mouseAnimOff(); - const uint32_t smpAbs = ABS(smp16); - if (smpAbs > max) - max = smpAbs; - } + SDL_CloseAudioDevice(recordDev); - max = (max * SAMPLE_AREA_HEIGHT) >> 16; - if (max > 76) - max = 76; + if (smpL != NULL) fixSample(smpL); + if (smpR != NULL) fixSample(smpR); - return (uint8_t)max; + editor.samplingAudioFlag = false; + + updateSampleEditorSample(); + editor.updateCurInstr = true; } -static uint8_t getDispBuffPeakLeft(const int16_t *smpData, int32_t smpNum) +static void getMinMax16(const int16_t *p, uint32_t position, uint32_t scanLen, int16_t *min16, int16_t *max16) { - smpNum <<= 1; + int16_t minVal = 32767; + int16_t maxVal = -32768; - uint32_t max = 0; - for (int32_t i = 0; i < smpNum; i += 2) - { - const int16_t smp16 = smpData[i]; + assert(position+scanLen <= PREVIEW_SAMPLES); - const uint32_t smpAbs = ABS(smp16); - if (smpAbs > max) - max = smpAbs; + const int16_t *ptr16 = (const int16_t *)p + position; + for (uint32_t i = 0; i < scanLen; i++) + { + const int16_t smp16 = ptr16[i]; + if (smp16 < minVal) minVal = smp16; + if (smp16 > maxVal) maxVal = smp16; } - max = (max * SAMPLE_AREA_HEIGHT) >> (16 + 1); - if (max > 38) - max = 38; + *min16 = minVal; + *max16 = maxVal; +} - return (uint8_t)max; +static int32_t scr2BufPos(int32_t x) +{ + const double dXScaleMul = PREVIEW_SAMPLES / (double)SAMPLE_AREA_WIDTH; + return (int32_t)(x * dXScaleMul); } -static uint8_t getDispBuffPeakRight(const int16_t *smpData, int32_t smpNum) +static void drawSamplingPreview(void) { - smpNum <<= 1; + int16_t min, max; + + // clear sample data area + memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t)); - uint32_t max = 0; - for (int32_t i = 0; i < smpNum; i += 2) + if (sampleInStereo) // stereo sampling { - const int16_t smp16 = smpData[i]; + const int16_t *smpDataL = previewBufL[currPreviewBufNum]; + const int16_t *smpDataR = previewBufR[currPreviewBufNum]; - const uint32_t smpAbs = ABS(smp16); - if (smpAbs > max) - max = smpAbs; - } + int32_t oldMinL = SAMPLE_L_CENTER; + int32_t oldMaxL = SAMPLE_L_CENTER; + int32_t oldMinR = SAMPLE_R_CENTER; + int32_t oldMaxR = SAMPLE_R_CENTER; - max = (max * SAMPLE_AREA_HEIGHT) >> (16 + 1); - if (max > 38) - max = 38; + hLine(0, SAMPLE_L_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line + hLine(0, SAMPLE_R_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line - return (uint8_t)max; -} + for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) + { + int32_t smpIdx = scr2BufPos(x+0); + int32_t smpNum = scr2BufPos(x+1) - smpIdx; -static inline int32_t scrPos2SmpBufPos(int32_t x) // x = 0..SAMPLE_AREA_WIDTH -{ - return (x * ((SAMPLING_BUFFER_SIZE << 16) / SAMPLE_AREA_WIDTH)) >> 16; -} + if (smpIdx+smpNum > PREVIEW_SAMPLES) + smpNum = PREVIEW_SAMPLES-smpIdx; -static void drawSamplingPreview(void) -{ - uint8_t smpAbs; - int16_t *readBuf; - uint16_t x; - int32_t smpIdx, smpNum; - uint32_t *centerPtrL, *centerPtrR; + if (smpNum > 0) + { + // left channel + getMinMax16(smpDataL, smpIdx, smpNum, &min, &max); + min = SAMPLE_L_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16); + max = SAMPLE_L_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16); - const uint32_t pixVal = video.palette[PAL_PATTEXT]; + if (x != 0) + { + if (min > oldMaxL) sampleLine(x-1, x, oldMaxL, min); + if (max < oldMinL) sampleLine(x-1, x, oldMinL, max); + } - // select buffer currently not being written to (double-buffering) - if (currWriteBuf == displayBuffer1) - readBuf = displayBuffer2; - else - readBuf = displayBuffer1; + sampleLine(x, x, max, min); - if (sampleInStereo) - { - // stereo sampling + oldMinL = min; + oldMaxL = max; - const uint16_t centerL = SAMPLE_AREA_Y_CENTER - (SAMPLE_AREA_HEIGHT / 4); - const uint16_t centerR = SAMPLE_AREA_Y_CENTER + (SAMPLE_AREA_HEIGHT / 4); + // right channel + getMinMax16(smpDataR, smpIdx, smpNum, &min, &max); + min = SAMPLE_R_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16); + max = SAMPLE_R_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16); - centerPtrL = &video.frameBuffer[centerL*SCREEN_W]; - centerPtrR = &video.frameBuffer[centerR*SCREEN_W]; + if (x != 0) + { + if (min > oldMaxR) sampleLine(x-1, x, oldMaxR, min); + if (max < oldMinR) sampleLine(x-1, x, oldMinR, max); + } - for (x = 0; x < SAMPLE_AREA_WIDTH; x++) - { - smpIdx = scrPos2SmpBufPos(x); - smpNum = scrPos2SmpBufPos(x+1) - smpIdx; - - if (smpIdx+smpNum >= SAMPLING_BUFFER_SIZE) - smpNum = SAMPLING_BUFFER_SIZE - smpIdx; - - // left channel samples - smpAbs = getDispBuffPeakLeft(&readBuf[(smpIdx * 2) + 0], smpNum); - if (smpAbs == 0) - centerPtrL[x] = pixVal; - else - vLine(x, centerL - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT); - - // right channel samples - smpAbs = getDispBuffPeakRight(&readBuf[(smpIdx * 2) + 1], smpNum); - if (smpAbs == 0) - centerPtrR[x] = pixVal; - else - vLine(x, centerR - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT); + sampleLine(x, x, max, min); + + oldMinR = min; + oldMaxR = max; + } } } - else + else // mono sampling { - // mono sampling + const int16_t *smpData = previewBufL[currPreviewBufNum]; + + int32_t oldMin = SAMPLE_CENTER; + int32_t oldMax = SAMPLE_CENTER; - centerPtrL = &video.frameBuffer[SAMPLE_AREA_Y_CENTER * SCREEN_W]; + hLine(0, SAMPLE_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line - for (x = 0; x < SAMPLE_AREA_WIDTH; x++) + for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) { - smpIdx = scrPos2SmpBufPos(x); - smpNum = scrPos2SmpBufPos(x+1) - smpIdx; + int32_t smpIdx = scr2BufPos(x+0); + int32_t smpNum = scr2BufPos(x+1) - smpIdx; - if (smpIdx+smpNum >= SAMPLING_BUFFER_SIZE) - smpNum = SAMPLING_BUFFER_SIZE - smpIdx; + if (smpIdx+smpNum > PREVIEW_SAMPLES) + smpNum = PREVIEW_SAMPLES-smpIdx; + + if (smpNum > 0) + { + getMinMax16(smpData, smpIdx, smpNum, &min, &max); + min = SAMPLE_CENTER - ((min * MONO_SAMPLE_HEIGHT) >> 16); + max = SAMPLE_CENTER - ((max * MONO_SAMPLE_HEIGHT) >> 16); - smpAbs = getDispBuffPeakMono(&readBuf[smpIdx], smpNum); - if (smpAbs == 0) - centerPtrL[x] = pixVal; - else - vLine(x, SAMPLE_AREA_Y_CENTER - smpAbs, (smpAbs * 2) + 1, PAL_PATTEXT); + if (x != 0) + { + if (min > oldMax) sampleLine(x-1, x, oldMax, min); + if (max < oldMin) sampleLine(x-1, x, oldMin, max); + } + + sampleLine(x, x, max, min); + + oldMin = min; + oldMax = max; + } } } } @@ -315,19 +314,13 @@ void handleSamplingUpdates(void) { drawSamplingBufferFlag = false; - // clear sample data area - memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t)); - drawSamplingPreview(); - - // clear and draw new sample length number - fillRect(536, 362, 56, 10, PAL_DESKTOP); - - if (sampleInStereo) - hexOut(536, 362, PAL_FORGRND, currSampleLen >> 1, 8); - else - hexOut(536, 362, PAL_FORGRND, currSampleLen, 8); + currPreviewBufNum ^= 1; } + + // clear and draw new sample length number + fillRect(536, 362, 56, 10, PAL_DESKTOP); + hexOut(536, 362, PAL_FORGRND, currSampleLen, 8); } void startSampling(void) @@ -346,23 +339,20 @@ void startSampling(void) return; sampleInStereo = (result == 2); - samplingBufferBytes = sampleInStereo ? (SAMPLING_BUFFER_SIZE * 4) : (SAMPLING_BUFFER_SIZE * 2); - mouseAnimOn(); switch (config.audioInputFreq) { - case INPUT_FREQ_96KHZ: samplingRate = 96000; break; - case INPUT_FREQ_44KHZ: samplingRate = 44100; break; - default: samplingRate = 48000; break; + case INPUT_FREQ_44KHZ: samplingRate = 44100; break; + default: case INPUT_FREQ_48KHZ: samplingRate = 48000; break; + case INPUT_FREQ_96KHZ: samplingRate = 96000; break; } memset(&want, 0, sizeof (SDL_AudioSpec)); want.freq = samplingRate; want.format = AUDIO_S16; - want.channels = 1 + sampleInStereo; - want.callback = samplingCallback; - want.userdata = NULL; + want.channels = sampleInStereo ? 2 : 1; + want.callback = sampleInStereo ? stereoSamplingCallback : monoSamplingCallback; want.samples = SAMPLING_BUFFER_SIZE; recordDev = SDL_OpenAudioDevice(audio.currInputDevice, true, &want, &have, 0); @@ -372,63 +362,60 @@ void startSampling(void) return; } + samplingRate = have.freq; + samplingBufferSize = have.samples; + pauseAudio(); if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) { - resumeAudio(); + stopSampling(); okBox(0, "System message", "Not enough memory!"); return; } - sampleTyp *s = &instr[editor.curInstr]->samp[editor.curSmp]; - - // wipe current sample and prepare it - freeSample(editor.curInstr, editor.curSmp); - s->typ |= 16; // we always sample in 16-bit + if (sampleInStereo && editor.curSmp+1 >= MAX_SMP_PER_INST) + { + stopSampling(); + okBox(0, "System message", "Error: No free sample slot for the right channel!"); + return; + } - tuneSample(s, samplingRate, audio.linearPeriodsFlag); // tune sample (relTone/finetune) to the sampling frequency we obtained + smpL = &instr[editor.curInstr]->smp[editor.curSmp]; + freeSample(editor.curInstr, editor.curSmp); // also sets pan to 128 and vol to 64 + tuneSample(smpL, samplingRate, audio.linearPeriodsFlag); + smpL->flags |= SAMPLE_16BIT; if (sampleInStereo) { - strcpy(s->name, "Left sample"); - s->pan = 0; - - if (editor.curSmp+1 < MAX_SMP_PER_INST) - rightChSmpSlot = editor.curSmp+1; - else - rightChSmpSlot = -1; + smpR = &instr[editor.curInstr]->smp[editor.curSmp+1]; + freeSample(editor.curInstr, editor.curSmp+1); // also sets pan to 128 and vol to 64 + tuneSample(smpR, samplingRate, audio.linearPeriodsFlag); + smpR->flags |= SAMPLE_16BIT; - if (rightChSmpSlot != -1) - { - // wipe current sample and prepare it - freeSample(editor.curInstr, rightChSmpSlot); - sampleTyp *nextSmp = &instr[editor.curInstr]->samp[rightChSmpSlot]; + strcpy(smpL->name, "Left sample"); + smpL->panning = 0; - strcpy(nextSmp->name, "Right sample"); - nextSmp->typ |= 16; // we always sample in 16-bit - nextSmp->pan = 255; - - tuneSample(nextSmp, samplingRate, audio.linearPeriodsFlag); // tune sample (relTone/finetune) to the sampling frequency we obtained - } + strcpy(smpR->name, "Right sample"); + smpR->panning = 255; } else { - strcpy(s->name, "Mono-mixed sample"); + strcpy(smpL->name, "Mono sample"); } + memset(previewBufL, 0, sizeof (previewBufL)); + memset(previewBufR, 0, sizeof (previewBufR)); + currPreviewBufNum = 0; + + samplesSampled = currSampleLen = oldPreviewSmpPos = 0; + noMoreRoomFlag = outOfMemoryFlag = drawSamplingBufferFlag = false; + updateSampleEditorSample(); updateSampleEditor(); setSongModifiedFlag(); - currWriteBuf = displayBuffer1; - memset(displayBuffer1, 0, sizeof (displayBuffer1)); - memset(displayBuffer2, 0, sizeof (displayBuffer2)); - editor.samplingAudioFlag = true; - bytesSampled = 0; - currSampleLen = 0; - SDL_PauseAudioDevice(recordDev, false); #endif } diff --git a/src/ft2_scopedraw.c b/src/ft2_scopedraw.c @@ -1,402 +0,0 @@ -#include "ft2_scopes.h" -#include "ft2_scopedraw.h" -#include "ft2_video.h" -#include "ft2_palette.h" - -/* ----------------------------------------------------------------------- */ -/* SCOPE DRAWING MACROS */ -/* ----------------------------------------------------------------------- */ - -#define SCOPE_REGS_NO_LOOP \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ - int32_t sample; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - -#define SCOPE_REGS_LOOP \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ - int32_t sample; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - -#define SCOPE_REGS_PINGPONG \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t color = video.palette[PAL_PATTEXT]; \ - const uint32_t width = x + w; \ - int32_t sample; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - int32_t direction = s->direction; \ - -#define LINED_SCOPE_REGS_NO_LOOP \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t width = (x + w) - 1; \ - int32_t sample; \ - int32_t y1, y2; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - -#define LINED_SCOPE_REGS_LOOP \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t width = (x + w) - 1; \ - int32_t sample; \ - int32_t y1, y2; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - -#define LINED_SCOPE_REGS_PINGPONG \ - const int32_t vol = s->vol; \ - const int32_t end = s->end; \ - const int32_t loopStart = s->loopStart; \ - const int32_t loopLength = s->loopLength; \ - const uint32_t delta = s->drawDelta; \ - const uint32_t width = (x + w) - 1; \ - int32_t sample; \ - int32_t y1, y2; \ - int32_t pos = s->pos; \ - int32_t posFrac = 0; \ - int32_t direction = s->direction; \ - -#define SCOPE_GET_SMP8 \ - if (s->active) \ - { \ - assert(pos >= 0 && pos < end); \ - sample = (s->base8[pos] * vol) >> 8; \ - } \ - else \ - { \ - sample = 0; \ - } \ - -#define SCOPE_GET_SMP16 \ - if (s->active) \ - { \ - assert(pos >= 0 && pos < end); \ - sample = (int8_t)((s->base16[pos] * vol) >> 16); \ - } \ - else \ - { \ - sample = 0; \ - } \ - -#define SCOPE_UPDATE_DRAWPOS \ - posFrac += delta; \ - pos += posFrac >> SCOPE_DRAW_FRAC_BITS; \ - posFrac &= SCOPE_DRAW_FRAC_MASK; \ - -#define SCOPE_UPDATE_DRAWPOS_PINGPONG \ - posFrac += delta; \ - pos += (int32_t)(posFrac >> SCOPE_DRAW_FRAC_BITS) * direction; \ - posFrac &= SCOPE_DRAW_FRAC_MASK; \ - -#define SCOPE_DRAW_SMP \ - video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = color; - -#define LINED_SCOPE_PREPARE_SMP8 \ - SCOPE_GET_SMP8 \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS - -#define LINED_SCOPE_PREPARE_SMP16 \ - SCOPE_GET_SMP16 \ - y1 = lineY - sample; \ - SCOPE_UPDATE_DRAWPOS - -#define LINED_SCOPE_DRAW_SMP \ - y2 = lineY - sample; \ - scopeLine(x, y1, y2); \ - y1 = y2; \ - -#define SCOPE_HANDLE_POS_NO_LOOP \ - if (pos >= end) \ - s->active = false; \ - -#define SCOPE_HANDLE_POS_LOOP \ - if (pos >= end) \ - { \ - if (loopLength >= 2) \ - pos = loopStart + ((pos - end) % loopLength); \ - else \ - pos = loopStart; \ - \ - assert(pos >= loopStart && pos < end); \ - } \ - -#define SCOPE_HANDLE_POS_PINGPONG \ - if (direction == -1 && pos < loopStart) \ - { \ - direction = 1; /* change direction to forwards */ \ - \ - if (loopLength >= 2) \ - pos = loopStart + ((loopStart - pos - 1) % loopLength); \ - else \ - pos = loopStart; \ - \ - assert(pos >= loopStart && pos < end); \ - } \ - else if (pos >= end) \ - { \ - direction = -1; /* change direction to backwards */ \ - \ - if (loopLength >= 2) \ - pos = (end - 1) - ((pos - end) % loopLength); \ - else \ - pos = end - 1; \ - \ - assert(pos >= loopStart && pos < end); \ - } \ - assert(pos >= 0); \ - -static void scopeLine(int32_t x1, int32_t y1, int32_t y2) -{ - const int32_t dy = y2 - y1; - const int32_t sy = SGN(dy); - const uint32_t color = video.palette[PAL_PATTEXT]; - const int32_t pitch = sy * SCREEN_W; - - uint32_t *dst32 = &video.frameBuffer[(y1 * SCREEN_W) + x1]; - - *dst32 = color; // set first pixel - - int32_t ay = ABS(dy); - if (ay <= 1) - { - if (ay != 0) - dst32 += pitch; - - *++dst32 = color; - return; - } - - int32_t d = 1 - ay; - - ay <<= 1; - while (y1 != y2) - { - if (d >= 0) - { - d -= ay; - dst32++; - } - - y1 += sy; - d += 2; - - dst32 += pitch; - *dst32 = color; - } -} - -/* ----------------------------------------------------------------------- */ -/* SCOPE DRAWING ROUTINES */ -/* ----------------------------------------------------------------------- */ - -static void scopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_NO_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_NO_LOOP - } -} - -static void scopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_LOOP - } -} - -static void scopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_PINGPONG - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG - } -} - -static void scopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_NO_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_NO_LOOP - } -} - -static void scopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_LOOP - } -} - -static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - SCOPE_REGS_PINGPONG - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG - } -} - -/* ----------------------------------------------------------------------- */ -/* LINED SCOPE DRAWING ROUTINES */ -/* ----------------------------------------------------------------------- */ - -static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_NO_LOOP - LINED_SCOPE_PREPARE_SMP8 - SCOPE_HANDLE_POS_NO_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_NO_LOOP - } -} - -static void linedScopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_LOOP - LINED_SCOPE_PREPARE_SMP8 - SCOPE_HANDLE_POS_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_LOOP - } -} - -static void linedScopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_PINGPONG - LINED_SCOPE_PREPARE_SMP8 - SCOPE_HANDLE_POS_PINGPONG - - for (; x < width; x++) - { - SCOPE_GET_SMP8 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG - } -} - -static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_NO_LOOP - LINED_SCOPE_PREPARE_SMP16 - SCOPE_HANDLE_POS_NO_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_NO_LOOP - } -} - -static void linedScopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_LOOP - LINED_SCOPE_PREPARE_SMP16 - SCOPE_HANDLE_POS_LOOP - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS - SCOPE_HANDLE_POS_LOOP - } -} - -static void linedScopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) -{ - LINED_SCOPE_REGS_PINGPONG - LINED_SCOPE_PREPARE_SMP16 - SCOPE_HANDLE_POS_PINGPONG - - for (; x < width; x++) - { - SCOPE_GET_SMP16 - LINED_SCOPE_DRAW_SMP - SCOPE_UPDATE_DRAWPOS_PINGPONG - SCOPE_HANDLE_POS_PINGPONG - } -} - -// ----------------------------------------------------------------------- - -const scopeDrawRoutine scopeDrawRoutineTable[12] = -{ - (scopeDrawRoutine)scopeDrawNoLoop_8bit, - (scopeDrawRoutine)scopeDrawLoop_8bit, - (scopeDrawRoutine)scopeDrawPingPong_8bit, - (scopeDrawRoutine)scopeDrawNoLoop_16bit, - (scopeDrawRoutine)scopeDrawLoop_16bit, - (scopeDrawRoutine)scopeDrawPingPong_16bit, - (scopeDrawRoutine)linedScopeDrawNoLoop_8bit, - (scopeDrawRoutine)linedScopeDrawLoop_8bit, - (scopeDrawRoutine)linedScopeDrawPingPong_8bit, - (scopeDrawRoutine)linedScopeDrawNoLoop_16bit, - (scopeDrawRoutine)linedScopeDrawLoop_16bit, - (scopeDrawRoutine)linedScopeDrawPingPong_16bit -}; diff --git a/src/ft2_scopes.c b/src/ft2_scopes.c @@ -1,641 +0,0 @@ -// for finding memory leaks in debug mode with Visual Studio -#if defined _DEBUG && defined _MSC_VER -#include <crtdbg.h> -#endif - -#include <stdint.h> -#include <stdbool.h> -#include <math.h> // modf() -#ifndef _WIN32 -#include <unistd.h> // usleep() -#endif -#include "ft2_header.h" -#include "ft2_events.h" -#include "ft2_config.h" -#include "ft2_audio.h" -#include "ft2_gui.h" -#include "ft2_midi.h" -#include "ft2_bmp.h" -#include "ft2_scopes.h" -#include "ft2_mouse.h" -#include "ft2_video.h" -#include "ft2_scopedraw.h" -#include "ft2_tables.h" -#include "ft2_structs.h" - -static volatile bool scopesUpdatingFlag, scopesDisplayingFlag; -static uint32_t scopeTimeLen, scopeTimeLenFrac; -static uint64_t timeNext64, timeNext64Frac; -static volatile scope_t scope[MAX_VOICES]; -static SDL_Thread *scopeThread; - -lastChInstr_t lastChInstr[MAX_VOICES]; // global - -void resetCachedScopeVars(void) -{ - volatile scope_t *sc = scope; - for (int32_t i = 0; i < MAX_VOICES; i++, sc++) - { - sc->dOldHz = -1.0; - sc->oldDelta = 0; - sc->oldDrawDelta = 0; - } -} - -int32_t getSamplePosition(uint8_t ch) -{ - if (ch >= song.antChn) - return -1; - - volatile scope_t *sc = &scope[ch]; - - // cache some stuff - volatile bool active = sc->active; - volatile int32_t pos = sc->pos; - volatile int32_t end = sc->end; - volatile bool sampleIs16Bit = sc->sampleIs16Bit; - - if (!active || end == 0) - return -1; - - if (pos >= 0 && pos < end) - { - if (sampleIs16Bit) - pos <<= 1; - - return pos; - } - - return -1; // not active or overflown -} - -void stopAllScopes(void) -{ - // wait for scopes to finish updating - while (scopesUpdatingFlag); - - volatile scope_t *sc = scope; - for (int32_t i = 0; i < MAX_VOICES; i++, sc++) - sc->active = false; - - // wait for scope displaying to be done (safety) - while (scopesDisplayingFlag); -} - -// toggle mute -static void setChannel(int32_t nr, bool on) -{ - stmTyp *ch = &stm[nr]; - - ch->stOff = !on; - if (ch->stOff) - { - ch->effTyp = 0; - ch->eff = 0; - ch->realVol = 0; - ch->outVol = 0; - ch->oldVol = 0; - ch->dFinalVol = 0.0; - ch->outPan = 128; - ch->oldPan = 128; - ch->finalPan = 128; - ch->status = IS_Vol; - - ch->envSustainActive = false; // non-FT2 bug fix for stuck piano keys - } - - scope[nr].wasCleared = false; -} - -static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t channel, bool outline) -{ - scopeXOffs++; - scopeYOffs++; - channel++; - - if (outline) - { - if (channel < 10) // one digit? - { - charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + channel); - } - else - { - charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[channel]); - charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[channel]); - } - } - else - { - if (channel < 10) // one digit? - { - charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + channel); - } - else - { - charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[channel]); - charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[channel]); - } - } -} - -static void redrawScope(int32_t ch) -{ - int32_t i; - - int32_t chansPerRow = (uint32_t)song.antChn >> 1; - int32_t chanLookup = chansPerRow - 1; - const uint16_t *scopeLens = scopeLenTab[chanLookup]; - - // get x,y,len for scope according to channel (we must do it this way since 'len' can differ!) - - uint16_t x = 2; - uint16_t y = 94; - - uint16_t scopeLen = 0; // prevent compiler warning - for (i = 0; i < song.antChn; i++) - { - scopeLen = scopeLens[i]; - - if (i == chansPerRow) // did we reach end of row? - { - // yes, go one row down - x = 2; - y += 39; - } - - if (i == ch) - break; - - // adjust position to next channel - x += scopeLen + 3; - } - - drawFramework(x, y, scopeLen + 2, 38, FRAMEWORK_TYPE2); - - // draw mute graphics if channel is muted - if (!editor.chnMode[i]) - { - const uint16_t muteGfxLen = scopeMuteBMP_Widths[chanLookup]; - const uint16_t muteGfxX = x + ((scopeLen - muteGfxLen) >> 1); - - blitFastClipX(muteGfxX, y + 6, bmp.scopeMute+scopeMuteBMP_Offs[chanLookup], 162, scopeMuteBMP_Heights[chanLookup], muteGfxLen); - - if (config.ptnChnNumbers) - drawScopeNumber(x + 1, y + 1, (uint8_t)i, true); - } - - scope[ch].wasCleared = false; -} - -void refreshScopes(void) -{ - for (int32_t i = 0; i < MAX_VOICES; i++) - scope[i].wasCleared = false; -} - -static void channelMode(int32_t chn) -{ - int32_t i; - - assert(chn < song.antChn); - - bool m = mouse.leftButtonPressed && !mouse.rightButtonPressed; - bool m2 = mouse.rightButtonPressed && mouse.leftButtonPressed; - - if (m2) - { - bool test = false; - for (i = 0; i < song.antChn; i++) - { - if (i != chn && !editor.chnMode[i]) - test = true; - } - - if (test) - { - for (i = 0; i < song.antChn; i++) - editor.chnMode[i] = true; - } - else - { - for (i = 0; i < song.antChn; i++) - editor.chnMode[i] = (i == chn); - } - } - else if (m) - { - editor.chnMode[chn] ^= 1; - } - else - { - if (editor.chnMode[chn]) - { - config.multiRecChn[chn] ^= 1; - } - else - { - config.multiRecChn[chn] = true; - editor.chnMode[chn] = true; - m = true; - } - } - - for (i = 0; i < song.antChn; i++) - setChannel(i, editor.chnMode[i]); - - if (m2) - { - for (i = 0; i < song.antChn; i++) - redrawScope(i); - } - else - { - redrawScope(chn); - } -} - -bool testScopesMouseDown(void) -{ - int32_t i; - - if (!ui.scopesShown) - return false; - - if (mouse.y >= 95 && mouse.y <= 169 && mouse.x >= 3 && mouse.x <= 288) - { - if (mouse.y > 130 && mouse.y < 134) - return true; - - int32_t chansPerRow = (uint32_t)song.antChn >> 1; - const uint16_t *scopeLens = scopeLenTab[chansPerRow-1]; - - // find out if we clicked inside a scope - uint16_t x = 3; - for (i = 0; i < chansPerRow; i++) - { - if (mouse.x >= x && mouse.x < x+scopeLens[i]) - break; - - x += scopeLens[i]+3; - } - - if (i == chansPerRow) - return true; // scope framework was clicked instead - - int32_t chanToToggle = i; - if (mouse.y >= 134) // second row of scopes? - chanToToggle += chansPerRow; // yes, increase lookup offset - - channelMode(chanToToggle); - return true; - } - - return false; -} - -static void scopeTrigger(int32_t ch, const sampleTyp *s, int32_t playOffset) -{ - scope_t tempState; - - volatile scope_t *sc = &scope[ch]; - - int32_t length = s->len; - int32_t loopStart = s->repS; - int32_t loopLength = s->repL; - int32_t loopEnd = s->repS + s->repL; - uint8_t loopType = s->typ & 3; - bool sampleIs16Bit = (s->typ >> 4) & 1; - - if (sampleIs16Bit) - { - assert(!(length & 1)); - assert(!(loopStart & 1)); - assert(!(loopLength & 1)); - assert(!(loopEnd & 1)); - - length >>= 1; - loopStart >>= 1; - loopLength >>= 1; - loopEnd >>= 1; - } - - if (s->pek == NULL || length < 1) - { - sc->active = false; // shut down scope (illegal parameters) - return; - } - - if (loopLength < 1) // disable loop if loopLength is below 1 - loopType = 0; - - if (sampleIs16Bit) - tempState.base16 = (const int16_t *)s->pek; - else - tempState.base8 = s->pek; - - tempState.sampleIs16Bit = sampleIs16Bit; - tempState.loopType = loopType; - - tempState.direction = 1; // forwards - tempState.end = (loopType > 0) ? loopEnd : length; - tempState.loopStart = loopStart; - tempState.loopLength = loopLength; - tempState.pos = playOffset; - tempState.posFrac = 0; - - // if position overflows (f.ex. through 9xx command), shut down scopes - if (tempState.pos >= tempState.end) - { - sc->active = false; - return; - } - - // these has to be copied so that they are not lost - tempState.wasCleared = sc->wasCleared; - tempState.delta = sc->delta; - tempState.oldDelta = sc->oldDelta; - tempState.drawDelta = sc->drawDelta; - tempState.oldDrawDelta = sc->oldDrawDelta; - tempState.dOldHz = sc->dOldHz; - tempState.vol = sc->vol; - - tempState.active = true; - - /* Update live scope now. - ** In theory it -can- be written to in the middle of a cached read, - ** then the read thread writes its own non-updated cached copy back and - ** the trigger never happens. So far I have never seen it happen, - ** so it's probably very rare. Yes, this is not good coding... - */ - - *sc = tempState; -} - -static void updateScopes(void) -{ - int32_t loopOverflowVal; - - scopesUpdatingFlag = true; - - volatile scope_t *sc = scope; - for (int32_t i = 0; i < song.antChn; i++, sc++) - { - scope_t s = *sc; // cache it - if (!s.active) - continue; // scope is not active, no need - - // scope position update - - s.posFrac += s.delta; - const int32_t wholeSamples = s.posFrac >> 32; - s.posFrac &= 0xFFFFFFFF; - - if (s.direction == 1) - s.pos += wholeSamples; // forwards - else - s.pos -= wholeSamples; // backwards - - // handle loop wrapping or sample end - if (s.direction == -1 && s.pos < s.loopStart) // sampling backwards (definitely pingpong loop) - { - s.direction = 1; // change direction to forwards - - if (s.loopLength >= 2) - s.pos = s.loopStart + ((s.loopStart - s.pos - 1) % s.loopLength); - else - s.pos = s.loopStart; - - assert(s.pos >= s.loopStart && s.pos < s.end); - } - else if (s.pos >= s.end) - { - if (s.loopLength >= 2) - loopOverflowVal = (s.pos - s.end) % s.loopLength; - else - loopOverflowVal = 0; - - if (s.loopType == LOOP_DISABLED) - { - s.active = false; - } - else if (s.loopType == LOOP_FORWARD) - { - s.pos = s.loopStart + loopOverflowVal; - assert(s.pos >= s.loopStart && s.pos < s.end); - } - else // pingpong loop - { - s.direction = -1; // change direction to backwards - s.pos = (s.end - 1) - loopOverflowVal; - assert(s.pos >= s.loopStart && s.pos < s.end); - } - } - assert(s.pos >= 0); - - *sc = s; // update scope state - } - scopesUpdatingFlag = false; -} - -void drawScopes(void) -{ - scopesDisplayingFlag = true; - int32_t chansPerRow = (uint32_t)song.antChn >> 1; - - const uint16_t *scopeLens = scopeLenTab[chansPerRow-1]; - uint16_t scopeXOffs = 3; - uint16_t scopeYOffs = 95; - int16_t scopeLineY = 112; - - for (int32_t i = 0; i < song.antChn; i++) - { - // if we reached the last scope on the row, go to first scope on the next row - if (i == chansPerRow) - { - scopeXOffs = 3; - scopeYOffs = 134; - scopeLineY = 151; - } - - const uint16_t scopeDrawLen = scopeLens[i]; - if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere) - { - scopeXOffs += scopeDrawLen+3; // align x to next scope - continue; - } - - const scope_t s = scope[i]; // cache scope to lower thread race condition issues - if (s.active && s.vol > 0 && !audio.locked) - { - // scope is active - scope[i].wasCleared = false; - - // clear scope background - clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT); - - // draw scope - bool linedScopesFlag = !!(config.specialFlags & LINED_SCOPES); - scopeDrawRoutineTable[(linedScopesFlag * 6) + (s.sampleIs16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen); - } - else - { - // scope is inactive - volatile scope_t *sc = &scope[i]; - if (!sc->wasCleared) - { - // clear scope background - clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT); - - // draw empty line - hLine(scopeXOffs, scopeLineY, scopeDrawLen, PAL_PATTEXT); - - sc->wasCleared = true; - } - } - - // draw channel numbering (if enabled) - if (config.ptnChnNumbers) - drawScopeNumber(scopeXOffs, scopeYOffs, (uint8_t)i, false); - - // draw rec. symbol (if enabled) - if (config.multiRecChn[i]) - blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4); - - scopeXOffs += scopeDrawLen+3; // align x to next scope - } - - scopesDisplayingFlag = false; -} - -void drawScopeFramework(void) -{ - drawFramework(0, 92, 291, 81, FRAMEWORK_TYPE1); - for (int32_t i = 0; i < song.antChn; i++) - redrawScope(i); -} - -void handleScopesFromChQueue(chSyncData_t *chSyncData, uint8_t *scopeUpdateStatus) -{ - volatile scope_t *sc = scope; - syncedChannel_t *ch = chSyncData->channels; - for (int32_t i = 0; i < song.antChn; i++, sc++, ch++) - { - const uint8_t status = scopeUpdateStatus[i]; - - if (status & IS_Vol) - sc->vol = ((ch->vol * SCOPE_HEIGHT) + 128) >> 8; // rounded - - if (status & IS_Period) - { - // use cached values when possible - if (ch->dHz != sc->dOldHz) - { - sc->dOldHz = ch->dHz; - - const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ; - sc->oldDelta = (int64_t)((ch->dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> 32.32fp delta (rounded) - - const double dRelativeHz = ch->dHz * (1.0 / (8363.0 / 2.0)); - sc->oldDrawDelta = (int32_t)((dRelativeHz * SCOPE_DRAW_FRAC_SCALE) + 0.5); // Hz -> 13.19fp draw delta (rounded) - } - - sc->delta = sc->oldDelta; - sc->drawDelta = sc->oldDrawDelta; - } - - if (status & IS_NyTon) - { - if (instr[ch->instrNr] != NULL) - { - scopeTrigger(i, &instr[ch->instrNr]->samp[ch->sampleNr], ch->smpStartPos); - - // set some stuff used by Smp. Ed. for sampling position line - - if (ch->instrNr == 130 || (ch->instrNr == editor.curInstr && ch->sampleNr == editor.curSmp)) - editor.curSmpChannel = (uint8_t)i; - - lastChInstr[i].instrNr = ch->instrNr; - lastChInstr[i].sampleNr = ch->sampleNr; - } - else - { - // empty instrument, shut down scope - scope[i].active = false; - lastChInstr[i].instrNr = 255; - lastChInstr[i].sampleNr = 255; - } - } - } -} - -static int32_t SDLCALL scopeThreadFunc(void *ptr) -{ - // this is needed for scope stability (confirmed) - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); - - // set next frame time - timeNext64 = SDL_GetPerformanceCounter() + scopeTimeLen; - timeNext64Frac = scopeTimeLenFrac; - - while (editor.programRunning) - { - editor.scopeThreadMutex = true; - updateScopes(); - editor.scopeThreadMutex = false; - - uint64_t time64 = SDL_GetPerformanceCounter(); - if (time64 < timeNext64) - { - time64 = timeNext64 - time64; - if (time64 > INT32_MAX) - time64 = INT32_MAX; - - const int32_t diff32 = (int32_t)time64; - - // convert and round to microseconds - const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5); - - // delay until we have reached the next frame - if (time32 > 0) - usleep(time32); - } - - // update next tick time - timeNext64 += scopeTimeLen; - timeNext64Frac += scopeTimeLenFrac; - if (timeNext64Frac > UINT32_MAX) - { - timeNext64Frac &= UINT32_MAX; - timeNext64++; - } - } - - (void)ptr; - return true; -} - -bool initScopes(void) -{ - double dInt; - - // calculate scope time for performance counters and split into int/frac - double dFrac = modf(editor.dPerfFreq / SCOPE_HZ, &dInt); - - // integer part - scopeTimeLen = (int32_t)dInt; - - // fractional part (scaled to 0..2^32-1) - dFrac *= UINT32_MAX+1.0; - scopeTimeLenFrac = (uint32_t)dFrac; - - scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL); - if (scopeThread == NULL) - { - showErrorMsgBox("Couldn't create channel scope thread!"); - return false; - } - - SDL_DetachThread(scopeThread); - return true; -} diff --git a/src/ft2_scopes.h b/src/ft2_scopes.h @@ -1,48 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <stdbool.h> -#include "ft2_header.h" -#include "ft2_audio.h" - -#define SCOPE_HEIGHT 36 - -#define SCOPE_FRAC_BITS 32 -#define SCOPE_FRAC_SCALE (1ULL << SCOPE_FRAC_BITS) -#define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1) - -// *absolute* max safe bits (Amiga periods, period 1), don't mess with it! -#define SCOPE_DRAW_FRAC_BITS 19 -#define SCOPE_DRAW_FRAC_SCALE (1UL << SCOPE_DRAW_FRAC_BITS) -#define SCOPE_DRAW_FRAC_MASK (SCOPE_DRAW_FRAC_SCALE-1) - -void resetCachedScopeVars(void); -int32_t getSamplePosition(uint8_t ch); -void stopAllScopes(void); -void refreshScopes(void); -bool testScopesMouseDown(void); -void drawScopes(void); -void drawScopeFramework(void); -bool initScopes(void); - -// actual scope data -typedef struct scope_t -{ - volatile bool active; - const int8_t *base8; - const int16_t *base16; - bool wasCleared, sampleIs16Bit; - uint8_t loopType; - int32_t vol, loopStart, loopLength, end, pos, direction; - uint32_t drawDelta, oldDrawDelta; - uint64_t delta, oldDelta, posFrac; - - double dOldHz; -} scope_t; - -typedef struct lastChInstr_t -{ - uint8_t sampleNr, instrNr; -} lastChInstr_t; - -extern lastChInstr_t lastChInstr[MAX_VOICES]; diff --git a/src/ft2_structs.h b/src/ft2_structs.h @@ -22,16 +22,16 @@ typedef struct editor_t bool autoPlayOnDrop, trimThreadWasDone, throwExit, editTextFlag; bool copyMaskEnable, diskOpReadOnOpen, samplingAudioFlag, editSampleFlag; - bool instrBankSwapped, chnMode[MAX_VOICES], NI_Play; + bool instrBankSwapped, chnMode[MAX_CHANNELS], NI_Play; uint8_t curPlayInstr, curPlaySmp, curSmpChannel, currPanEnvPoint, currVolEnvPoint; uint8_t copyMask[5], pasteMask[5], transpMask[5], smpEd_NoteNr, instrBankOffset, sampleBankOffset; uint8_t srcInstr, curInstr, srcSmp, curSmp, currHelpScreen, currConfigScreen, textCursorBlinkCounter; - uint8_t keyOnTab[MAX_VOICES], ID_Add, curOctave; + uint8_t keyOnTab[MAX_CHANNELS], editRowSkip, curOctave; uint8_t sampleSaveMode, moduleSaveMode, ptnJumpPos[4]; - int16_t globalVol, songPos, pattPos; - uint16_t tmpPattern, editPattern, speed, tempo, timer, ptnCursorY; - int32_t keyOffNr, keyOffTime[MAX_VOICES]; + int16_t globalVolume, songPos, row; + uint16_t tmpPattern, editPattern, BPM, speed, tick, ptnCursorY; + int32_t keyOffNr, keyOffTime[MAX_CHANNELS]; uint32_t framesPassed, wavRendererTime; double dPerfFreq, dPerfFreqMulMicro, dPerfFreqMulMs; } editor_t; diff --git a/src/ft2_sysreqs.c b/src/ft2_sysreqs.c @@ -54,7 +54,7 @@ static SDL_Keycode shortCut[NUM_SYSREQ_TYPES][5] = typedef struct quitType_t { const char *text; - uint8_t typ; + uint8_t type; } quitType_t; #define QUIT_MESSAGES 11 @@ -180,7 +180,7 @@ static bool mouseButtonUpLogic(uint8_t mouseButton) } // WARNING: This routine must ONLY be called from the main input/video thread! -int16_t okBox(int16_t typ, const char *headline, const char *text) +int16_t okBox(int16_t type, const char *headline, const char *text) { #define PUSHBUTTON_W 80 @@ -207,9 +207,9 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) int16_t oldLastUsedObjectType = mouse.lastUsedObjectType; // count number of buttons - uint16_t knp = 0; - while (buttonText[typ][knp][0] != '\0' && knp < 5) - knp++; + uint16_t numButtons = 0; + while (buttonText[type][numButtons][0] != '\0' && numButtons < 5) + numButtons++; uint16_t tlen = textWidth(text); uint16_t hlen = textWidth(headline); @@ -218,7 +218,7 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) if (hlen > tlen) wlen = hlen; - uint16_t tx = (knp * 100) - 20; + uint16_t tx = (numButtons * 100) - 20; if (tx > wlen) wlen = tx; @@ -234,7 +234,7 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) uint16_t y = ui.extended ? SYSTEM_REQUEST_Y_EXT : SYSTEM_REQUEST_Y; // set up buttons - for (i = 0; i < knp; i++) + for (i = 0; i < numButtons; i++) { p = &pushButtons[i]; @@ -242,12 +242,12 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) p->y = y + 42; p->w = PUSHBUTTON_W; p->h = 16; - p->caption = buttonText[typ][i]; + p->caption = buttonText[type][i]; p->visible = true; } // set up checkbox (special okBox types only!) - if (typ >= 6 && typ <= 7) + if (type >= 6 && type <= 7) { checkBox_t *c = &checkBoxes[0]; c->x = x + 5; @@ -256,12 +256,12 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) c->clickAreaHeight = 12; c->checked = false; - if (typ == 6) + if (type == 6) { // S3M load warning c->callbackFunc = configToggleImportWarning; } - else if (typ == 7) + else if (type == 7) { // "setting not yet applied" c->callbackFunc = configToggleNotYetAppliedWarning; @@ -312,9 +312,9 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) keyb.ignoreCurrKeyUp = true; // don't handle key up event for any keys that were pressed } - for (i = 0; i < knp; i++) + for (i = 0; i < numButtons; i++) { - if (shortCut[typ][i] == inputEvent.key.keysym.sym) + if (shortCut[type][i] == inputEvent.key.keysym.sym) { returnVal = i + 1; ui.sysReqShown = false; @@ -327,7 +327,7 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) { if (mouseButtonUpLogic(inputEvent.button.button)) { - if (typ >= 6 && typ <= 7) + if (type >= 6 && type <= 7) testCheckBoxMouseRelease(); returnVal = testPushButtonMouseRelease(false) + 1; @@ -360,8 +360,8 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) drawWindow(wlen); textOutShadow(headlineX, y + 4, PAL_FORGRND, PAL_BUTTON2, headline); textOutShadow(textX, y + 24, PAL_FORGRND, PAL_BUTTON2, text); - for (i = 0; i < knp; i++) drawPushButton(i); - if (typ >= 6 && typ <= 7) + for (i = 0; i < numButtons; i++) drawPushButton(i); + if (type >= 6 && type <= 7) { drawCheckBox(0); textOutShadow(x + 21, y + 52, PAL_FORGRND, PAL_BUTTON2, "Don't show again"); @@ -371,10 +371,10 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) endFPSCounter(); } - for (i = 0; i < knp; i++) + for (i = 0; i < numButtons; i++) hidePushButton(i); - if (typ >= 6 && typ <= 7) + if (type >= 6 && type <= 7) hideCheckBox(0); mouse.lastUsedObjectID = oldLastUsedObjectID; @@ -391,7 +391,7 @@ int16_t okBox(int16_t typ, const char *headline, const char *text) ** - This routine must ONLY be called from the main input/video thread!! ** - edText must be null-terminated */ -int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxStrLen) +int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen) { #define PUSHBUTTON_W 80 #define TEXTBOX_W 250 @@ -443,15 +443,15 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt uint16_t headlineX = (SCREEN_W - wlen) >> 1; // count number of buttons - uint16_t knp = 0; - while (buttonText[typ][knp][0] != '\0' && knp < 5) - knp++; + uint16_t numButtons = 0; + while (buttonText[type][numButtons][0] != '\0' && numButtons < 5) + numButtons++; uint16_t tx = TEXTBOX_W; if (tx > wlen) wlen = tx; - tx = (knp * 100) - 20; + tx = (numButtons * 100) - 20; if (tx > wlen) wlen = tx; @@ -470,13 +470,13 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt // setup buttons pushButton_t *p = pushButtons; - for (i = 0; i < knp; i++, p++) + for (i = 0; i < numButtons; i++, p++) { p->w = PUSHBUTTON_W; p->h = 16; p->x = ((SCREEN_W - tx) >> 1) + (i * 100); p->y = y + 42; - p->caption = buttonText[typ][i]; + p->caption = buttonText[type][i]; p->visible = true; } @@ -553,7 +553,7 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt } else { - for (i = 0; i < knp; i++) + for (i = 0; i < numButtons; i++) { if (shortCut[1][i] == inputEvent.key.keysym.sym) { @@ -604,7 +604,7 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt hLine(t->x, t->y + t->h, t->w + 1, PAL_BUTTON1); vLine(t->x + t->w, t->y, t->h, PAL_BUTTON1); drawTextBox(0); - for (i = 0; i < knp; i++) drawPushButton(i); + for (i = 0; i < numButtons; i++) drawPushButton(i); flipFrame(); endFPSCounter(); @@ -613,7 +613,7 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt editor.editTextFlag = false; SDL_StopTextInput(); - for (i = 0; i < knp; i++) + for (i = 0; i < numButtons; i++) hidePushButton(i); hideTextBox(0); @@ -630,7 +630,7 @@ int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxSt } // WARNING: This routine must NOT be called from the main input/video thread! -int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text) +int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text) { if (!editor.mainLoopOngoing) return 0; // main loop was not even started yet, bail out. @@ -639,7 +639,7 @@ int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text) while (okBoxData.active) SDL_Delay(1000 / VBLANK_HZ); - okBoxData.typ = typ; + okBoxData.type = type; okBoxData.headline = headline; okBoxData.text = text; okBoxData.active = true; @@ -653,7 +653,7 @@ int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text) static bool askQuit_RandomMsg(void) { uint8_t msg = rand() % QUIT_MESSAGES; - int16_t button = okBox(quitMessage[msg].typ, "System request", quitMessage[msg].text); + int16_t button = okBox(quitMessage[msg].type, "System request", quitMessage[msg].text); return (button == 1) ? true : false; } diff --git a/src/ft2_sysreqs.h b/src/ft2_sysreqs.h @@ -13,14 +13,14 @@ enum typedef struct okBoxData_t { volatile bool active; - int16_t typ, returnData; + int16_t type, returnData; const char *headline, *text; } okBoxData_t; -int16_t okBoxThreadSafe(int16_t typ, const char *headline, const char *text); -int16_t okBox(int16_t typ, const char *headline, const char *text); +int16_t okBoxThreadSafe(int16_t type, const char *headline, const char *text); +int16_t okBox(int16_t type, const char *headline, const char *text); int16_t quitBox(bool skipQuitMsg); -int16_t inputBox(int16_t typ, const char *headline, char *edText, uint16_t maxStrLen); +int16_t inputBox(int16_t type, const char *headline, char *edText, uint16_t maxStrLen); bool askUnsavedChanges(uint8_t type); void myLoaderMsgBoxThreadSafe(const char *fmt, ...); diff --git a/src/ft2_tables.c b/src/ft2_tables.c @@ -2,7 +2,7 @@ #include <stdbool.h> #include "ft2_palette.h" // pal16 typedef #include "ft2_pattern_ed.h" // pattCoord_t/pattCoord2_t/pattCoordsMouse_t/markCoord_t typedef -#include "ft2_header.h" // MAX_VOICES +#include "ft2_header.h" // MAX_CHANNELS #include "ft2_config.h" // CONFIG_FILE_SIZE #include "ft2_bmp.h" @@ -694,7 +694,7 @@ const uint8_t pattCursorWTab[2 * 4 * 8] = }; // these two are for channel numbering on pattern data/scopes -const char chDecTab1[MAX_VOICES+1] = +const char chDecTab1[MAX_CHANNELS+1] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', @@ -702,7 +702,7 @@ const char chDecTab1[MAX_VOICES+1] = '3', '3', '3' }; -const char chDecTab2[MAX_VOICES+1] = +const char chDecTab2[MAX_CHANNELS+1] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -872,176 +872,50 @@ const uint8_t defConfigData[CONFIG_FILE_SIZE] = /* ----------------------------------------------------------------------- */ /* -** const double dBpmMs1024 = 1024.0 / (bpm / 2.5); // milliseconds (scaled from 1000 to 1024) -** x = (uint64_t)floor((UINT32_MAX+1.0) * dBpmMs1024); +** for (int32_t bpm = 32; bpm <= 255; bpm++) +** { +** const double dBpmMs1024 = 1024.0 / (bpm / 2.5); // milliseconds (scaled from 1000 to 1024) +** uint64_t x = (uint64_t)floor((UINT32_MAX+1.0) * dBpmMs1024); +** } */ -const uint64_t musicTimeTab64[MAX_BPM+1] = -{ - 0x00000000000,0xA0000000000,0x50000000000,0x35555555555,0x28000000000,0x20000000000, - 0x1AAAAAAAAAA,0x16DB6DB6DB6,0x14000000000,0x11C71C71C71,0x10000000000,0x0E8BA2E8BA2, - 0x0D555555555,0x0C4EC4EC4EC,0x0B6DB6DB6DB,0x0AAAAAAAAAA,0x0A000000000,0x09696969696, - 0x08E38E38E38,0x086BCA1AF28,0x08000000000,0x079E79E79E7,0x0745D1745D1,0x06F4DE9BD37, - 0x06AAAAAAAAA,0x06666666666,0x06276276276,0x05ED097B425,0x05B6DB6DB6D,0x058469EE584, - 0x05555555555,0x05294A5294A,0x05000000000,0x04D9364D936,0x04B4B4B4B4B,0x04924924924, - 0x0471C71C71C,0x045306EB3E4,0x0435E50D794,0x041A41A41A4,0x04000000000,0x03E7063E706, - 0x03CF3CF3CF3,0x03B88EE23B8,0x03A2E8BA2E8,0x038E38E38E3,0x037A6F4DE9B,0x03677D46CEF, - 0x03555555555,0x0343EB1A1F5,0x03333333333,0x03232323232,0x0313B13B13B,0x0304D4873EC, - 0x02F684BDA12,0x02E8BA2E8BA,0x02DB6DB6DB6,0x02CE98B3A62,0x02C234F72C2,0x02B63CBEEA4, - 0x02AAAAAAAAA,0x029F79B4758,0x0294A5294A5,0x028A28A28A2,0x02800000000,0x02762762762, - 0x026C9B26C9B,0x026357E16EC,0x025A5A5A5A5,0x02519F89467,0x02492492492,0x0240E6C2B44, - 0x0238E38E38E,0x0231188C462,0x022983759F2,0x02222222222,0x021AF286BCA,0x0213F2B3884, - 0x020D20D20D2,0x02067B23A54,0x02000000000,0x01F9ADD3C0C,0x01F3831F383,0x01ED7E75346, - 0x01E79E79E79,0x01E1E1E1E1E,0x01DC47711DC,0x01D6CDFA1D6,0x01D1745D174,0x01CC398730E, - 0x01C71C71C71,0x01C21C21C21,0x01BD37A6F4D,0x01B86E1B86E,0x01B3BEA3677,0x01AF286BCA1, - 0x01AAAAAAAAA,0x01A6449E59B,0x01A1F58D0FA,0x019DBCC4867,0x01999999999,0x01958B67EBB, - 0x01919191919,0x018DAB7EC1D,0x0189D89D89D,0x01861861861,0x01826A439F6,0x017ECDC1CB5, - 0x017B425ED09,0x0177C7A20E1,0x01745D1745D,0x0171024E6A1,0x016DB6DB6DB,0x016A7A5616A, - 0x01674C59D31,0x01642C8590B,0x01611A7B961,0x015E15E15E1,0x015B1E5F752,0x015833A1583, - 0x01555555555,0x0152832C6E0,0x014FBCDA3AC,0x014D0214D02,0x014A5294A52,0x0147AE147AE, - 0x01451451451,0x0142850A142,0x01400000000,0x013D84F613D,0x013B13B13B1,0x0138ABF82EE, - 0x01364D9364D,0x0133F84CFE1,0x0131ABF0B76,0x012F684BDA1,0x012D2D2D2D2,0x012AFA64E7B, - 0x0128CFC4A33,0x0126AD1F4F3,0x01249249249,0x01227F179A5,0x012073615A2,0x011E6EFE35B, - 0x011C71C71C7,0x011A7B9611A,0x01188C46231,0x0116A3B35FC,0x0114C1BACF9,0x0112E63A6A8, - 0x01111111111,0x010F421E843,0x010D79435E5,0x010BB6610BB,0x0109F959C42,0x01084210842, - 0x01069069069,0x0104E447BEC,0x01033D91D2A,0x01019C2D14E,0x01000000000,0x00FE68F1B07, - 0x00FCD6E9E06,0x00FB49D0E22,0x00F9C18F9C1,0x00F83E0F83E,0x00F6BF3A9A3,0x00F544FB66B, - 0x00F3CF3CF3C,0x00F25DEACAF,0x00F0F0F0F0F,0x00EF883BE20,0x00EE23B88EE,0x00ECC35458C, - 0x00EB66FD0EB,0x00EA0EA0EA0,0x00E8BA2E8BA,0x00E76994F8C,0x00E61CC3987,0x00E4D3AA30A, - 0x00E38E38E38,0x00E24C602D4,0x00E10E10E10,0x00DFD33C272,0x00DE9BD37A6,0x00DD67C8A60, - 0x00DC370DC37,0x00DB0995382,0x00D9DF51B3B,0x00D8B8362E0,0x00D79435E50,0x00D673445B2, - 0x00D55555555,0x00D43A5CD98,0x00D3224F2CD,0x00D20D20D20,0x00D0FAC687D,0x00CFEB35477, - 0x00CEDE62433,0x00CDD442E4F,0x00CCCCCCCCC,0x00CBC7F5CF9,0x00CAC5B3F5D,0x00C9C5FD7A5, - 0x00C8C8C8C8C,0x00C7CE0C7CE,0x00C6D5BF60E,0x00C5DFD86CD,0x00C4EC4EC4E,0x00C3FB19B8F, - 0x00C30C30C30,0x00C21F8B86A,0x00C13521CFB,0x00C04CEB916,0x00BF66E0E5A,0x00BE82FA0BE, - 0x00BDA12F684,0x00BCC17982F,0x00BBE3D1070,0x00BB082EC20,0x00BA2E8BA2E,0x00B956E0B95, - 0x00B88127350,0x00B7AD58650,0x00B6DB6DB6D,0x00B60B60B60,0x00B53D2B0B5,0x00B470C67C0, - 0x00B3A62CE98,0x00B2DD58507,0x00B21642C85,0x00B150E682C,0x00B08D3DCB0,0x00AFCB43057, - 0x00AF0AF0AF0,0x00AE4C415C9,0x00AD8F2FBA9,0x00ACD3B68C6,0x00AC19D0AC1,0x00AB617909A, - 0x00AAAAAAAAA,0x00A9F560A9F,0x00A94196370,0x00A88F46959,0x00A7DE6D1D6,0x00A72F05397, - 0x00A6810A681,0x00A5D4783A0,0x00A5294A529,0x00A47F7C66C,0x00A3D70A3D7,0x00A32FEFAE6, - 0x00A28A28A28,0x00A1E5B1133,0x00A142850A1,0x00A0A0A0A0A,0x00A00000000,0x009F609F609, - 0x009EC27B09E,0x009E258F520,0x009D89D89D8,0x009CEF535F2,0x009C55FC177,0x009BBDCF54A, - 0x009B26C9B26,0x009A90E7D95,0x0099FC267F0,0x0099688265A,0x0098D5F85BB,0x009844853BF, - 0x0097B425ED0,0x009724D7614,0x00969696969,0x00960960960,0x00957D3273D,0x0094F2094F2, - 0x009467E2519,0x0093DEBAAF9,0x0093568FA79,0x0092CF5E824,0x00924924924,0x0091C3DF33E, - 0x00913F8BCD2,0x0090BC27CD5,0x009039B0AD1,0x008FB823EE0,0x008F377F1AD,0x008EB7BFC6E, - 0x008E38E38E3,0x008DBAE8154,0x008D3DCB08D,0x008CC18A1DE,0x008C4623118,0x008BCB93A8A, - 0x008B51D9AFE,0x008AD8F2FBA,0x008A60DD67C,0x0089E996D77,0x0089731D354,0x0088FD6E72B, - 0x00888888888,0x00881469763,0x0087A10F421,0x00872E77F93,0x0086BCA1AF2,0x00864B8A7DE, - 0x0085DB3085D,0x00856B91EDA,0x0084FCACE21,0x00848E7F95F,0x00842108421,0x0083B445250, - 0x00834834834,0x0082DCD4A6D,0x00827223DF6,0x00820820820,0x00819EC8E95,0x0081361B751, - 0x0080CE168A7,0x008066B893A,0x00800000000,0x007F99EB43C,0x007F3478D83,0x007ECFA73B7, - 0x007E6B74F03,0x007E07E07E0,0x007DA4E8711,0x007D428B5A0,0x007CE0C7CE0,0x007C7F9C66B, - 0x007C1F07C1F,0x007BBF0881E,0x007B5F9D4D1,0x007B00C4CE0,0x007AA27DB35,0x007A44C6AFC, - 0x0079E79E79E,0x00798B03CC5,0x00792EF5657,0x0078D372078,0x00787878787,0x00781E0781E, - 0x0077C41DF10,0x00776ABA96C,0x007711DC477,0x0076B981DAE,0x007661AA2C6,0x00760A541A8, - 0x0075B37E875,0x00755D28580,0x00750750750,0x0074B1F5CA0,0x00745D1745D,0x007408B3DA4, - 0x0073B4CA7C6,0x0073615A240,0x00730E61CC3,0x0072BBE072B,0x007269D5185,0x0072183EC08, - 0x0071C71C71C,0x0071766D352,0x0071263016A,0x0070D66424A,0x00708708708,0x0070381C0E0, - 0x006FE99E139,0x006F9B8D9A2,0x006F4DE9BD3,0x006F00B19AB,0x006EB3E4530,0x006E678108F, - 0x006E1B86E1B,0x006DCFF504C,0x006D84CA9C1,0x006D3A06D3A,0x006CEFA8D9D,0x006CA5AFDF6, - 0x006C5C1B170,0x006C12E9B5B,0x006BCA1AF28,0x006B81AE06B,0x006B39A22D9,0x006AF1F6A46, - 0x006AAAAAAAA,0x006A63BD81A,0x006A1D2E6CC,0x0069D6FCB14,0x00699127966,0x00694BAE655, - 0x00690690690,0x0068C1CCEE5,0x00687D6343E,0x00683952BA4,0x0067F59AA3B,0x0067B23A544, - 0x00676F31219,0x00672C7E634,0x0066EA21727,0x0066A819AA0,0x00666666666,0x0066250705B, - 0x0065E3FAE7C,0x0065A3416DE,0x006562D9FAE,0x006522C3F35,0x0064E2FEBD2,0x0064A389BFD, - 0x00646464646,0x0064258E154,0x0063E7063E7,0x0063A8CC4D3,0x00636ADFB07,0x00632D3FD85, - 0x0062EFEC366,0x0062B2E43DA,0x00627627627,0x006239B51A6,0x0061FD8CDC7,0x0061C1AE20F, - 0x00618618618,0x00614ACB18E,0x00610FC5C35,0x0060D507DE1,0x00609A90E7D,0x00606060606, - 0x00602675C8B,0x005FECD0A31,0x005FB37072D,0x005F7A54BC9,0x005F417D05F,0x005F08E8D5D, - 0x005ED097B42,0x005E988929F,0x005E60BCC17,0x005E293205E,0x005DF1E8838,0x005DBADFC7C, - 0x005D8417610,0x005D4D8EDEC,0x005D1745D17,0x005CE13BCA9,0x005CAB705CA,0x005C75E31B2, - 0x005C40939A8,0x005C0B81702,0x005BD6AC328,0x005BA21378D,0x005B6DB6DB6,0x005B3995F37, - 0x005B05B05B0,0x005AD205AD2,0x005A9E9585A,0x005A6B5F816,0x005A38633E0,0x005A05A05A0, - 0x0059D31674C,0x0059A0C52E7,0x00596EAC283,0x00593CCB03E,0x00590B21642,0x0058D9AEEC9, - 0x0058A873416,0x0058776E07B,0x0058469EE58,0x00581605816,0x0057E5A182B,0x0057B57291D, - 0x00578578578,0x005755B27D8,0x00572620AE4,0x0056F6C294E,0x0056C797DD4,0x005698A033F, - 0x005669DB463,0x00563B48C20,0x00560CE8560,0x0055DEB9B1A,0x0055B0BC84D,0x005582F0804, - 0x00555555555,0x005527EAB60,0x0054FAB054F,0x0054CDA5E57,0x0054A0CB1B8,0x0054741FAB8, - 0x005447A34AC,0x00541B55AF0,0x0053EF368EB,0x0053C345A0B,0x005397829CB,0x00536BED3AE, - 0x00534085340,0x0053154A416,0x0052EA3C1D0,0x0052BF5A814,0x005294A5294,0x00526A1BD09, - 0x00523FBE336,0x0052158C0E5,0x0051EB851EB,0x0051C1A9223,0x005197F7D73,0x00516E70FC6, - 0x00514514514,0x00511BE1958,0x0050F2D8899,0x0050C9F8EE5,0x0050A142850,0x005078B50F9, - 0x00505050505,0x005028140A0,0x00500000000,0x004FD813F60,0x004FB04FB04,0x004F88B2F39, - 0x004F613D84F,0x004F39EF2A1,0x004F12C7A90,0x004EEBC6C84,0x004EC4EC4EC,0x004E9E3803E, - 0x004E77A9AF9,0x004E514119F,0x004E2AFE0BB,0x004E04E04E0,0x004DDEE7AA5,0x004DB913EAA, - 0x004D9364D93,0x004D6DDA40D,0x004D4873ECA,0x004D2331A84,0x004CFE133F8,0x004CD9187EC, - 0x004CB44132D,0x004C8F8D28A,0x004C6AFC2DD,0x004C468E103,0x004C22429DF,0x004BFE19A5C, - 0x004BDA12F68,0x004BB62E5F9,0x004B926BB0A,0x004B6ECAB9C,0x004B4B4B4B4,0x004B27ED360, - 0x004B04B04B0,0x004AE1945BB,0x004ABE9939E,0x004A9BBEB7B,0x004A7904A79,0x004A566ADC3, - 0x004A33F128C,0x004A119760C,0x0049EF5D57C,0x0049CD42E20,0x0049AB47D3C,0x0049896C01D, - 0x004967AF412,0x00494611670,0x00492492492,0x00490331BD6,0x0048E1EF99F,0x0048C0CBB56, - 0x00489FC5E69,0x00487EDE048,0x00485E13E6A,0x00483D6764A,0x00481CD8568,0x0047FC66947, - 0x0047DC11F70,0x0047BBDA56F,0x00479BBF8D6,0x00477BC173B,0x00475BDFE37,0x00473C1AB68, - 0x00471C71C71,0x0046FCE4EF9,0x0046DD740AA,0x0046BE1EF32,0x00469EE5846,0x00467FC799C, - 0x004660C50EF,0x004641DDBFE,0x0046231188C,0x00460460460,0x0045E5C9D45,0x0045C74E109, - 0x0045A8ECD7F,0x00458AA607D,0x00456C797DD,0x00454E6717D,0x0045306EB3E,0x00451290305, - 0x0044F4CB6BB,0x0044D72044D,0x0044B98E9AA,0x00449C164C5,0x00447EB7395,0x00446171416, - 0x00444444444,0x00442730221,0x00440A34BB1,0x0043ED51EFD,0x0043D087A10,0x0043B3D5AF9, - 0x0043973BFC9,0x00437ABA696,0x00435E50D79,0x004341FF28C,0x004325C53EF,0x004309A2FC3, - 0x0042ED9842E,0x0042D1A4F58,0x0042B5C8F6D,0x00429A0429A,0x00427E56710,0x004262BFB05, - 0x0042473FCAF,0x00422BD6A49,0x00421084210,0x0041F548244,0x0041DA22928,0x0041BF13502, - 0x0041A41A41A,0x004189374BC,0x00416E6A536,0x004153B33DA,0x00413911EFB,0x00411E864EF, - 0x00410410410,0x0040E9AFAB9,0x0040CF6474A,0x0040B52E823,0x00409B0DBA8,0x00408102040, - 0x0040670B453,0x00404D2964D,0x0040335C49D,0x004019A3DB2,0x00400000000,0x003FE6709FC, - 0x003FCCF5A1E,0x003FB38EEE1,0x003F9A3C6C1,0x003F80FE03F,0x003F67D39DB,0x003F4EBD21A, - 0x003F35BA781,0x003F1CCB89A,0x003F03F03F0,0x003EEB2880F,0x003ED274388,0x003EB9D34EC, - 0x003EA145AD0,0x003E88CB3C9,0x003E7063E70,0x003E580F960,0x003E3FCE335,0x003E279FA8F, - 0x003E0F83E0F,0x003DF77AC58,0x003DDF8440F,0x003DC7A03DC,0x003DAFCEA68,0x003D980F660, - 0x003D8062670,0x003D68C7948,0x003D513ED9A,0x003D39C821A,0x003D226357E,0x003D0B1067C, - 0x003CF3CF3CF,0x003CDC9FC32,0x003CC581E62,0x003CAE75920,0x003C977AB2B,0x003C8091348, - 0x003C69B903C,0x003C52F20CD,0x003C3C3C3C3,0x003C25977EA,0x003C0F03C0F,0x003BF880EFE, - 0x003BE20EF88,0x003BCBADC7F,0x003BB55D4B6,0x003B9F1D702,0x003B88EE23B,0x003B72CF539, - 0x003B5CC0ED7,0x003B46C2DF0,0x003B30D5163,0x003B1AF780E,0x003B052A0D4,0x003AEF6CA97, - 0x003AD9BF43A,0x003AC421CA6,0x003AAE942C0,0x003A9916572,0x003A83A83A8,0x003A6E49C4D, - 0x003A58FAE50,0x003A43BB8A0,0x003A2E8BA2E,0x003A196B1ED,0x003A0459ED2,0x0039EF57FD1, - 0x0039DA653E3,0x0039C5819FF,0x0039B0AD120,0x00399BE7842,0x00398730E61,0x0039728927D, - 0x00395DF0395,0x003949660AB,0x003934EA8C2,0x0039207DADE,0x00390C1F604,0x0038F7CF93C, - 0x0038E38E38E,0x0038CF5B404,0x0038BB369A9,0x0038A72038A,0x003893180B5,0x00387F1E038, - 0x00386B32125,0x0038575428D,0x00384384384,0x00382FC231D,0x00381C0E070,0x00380867A92, - 0x0037F4CF09C,0x0037E1441A8,0x0037CDC6CD1,0x0037BA57132,0x0037A6F4DE9,0x003793A0215, - 0x00378058CD5,0x00376D1ED4B,0x003759F2298,0x003746D2BE0,0x003733C0847,0x003720BB6F4, - 0x00370DC370D,0x0036FAD87BB,0x0036E7FA826,0x0036D529779,0x0036C2654E0,0x0036AFADF87, - 0x00369D0369D,0x00368A6594F,0x003677D46CE,0x0036654FE4C,0x003652D7EFB,0x0036406C80D, - 0x00362E0D8B8,0x00361BBB030,0x00360974DAD,0x0035F73B066,0x0035E50D794,0x0035D2EC270, - 0x0035C0D7035,0x0035AECE020,0x00359CD116C,0x00358AE0358,0x003578FB523,0x0035672260C, - 0x00355555555,0x0035439423F,0x003531DEC0D,0x00352035203,0x00350E97366,0x0034FD04F7B, - 0x0034EB7E58A,0x0034DA034DA,0x0034C893CB3,0x0034B72FC60,0x0034A5D732A,0x0034948A05E, - 0x00348348348,0x00347211B34,0x003460E6772,0x00344FC6750,0x00343EB1A1F,0x00342DA7F2F, - 0x00341CA95D2,0x00340BB5D5B,0x0033FACD51D,0x0033E9EFC6E,0x0033D91D2A2,0x0033C85570F, - 0x0033B79890C,0x0033A6E67F3,0x0033963F31A,0x003385A29DC,0x00337510B93,0x0033648979B, - 0x0033540CD50,0x0033439AC0E,0x00333333333,0x003322D621E,0x0033128382D,0x0033023B4C3, - 0x0032F1FD73E,0x0032E1C9F01,0x0032D1A0B6F,0x0032C181BEA,0x0032B16CFD7,0x0032A16269B, - 0x00329161F9A,0x0032816BA3D,0x0032717F5E9,0x0032619D206,0x003251C4DFE,0x003241F693A, - 0x00323232323,0x00322277B24,0x003212C70AA,0x00320320320,0x0031F3831F3,0x0031E3EFC91, - 0x0031D466269,0x0031C4E62EA,0x0031B56FD83,0x0031A6031A6,0x0031969FEC2,0x0031874644B, - 0x003177F61B3,0x003168AF66D,0x003159721ED,0x00314A3E3A8,0x00313B13B13,0x00312BF27A5, - 0x00311CDA8D3,0x00310DCBE15,0x0030FEC66E3,0x0030EFCA2B6,0x0030E0D7107,0x0030D1ED150, - 0x0030C30C30C,0x0030B4345B5,0x0030A5658C7,0x0030969FBBF,0x003087E2E1A,0x0030792EF56, - 0x00306A83EF0,0x00305BE1C69,0x00304D4873E,0x00303EB7EF1,0x00303030303,0x003021B12F3, - 0x0030133AE45,0x003004CD47B,0x002FF668518,0x002FE80BFA0,0x002FD9B8396,0x002FCB6D081, - 0x002FBD2A5E4,0x002FAEF0347,0x002FA0BE82F,0x002F9295424,0x002F84746AE,0x002F765BF55, - 0x002F684BDA1,0x002F5A4411C,0x002F4C4494F,0x002F3E4D5C6,0x002F305E60B,0x002F22779AA, - 0x002F149902F,0x002F06C2925,0x002EF8F441C,0x002EEB2E09F,0x002EDD6FE3E,0x002ECFB9C86, - 0x002EC20BB08,0x002EB465952,0x002EA6C76F6,0x002E9931383,0x002E8BA2E8B,0x002E7E1C7A0, - 0x002E709DE54,0x002E632723A,0x002E55B82E5,0x002E4850FE8,0x002E3AF18D9,0x002E2D99D4B, - 0x002E2049CD4,0x002E1301709,0x002E05C0B81,0x002DF8879D2,0x002DEB56194,0x002DDE2C25D, - 0x002DD109BC6,0x002DC3EED68,0x002DB6DB6DB,0x002DA9CF7B9,0x002D9CCAF9B,0x002D8FCDE1D, - 0x002D82D82D8,0x002D75E9D68,0x002D6902D69,0x002D5C23276,0x002D4F4AC2D,0x002D4279A2A, - 0x002D35AFC0B,0x002D28ED16D,0x002D1C319F0,0x002D0F7D531,0x002D02D02D0,0x002CF62A26C, - 0x002CE98B3A6,0x002CDCF361D,0x002CD062973,0x002CC3D8D4A,0x002CB756141,0x002CAADA4FD, - 0x002C9E6581F,0x002C91F7A4A,0x002C8590B21,0x002C7930A48,0x002C6CD7764,0x002C6085218, - 0x002C5439A0B,0x002C47F4EE0,0x002C3BB703D,0x002C2F7FDCA,0x002C234F72C,0x002C1725C09, - 0x002C0B02C0B,0x002BFEE66D7,0x002BF2D0C15,0x002BE6C1B70,0x002BDAB948E,0x002BCEB771A, - 0x002BC2BC2BC,0x002BB6C771E,0x002BAAD93EC,0x002B9EF18CF,0x002B9310572,0x002B8735981, - 0x002B7B614A7,0x002B6F93690,0x002B63CBEEA,0x002B580AD60,0x002B4C5019F,0x002B409BB56, - 0x002B34EDA31,0x002B2945DE0,0x002B1DA4610,0x002B1209270,0x002B06742B0,0x002AFAE567F, - 0x002AEF5CD8D,0x002AE3DA78A,0x002AD85E426,0x002ACCE8313,0x002AC178402,0x002AB60E6A3, - 0x002AAAAAAAA,0x002A9F4CFC8,0x002A93F55B0,0x002A88A3C14,0x002A7D582A7,0x002A721291E, - 0x002A66D2F2B,0x002A5B99484,0x002A50658DC,0x002A4537BE7,0x002A3A0FD5C,0x002A2EEDCEF, - 0x002A23D1A56,0x002A18BB547,0x002A0DAAD78,0x002A02A02A0,0x0029F79B475,0x0029EC9C2AF, - 0x0029E1A2D05,0x0029D6AF32F,0x0029CBC14E5,0x0029C0D91E0,0x0029B5F69D7,0x0029AB19C84, - 0x0029A0429A0,0x002995710E4,0x00298AA520B,0x00297FDECCE,0x0029751E0E8,0x00296A62E13, - 0x00295FAD40A,0x002954FD288,0x00294A5294A,0x00293FAD80A,0x0029350DE84,0x00292A73C76, - 0x00291FDF19B,0x0029154FDB0,0x00290AC6072,0x002900419A0 +const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1] = +{ + 0x5000000000,0x4D9364D936,0x4B4B4B4B4B,0x4924924924,0x471C71C71C,0x45306EB3E4, + 0x435E50D794,0x41A41A41A4,0x4000000000,0x3E7063E706,0x3CF3CF3CF3,0x3B88EE23B8, + 0x3A2E8BA2E8,0x38E38E38E3,0x37A6F4DE9B,0x3677D46CEF,0x3555555555,0x343EB1A1F5, + 0x3333333333,0x3232323232,0x313B13B13B,0x304D4873EC,0x2F684BDA12,0x2E8BA2E8BA, + 0x2DB6DB6DB6,0x2CE98B3A62,0x2C234F72C2,0x2B63CBEEA4,0x2AAAAAAAAA,0x29F79B4758, + 0x294A5294A5,0x28A28A28A2,0x2800000000,0x2762762762,0x26C9B26C9B,0x26357E16EC, + 0x25A5A5A5A5,0x2519F89467,0x2492492492,0x240E6C2B44,0x238E38E38E,0x231188C462, + 0x22983759F2,0x2222222222,0x21AF286BCA,0x213F2B3884,0x20D20D20D2,0x2067B23A54, + 0x2000000000,0x1F9ADD3C0C,0x1F3831F383,0x1ED7E75346,0x1E79E79E79,0x1E1E1E1E1E, + 0x1DC47711DC,0x1D6CDFA1D6,0x1D1745D174,0x1CC398730E,0x1C71C71C71,0x1C21C21C21, + 0x1BD37A6F4D,0x1B86E1B86E,0x1B3BEA3677,0x1AF286BCA1,0x1AAAAAAAAA,0x1A6449E59B, + 0x1A1F58D0FA,0x19DBCC4867,0x1999999999,0x1958B67EBB,0x1919191919,0x18DAB7EC1D, + 0x189D89D89D,0x1861861861,0x1826A439F6,0x17ECDC1CB5,0x17B425ED09,0x177C7A20E1, + 0x1745D1745D,0x171024E6A1,0x16DB6DB6DB,0x16A7A5616A,0x1674C59D31,0x1642C8590B, + 0x1611A7B961,0x15E15E15E1,0x15B1E5F752,0x15833A1583,0x1555555555,0x152832C6E0, + 0x14FBCDA3AC,0x14D0214D02,0x14A5294A52,0x147AE147AE,0x1451451451,0x142850A142, + 0x1400000000,0x13D84F613D,0x13B13B13B1,0x138ABF82EE,0x1364D9364D,0x133F84CFE1, + 0x131ABF0B76,0x12F684BDA1,0x12D2D2D2D2,0x12AFA64E7B,0x128CFC4A33,0x126AD1F4F3, + 0x1249249249,0x1227F179A5,0x12073615A2,0x11E6EFE35B,0x11C71C71C7,0x11A7B9611A, + 0x1188C46231,0x116A3B35FC,0x114C1BACF9,0x112E63A6A8,0x1111111111,0x10F421E843, + 0x10D79435E5,0x10BB6610BB,0x109F959C42,0x1084210842,0x1069069069,0x104E447BEC, + 0x1033D91D2A,0x1019C2D14E,0x1000000000,0x0FE68F1B07,0x0FCD6E9E06,0x0FB49D0E22, + 0x0F9C18F9C1,0x0F83E0F83E,0x0F6BF3A9A3,0x0F544FB66B,0x0F3CF3CF3C,0x0F25DEACAF, + 0x0F0F0F0F0F,0x0EF883BE20,0x0EE23B88EE,0x0ECC35458C,0x0EB66FD0EB,0x0EA0EA0EA0, + 0x0E8BA2E8BA,0x0E76994F8C,0x0E61CC3987,0x0E4D3AA30A,0x0E38E38E38,0x0E24C602D4, + 0x0E10E10E10,0x0DFD33C272,0x0DE9BD37A6,0x0DD67C8A60,0x0DC370DC37,0x0DB0995382, + 0x0D9DF51B3B,0x0D8B8362E0,0x0D79435E50,0x0D673445B2,0x0D55555555,0x0D43A5CD98, + 0x0D3224F2CD,0x0D20D20D20,0x0D0FAC687D,0x0CFEB35477,0x0CEDE62433,0x0CDD442E4F, + 0x0CCCCCCCCC,0x0CBC7F5CF9,0x0CAC5B3F5D,0x0C9C5FD7A5,0x0C8C8C8C8C,0x0C7CE0C7CE, + 0x0C6D5BF60E,0x0C5DFD86CD,0x0C4EC4EC4E,0x0C3FB19B8F,0x0C30C30C30,0x0C21F8B86A, + 0x0C13521CFB,0x0C04CEB916,0x0BF66E0E5A,0x0BE82FA0BE,0x0BDA12F684,0x0BCC17982F, + 0x0BBE3D1070,0x0BB082EC20,0x0BA2E8BA2E,0x0B956E0B95,0x0B88127350,0x0B7AD58650, + 0x0B6DB6DB6D,0x0B60B60B60,0x0B53D2B0B5,0x0B470C67C0,0x0B3A62CE98,0x0B2DD58507, + 0x0B21642C85,0x0B150E682C,0x0B08D3DCB0,0x0AFCB43057,0x0AF0AF0AF0,0x0AE4C415C9, + 0x0AD8F2FBA9,0x0ACD3B68C6,0x0AC19D0AC1,0x0AB617909A,0x0AAAAAAAAA,0x0A9F560A9F, + 0x0A94196370,0x0A88F46959,0x0A7DE6D1D6,0x0A72F05397,0x0A6810A681,0x0A5D4783A0, + 0x0A5294A529,0x0A47F7C66C,0x0A3D70A3D7,0x0A32FEFAE6,0x0A28A28A28,0x0A1E5B1133, + 0x0A142850A1,0x0A0A0A0A0A }; diff --git a/src/ft2_tables.h b/src/ft2_tables.h @@ -3,7 +3,7 @@ #include <stdint.h> #include "ft2_palette.h" // pal16 typedef #include "ft2_pattern_ed.h" // pattCoord_t/pattCoord2_t/pattCoordsMouse_t/markCoord_t typedef -#include "ft2_header.h" // MAX_VOICES +#include "ft2_header.h" // MAX_CHANNELS #include "ft2_config.h" // CONFIG_FILE_SIZE #define KEY2VOL_ENTRIES (signed)(sizeof (key2VolTab) / sizeof (SDL_Keycode)) @@ -37,8 +37,8 @@ extern const pattCoord2_t pattCoord2Table[2][2][2]; extern const markCoord_t markCoordTable[2][2][2]; extern const uint8_t pattCursorXTab[2 * 4 * 8]; extern const uint8_t pattCursorWTab[2 * 4 * 8]; -extern const char chDecTab1[MAX_VOICES+1]; -extern const char chDecTab2[MAX_VOICES+1]; +extern const char chDecTab1[MAX_CHANNELS+1]; +extern const char chDecTab2[MAX_CHANNELS+1]; extern const SDL_Keycode key2VolTab[16]; extern const SDL_Keycode key2EfxTab[36]; extern const SDL_Keycode key2HexTab[16]; @@ -49,4 +49,4 @@ extern const uint16_t scopeLenTab[16][32]; extern const uint8_t defConfigData[CONFIG_FILE_SIZE]; -extern const uint64_t musicTimeTab64[MAX_BPM+1]; +extern const uint64_t musicTimeTab64[(MAX_BPM-MIN_BPM)+1]; diff --git a/src/ft2_textboxes.c b/src/ft2_textboxes.c @@ -727,7 +727,7 @@ bool testTextBoxMouseDown(void) void updateTextBoxPointers(void) { int32_t i; - instrTyp *curIns = instr[editor.curInstr]; + instr_t *curIns = instr[editor.curInstr]; // instrument names for (i = 0; i < 8; i++) @@ -742,7 +742,7 @@ void updateTextBoxPointers(void) else { for (i = 0; i < 5; i++) - textBoxes[TB_SAMP1+i].textPtr = curIns->samp[editor.sampleBankOffset+i].name; + textBoxes[TB_SAMP1+i].textPtr = curIns->smp[editor.sampleBankOffset+i].name; } // song name diff --git a/src/ft2_trim.c b/src/ft2_trim.c @@ -10,7 +10,7 @@ #include "ft2_header.h" #include "ft2_sample_ed.h" #include "ft2_gui.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_pattern_ed.h" #include "ft2_replayer.h" #include "ft2_audio.h" @@ -24,8 +24,8 @@ static bool removePatt, removeInst, removeSamp, removeChans, removeSmpDataAfterL static uint8_t instrUsed[MAX_INST], instrOrder[MAX_INST], pattUsed[MAX_PATTERNS], pattOrder[MAX_PATTERNS]; static int16_t oldPattLens[MAX_PATTERNS], tmpPattLens[MAX_PATTERNS]; static int64_t xmSize64 = -1, xmAfterTrimSize64 = -1, spaceSaved64 = -1; -static tonTyp *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS]; -static instrTyp *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation +static note_t *oldPatts[MAX_PATTERNS], *tmpPatt[MAX_PATTERNS]; +static instr_t *tmpInstr[1 + MAX_INST], *tmpInst[MAX_INST]; // tmpInstr[x] = copy of instr[x] for "after trim" size calculation static SDL_Thread *trimThread; void pbTrimCalc(void); @@ -52,7 +52,7 @@ static bool setTmpInstruments(void) { if (instr[i] != NULL) { - tmpInstr[i] = (instrTyp *)malloc(sizeof (instrTyp)); + tmpInstr[i] = (instr_t *)malloc(sizeof (instr_t)); if (tmpInstr[i] == NULL) { freeTmpInstruments(); @@ -70,29 +70,30 @@ static void remapInstrInSong(uint8_t src, uint8_t dst, int32_t ap) { for (int32_t i = 0; i < ap; i++) { - tonTyp *pattPtr = patt[i]; + note_t *pattPtr = pattern[i]; if (pattPtr == NULL) continue; - const int32_t readLen = pattLens[i] * MAX_VOICES; - for (int32_t j = 0; j < readLen; j++) + const int32_t readLen = patternNumRows[i] * MAX_CHANNELS; + + note_t *p = pattPtr; + for (int32_t j = 0; j < readLen; j++, p++) { - tonTyp *note = &pattPtr[j]; - if (note->instr == src) - note->instr = dst; + if (p->instr == src) + p->instr = dst; } } } -static int16_t getUsedTempSamples(uint16_t nr) +static int16_t getUsedTempSamples(uint16_t insNum) { - if (tmpInstr[nr] == NULL) + if (tmpInstr[insNum] == NULL) return 0; - instrTyp *ins = tmpInstr[nr]; + instr_t *ins = tmpInstr[insNum]; int16_t i = 16 - 1; - while (i >= 0 && ins->samp[i].pek == NULL && ins->samp[i].name[0] == '\0') + while (i >= 0 && ins->smp[i].dataPtr == NULL && ins->smp[i].name[0] == '\0') i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 @@ -100,8 +101,8 @@ static int16_t getUsedTempSamples(uint16_t nr) **/ for (int16_t j = 0; j < 96; j++) { - if (ins->ta[j] > i) - i = ins->ta[j]; + if (ins->note2SampleLUT[j] > i) + i = ins->note2SampleLUT[j]; } return i+1; @@ -127,15 +128,21 @@ static int64_t getTempInsAndSmpSize(void) const int16_t a = getUsedTempSamples(i); if (a > 0) - currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp)); + currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t)); else currSize64 += 22+11; - instrTyp *ins = tmpInstr[j]; + instr_t *ins = tmpInstr[j]; for (int16_t k = 0; k < a; k++) { - if (ins->samp[k].pek != NULL) - currSize64 += ins->samp[k].len; + sample_t *s = &ins->smp[k]; + if (s->dataPtr != NULL && s->length > 0) + { + if (s->flags & SAMPLE_16BIT) + currSize64 += s->length << 1; + else + currSize64 += s->length; + } } } @@ -144,10 +151,9 @@ static int64_t getTempInsAndSmpSize(void) static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t antChn) { - uint8_t newInst; - int16_t pattLen; + int16_t numRows; int32_t i, j, k; - tonTyp *pattPtr; + note_t *p; int32_t numInsts = *ai; @@ -157,31 +163,31 @@ static void wipeInstrUnused(bool testWipeSize, int16_t *ai, int32_t ap, int32_t { if (testWipeSize) { - pattPtr = tmpPatt[i]; - pattLen = tmpPattLens[i]; + p = tmpPatt[i]; + numRows = tmpPattLens[i]; } else { - pattPtr = patt[i]; - pattLen = pattLens[i]; + p = pattern[i]; + numRows = patternNumRows[i]; } - if (pattPtr == NULL) + if (p == NULL) continue; - for (j = 0; j < pattLen; j++) + for (j = 0; j < numRows; j++) { for (k = 0; k < antChn; k++) { - newInst = pattPtr[(j * MAX_VOICES) + k].instr; - if (newInst > 0 && newInst <= MAX_INST) - instrUsed[newInst-1] = true; + uint8_t ins = p[(j * MAX_CHANNELS) + k].instr; + if (ins > 0 && ins <= MAX_INST) + instrUsed[ins-1] = true; } } } int16_t instToDel = 0; - newInst = 0; + uint8_t newInst = 0; int16_t newNumInsts = 0; memset(instrOrder, 0, numInsts); @@ -271,15 +277,15 @@ static void wipePattsUnused(bool testWipeSize, int16_t *ap) { uint8_t newPatt; int16_t i, *pLens; - tonTyp **p; + note_t **p; int16_t usedPatts = *ap; memset(pattUsed, 0, usedPatts); int16_t newUsedPatts = 0; - for (i = 0; i < song.len; i++) + for (i = 0; i < song.songLength; i++) { - newPatt = song.songTab[i]; + newPatt = song.orders[i]; if (newPatt < usedPatts && !pattUsed[newPatt]) { pattUsed[newPatt] = true; @@ -305,13 +311,13 @@ static void wipePattsUnused(bool testWipeSize, int16_t *ap) } else { - p = patt; - pLens = pattLens; + p = pattern; + pLens = patternNumRows; } - memcpy(oldPatts, p, usedPatts * sizeof (tonTyp *)); + memcpy(oldPatts, p, usedPatts * sizeof (note_t *)); memcpy(oldPattLens, pLens, usedPatts * sizeof (int16_t)); - memset(p, 0, usedPatts * sizeof (tonTyp *)); + memset(p, 0, usedPatts * sizeof (note_t *)); memset(pLens, 0, usedPatts * sizeof (int16_t)); // relocate patterns @@ -339,17 +345,17 @@ static void wipePattsUnused(bool testWipeSize, int16_t *ap) { for (i = 0; i < MAX_PATTERNS; i++) { - if (patt[i] == NULL) - pattLens[i] = 64; + if (pattern[i] == NULL) + patternNumRows[i] = 64; } // reorder order list (and clear unused entries) for (i = 0; i < 256; i++) { - if (i < song.len) - song.songTab[i] = pattOrder[song.songTab[i]]; + if (i < song.songLength) + song.orders[i] = pattOrder[song.orders[i]]; else - song.songTab[i] = 0; + song.orders[i] = 0; } } @@ -360,8 +366,8 @@ static void wipeSamplesUnused(bool testWipeSize, int16_t ai) { uint8_t smpUsed[16], smpOrder[16]; int16_t j, k, l; - instrTyp *ins; - sampleTyp tempSamples[16]; + instr_t *ins; + sample_t tempSamples[16]; for (int16_t i = 1; i <= ai; i++) { @@ -389,13 +395,13 @@ static void wipeSamplesUnused(bool testWipeSize, int16_t ai) memset(smpUsed, 0, l); if (l > 0) { - sampleTyp *s = ins->samp; + sample_t *s = ins->smp; for (j = 0; j < l; j++, s++) { // check if sample is referenced in instrument for (k = 0; k < 96; k++) { - if (ins->ta[k] == j) + if (ins->note2SampleLUT[k] == j) { smpUsed[j] = true; break; // sample is used @@ -406,12 +412,10 @@ static void wipeSamplesUnused(bool testWipeSize, int16_t ai) { // sample is unused - if (s->origPek != NULL && !testWipeSize) - free(s->origPek); + if (s->dataPtr != NULL && !testWipeSize) + freeSmpData(s); - memset(s, 0, sizeof (sampleTyp)); - s->origPek = NULL; - s->pek = NULL; + memset(s, 0, sizeof (sample_t)); } } @@ -426,23 +430,23 @@ static void wipeSamplesUnused(bool testWipeSize, int16_t ai) // re-order samples - memcpy(tempSamples, ins->samp, l * sizeof (sampleTyp)); - memset(ins->samp, 0, l * sizeof (sampleTyp)); + memcpy(tempSamples, ins->smp, l * sizeof (sample_t)); + memset(ins->smp, 0, l * sizeof (sample_t)); for (j = 0; j < l; j++) { if (smpUsed[j]) - ins->samp[smpOrder[j]] = tempSamples[j]; + ins->smp[smpOrder[j]] = tempSamples[j]; } // re-order note->sample list for (j = 0; j < 96; j++) { - newSamp = ins->ta[j]; + newSamp = ins->note2SampleLUT[j]; if (smpUsed[newSamp]) - ins->ta[j] = smpOrder[newSamp]; + ins->note2SampleLUT[j] = smpOrder[newSamp]; else - ins->ta[j] = 0; + ins->note2SampleLUT[j] = 0; } } } @@ -451,7 +455,7 @@ static void wipeSamplesUnused(bool testWipeSize, int16_t ai) static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai) { int16_t l; - instrTyp *ins; + instr_t *ins; for (int16_t i = 1; i <= ai; i++) { @@ -476,33 +480,25 @@ static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai) l = getUsedTempSamples(i); } - sampleTyp *s = ins->samp; + sample_t *s = ins->smp; for (int16_t j = 0; j < l; j++, s++) { - if (s->origPek != NULL && s->typ & 3 && s->len > 0 && s->len > s->repS+s->repL) + if (s->dataPtr != NULL && GET_LOOPTYPE(s->flags) != LOOP_OFF && s->length > 0 && s->length > s->loopStart+s->loopLength) { if (!testWipeSize) - restoreSample(s); + unfixSample(s); - s->len = s->repS + s->repL; + s->length = s->loopStart + s->loopLength; if (!testWipeSize) { - if (s->len <= 0) + if (s->length <= 0) { - s->len = 0; - - free(s->origPek); - s->origPek = NULL; - s->pek = NULL; + s->length = 0; + freeSmpData(s); } else { - int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } + reallocateSmpData(s, s->length, !!(s->flags & SAMPLE_16BIT)); } } @@ -516,7 +512,7 @@ static void wipeSmpDataAfterLoop(bool testWipeSize, int16_t ai) static void convertSamplesTo8bit(bool testWipeSize, int16_t ai) { int16_t k; - instrTyp *ins; + instr_t *ins; for (int16_t i = 1; i <= ai; i++) { @@ -541,42 +537,28 @@ static void convertSamplesTo8bit(bool testWipeSize, int16_t ai) k = getUsedTempSamples(i); } - sampleTyp *s = ins->samp; + sample_t *s = ins->smp; for (int16_t j = 0; j < k; j++, s++) { - if (s->origPek != NULL && (s->typ & 16) && s->len > 0) + if (s->dataPtr != NULL && s->length > 0 && (s->flags & SAMPLE_16BIT)) { if (testWipeSize) { - s->typ &= ~16; - s->len >>= 1; - s->repL >>= 1; - s->repS >>= 1; + s->flags &= ~SAMPLE_16BIT; } else { - restoreSample(s); + unfixSample(s); - assert(s->pek != NULL); - const int16_t *src16 = (const int16_t *)s->pek; - int8_t *dst8 = s->pek; + const int16_t *src16 = (const int16_t *)s->dataPtr; + int8_t *dst8 = s->dataPtr; - const int32_t newLen = s->len >> 1; - for (int32_t a = 0; a < newLen; a++) + for (int32_t a = 0; a < s->length; a++) dst8[a] = src16[a] >> 8; - s->repL >>= 1; - s->repS >>= 1; - s->len >>= 1; - s->typ &= ~16; - - int8_t *newPtr = (int8_t *)realloc(s->origPek, s->len + LOOP_FIX_LEN); - if (newPtr != NULL) - { - s->origPek = newPtr; - s->pek = s->origPek + SMP_DAT_OFFSET; - } + s->flags &= ~SAMPLE_16BIT; + reallocateSmpData(s, s->length, true); fixSample(s); } } @@ -584,12 +566,12 @@ static void convertSamplesTo8bit(bool testWipeSize, int16_t ai) } } -static uint16_t getPackedPattSize(tonTyp *pattern, int32_t numRows, int32_t antChn) +static uint16_t getPackedPattSize(note_t *p, int32_t numRows, int32_t antChn) { - uint8_t bytes[sizeof (tonTyp)]; + uint8_t bytes[sizeof (note_t)]; uint16_t totalPackLen = 0; - uint8_t *pattPtr = (uint8_t *)pattern; + uint8_t *pattPtr = (uint8_t *)p; uint8_t *writePtr = pattPtr; for (int32_t row = 0; row < numRows; row++) @@ -624,30 +606,28 @@ static uint16_t getPackedPattSize(tonTyp *pattern, int32_t numRows, int32_t antC } // skip unused channels - pattPtr += sizeof (tonTyp) * (MAX_VOICES - antChn); + pattPtr += sizeof (note_t) * (MAX_CHANNELS - antChn); } return totalPackLen; } -static bool tmpPatternEmpty(uint16_t nr, int32_t antChn) +static bool tmpPatternEmpty(uint16_t pattNum, int32_t numChannels) { - if (tmpPatt[nr] == NULL) + if (tmpPatt[pattNum] == NULL) return true; - uint8_t *scanPtr = (uint8_t *)tmpPatt[nr]; - int32_t scanLen = antChn * sizeof (tonTyp); - int32_t pattLen = tmpPattLens[nr]; + uint8_t *scanPtr = (uint8_t *)tmpPatt[pattNum]; + int32_t scanLen = numChannels * sizeof (note_t); + int32_t numRows = tmpPattLens[pattNum]; - for (int32_t i = 0; i < pattLen; i++) + for (int32_t i = 0; i < numRows; i++, scanPtr += TRACK_WIDTH) { for (int32_t j = 0; j < scanLen; j++) { if (scanPtr[j] != 0) return false; } - - scanPtr += TRACK_WIDTH; } return true; @@ -656,7 +636,7 @@ static bool tmpPatternEmpty(uint16_t nr, int32_t antChn) static int64_t calculateXMSize(void) { // count header size in song - int64_t currSize64 = sizeof (songHeaderTyp); + int64_t currSize64 = sizeof (xmHdr_t); // count number of patterns that would be saved int16_t ap = MAX_PATTERNS; @@ -677,9 +657,9 @@ static int64_t calculateXMSize(void) // count packed pattern data size in song for (int16_t i = 0; i < ap; i++) { - currSize64 += sizeof (patternHeaderTyp); + currSize64 += sizeof (xmPatHdr_t); if (!patternEmpty(i)) - currSize64 += getPackedPattSize(patt[i], pattLens[i], song.antChn); + currSize64 += getPackedPattSize(pattern[i], patternNumRows[i], song.numChannels); } // count instrument and sample data size in song @@ -693,15 +673,21 @@ static int64_t calculateXMSize(void) const int16_t a = getUsedSamples(i); if (a > 0) - currSize64 += INSTR_HEADER_SIZE + (a * sizeof (sampleHeaderTyp)); + currSize64 += INSTR_HEADER_SIZE + (a * sizeof (xmSmpHdr_t)); else currSize64 += 22+11; - instrTyp *ins = instr[j]; + instr_t *ins = instr[j]; for (int16_t k = 0; k < a; k++) { - if (ins->samp[k].pek != NULL) - currSize64 += ins->samp[k].len; + sample_t* s = &ins->smp[k]; + if (s->dataPtr != NULL && s->length > 0) + { + if (s->flags & SAMPLE_16BIT) + currSize64 += s->length << 1; + else + currSize64 += s->length; + } } } @@ -712,15 +698,15 @@ static int64_t calculateTrimSize(void) { int16_t i, j, k; - int32_t antChn = song.antChn; + int32_t numChannels = song.numChannels; int32_t pattDataLen = 0; int32_t newPattDataLen = 0; int64_t bytes64 = 0; int64_t oldInstrSize64 = 0; // copy over temp data - memcpy(tmpPatt, patt, sizeof (tmpPatt)); - memcpy(tmpPattLens, pattLens, sizeof (tmpPattLens)); + memcpy(tmpPatt, pattern, sizeof (tmpPatt)); + memcpy(tmpPattLens, patternNumRows, sizeof (tmpPattLens)); memcpy(tmpInstrName, song.instrName, sizeof (tmpInstrName)); if (!setTmpInstruments()) @@ -737,7 +723,7 @@ static int64_t calculateTrimSize(void) int16_t ap = MAX_PATTERNS; do { - if (tmpPatternEmpty(ap - 1, antChn)) + if (tmpPatternEmpty(ap - 1, numChannels)) ap--; else break; @@ -763,9 +749,9 @@ static int64_t calculateTrimSize(void) { for (i = 0; i < ap; i++) { - pattDataLen += sizeof (patternHeaderTyp); - if (!tmpPatternEmpty(i, antChn)) - pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn); + pattDataLen += sizeof (xmPatHdr_t); + if (!tmpPatternEmpty(i, numChannels)) + pattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels); } } @@ -776,17 +762,17 @@ static int64_t calculateTrimSize(void) int16_t highestChan = -1; for (i = 0; i < ap; i++) { - tonTyp *pattPtr = tmpPatt[i]; + note_t *pattPtr = tmpPatt[i]; if (pattPtr == NULL) continue; - const int16_t pattLen = tmpPattLens[i]; - for (j = 0; j < pattLen; j++) + const int16_t numRows = tmpPattLens[i]; + for (j = 0; j < numRows; j++) { - for (k = 0; k < antChn; k++) + for (k = 0; k < numChannels; k++) { - tonTyp *note = &pattPtr[(j * MAX_VOICES) + k]; - if (note->eff || note->effTyp || note->instr || note->ton || note->vol) + note_t *p = &pattPtr[(j * MAX_CHANNELS) + k]; + if (p->note > 0 || p->instr > 0 || p->vol > 0 || p->efx > 0 || p->efxData > 0) { if (k > highestChan) highestChan = k; @@ -802,7 +788,7 @@ static int64_t calculateTrimSize(void) if (highestChan & 1) highestChan++; - antChn = (uint8_t)(CLAMP(highestChan, 2, antChn)); + numChannels = (uint8_t)(CLAMP(highestChan, 2, numChannels)); } } @@ -814,9 +800,9 @@ static int64_t calculateTrimSize(void) { for (i = 0; i < ap; i++) { - newPattDataLen += sizeof (patternHeaderTyp); - if (!tmpPatternEmpty(i, antChn)) - newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], antChn); + newPattDataLen += sizeof (xmPatHdr_t); + if (!tmpPatternEmpty(i, numChannels)) + newPattDataLen += getPackedPattSize(tmpPatt[i], tmpPattLens[i], numChannels); } assert(pattDataLen >= newPattDataLen); @@ -826,9 +812,9 @@ static int64_t calculateTrimSize(void) } // calculate "remove unused instruments" size - if (removeInst) wipeInstrUnused(true, &ai, ap, antChn); + if (removeInst) wipeInstrUnused(true, &ai, ap, numChannels); - // calculat new instruments and samples size + // calculate new instruments and samples size if (removeInst || removeSamp || removeSmpDataAfterLoop || convSmpsTo8Bit) { int64_t newInstrSize64 = getTempInsAndSmpSize(); @@ -889,17 +875,17 @@ static int32_t SDLCALL trimThreadFunc(void *ptr) int16_t highestChan = -1; for (i = 0; i < ap; i++) { - tonTyp *pattPtr = patt[i]; + note_t *pattPtr = pattern[i]; if (pattPtr == NULL) continue; - const int16_t pattLen = pattLens[i]; - for (j = 0; j < pattLen; j++) + const int16_t numRows = patternNumRows[i]; + for (j = 0; j < numRows; j++) { - for (k = 0; k < song.antChn; k++) + for (k = 0; k < song.numChannels; k++) { - tonTyp *note = &pattPtr[(j * MAX_VOICES) + k]; - if (note->eff || note->effTyp || note->instr || note->ton || note->vol) + note_t *p = &pattPtr[(j * MAX_CHANNELS) + k]; + if (p->note > 0 || p->vol > 0 || p->instr > 0 || p->efx > 0 || p->efxData > 0) { if (k > highestChan) highestChan = k; @@ -915,21 +901,21 @@ static int32_t SDLCALL trimThreadFunc(void *ptr) if (highestChan & 1) highestChan++; - song.antChn = (uint8_t)(CLAMP(highestChan, 2, song.antChn)); + song.numChannels = (uint8_t)(CLAMP(highestChan, 2, song.numChannels)); } // clear potentially unused channel data - if (song.antChn < MAX_VOICES) + if (song.numChannels < MAX_CHANNELS) { for (i = 0; i < MAX_PATTERNS; i++) { - tonTyp *pattPtr = patt[i]; - if (pattPtr == NULL) + note_t *p = pattern[i]; + if (p == NULL) continue; - const int16_t pattLen = pattLens[i]; - for (j = 0; j < pattLen; j++) - memset(&pattPtr[(j * MAX_VOICES) + song.antChn], 0, sizeof (tonTyp) * (MAX_VOICES - song.antChn)); + const int16_t numRows = patternNumRows[i]; + for (j = 0; j < numRows; j++) + memset(&p[(j * MAX_CHANNELS) + song.numChannels], 0, sizeof (note_t) * (MAX_CHANNELS - song.numChannels)); } } } @@ -940,20 +926,19 @@ static int32_t SDLCALL trimThreadFunc(void *ptr) // remove unused instruments if (removeInst) - wipeInstrUnused(false, &ai, ap, song.antChn); + wipeInstrUnused(false, &ai, ap, song.numChannels); freeTmpInstruments(); editor.trimThreadWasDone = true; return true; - (void)ptr; } void trimThreadDone(void) { if (removePatt) - setPos(song.songPos, song.pattPos, false); + setPos(song.songPos, song.row, false); if (removeInst) { @@ -971,8 +956,8 @@ void trimThreadDone(void) { if (ui.patternEditorShown) { - if (ui.channelOffset > song.antChn-ui.numChannelsShown) - setScrollBarPos(SB_CHAN_SCROLL, song.antChn - ui.numChannelsShown, true); + if (ui.channelOffset > song.numChannels-ui.numChannelsShown) + setScrollBarPos(SB_CHAN_SCROLL, song.numChannels - ui.numChannelsShown, true); } if (cursor.ch >= ui.channelOffset+ui.numChannelsShown) diff --git a/src/ft2_video.c b/src/ft2_video.c @@ -20,7 +20,7 @@ #include "ft2_video.h" #include "ft2_events.h" #include "ft2_mouse.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_pattern_ed.h" #include "ft2_pattern_draw.h" #include "ft2_sample_ed.h" @@ -34,6 +34,7 @@ #include "ft2_midi.h" #include "ft2_bmp.h" #include "ft2_structs.h" +#include "ft2_cpu.h" static const uint8_t textCursorData[12] = { @@ -158,15 +159,23 @@ void endFPSCounter(void) void flipFrame(void) { + const uint32_t windowFlags = SDL_GetWindowFlags(video.window); + bool minimized = (windowFlags & SDL_WINDOW_MINIMIZED) ? true : false; + renderSprites(); if (video.showFPSCounter) drawFPSCounter(); SDL_UpdateTexture(video.texture, NULL, video.frameBuffer, SCREEN_W * sizeof (int32_t)); - SDL_RenderClear(video.renderer); + + // SDL2 bug on Windows (?): This function consumes ever-increasing memory if the program is minimized + if (!minimized) + SDL_RenderClear(video.renderer); + SDL_RenderCopy(video.renderer, video.texture, NULL, NULL); SDL_RenderPresent(video.renderer); + eraseSprites(); if (!video.vsync60HzPresent) @@ -175,21 +184,19 @@ void flipFrame(void) } else { - uint32_t windowFlags = SDL_GetWindowFlags(video.window); - /* We have VSync, but it can unexpectedly get inactive in certain scenarios. ** We have to force thread sleeping (to ~60Hz) if so. */ #ifdef __APPLE__ // macOS: VSync gets disabled if the window is 100% covered by another window. Let's add a (crude) fix: - if ((windowFlags & SDL_WINDOW_MINIMIZED) || !(windowFlags & SDL_WINDOW_INPUT_FOCUS)) + if (minimized || !(windowFlags & SDL_WINDOW_INPUT_FOCUS)) waitVBL(); #elif __unix__ // *NIX: VSync gets disabled in fullscreen mode (at least on some distros/systems). Let's add a fix: - if ((windowFlags & SDL_WINDOW_MINIMIZED) || video.fullscreen) + if (minimized || video.fullscreen) waitVBL(); #else - if (windowFlags & SDL_WINDOW_MINIMIZED) + if (minimized) waitVBL(); #endif } @@ -267,6 +274,7 @@ static void updateRenderSizeVars(void) // for mouse cursor creation video.xScale = (uint32_t)round(video.renderW * (1.0 / SCREEN_W)); video.yScale = (uint32_t)round(video.renderH * (1.0 / SCREEN_H)); + createMouseCursors(); } @@ -850,16 +858,16 @@ void updateWindowTitle(bool forceUpdate) songTitleTrunc[sizeof (songTitleTrunc)-1] = '\0'; if (song.isModified) - sprintf(wndTitle, "Fasttracker II clone v%s - \"%s\" (unsaved)", PROG_VER_STR, songTitleTrunc); + sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"%s\" (unsaved)", PROG_VER_STR, CPU_BITS, songTitleTrunc); else - sprintf(wndTitle, "Fasttracker II clone v%s - \"%s\"", PROG_VER_STR, songTitleTrunc); + sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"%s\"", PROG_VER_STR, CPU_BITS, songTitleTrunc); } else { if (song.isModified) - sprintf(wndTitle, "Fasttracker II clone v%s - \"untitled\" (unsaved)", PROG_VER_STR); + sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"untitled\" (unsaved)", PROG_VER_STR, CPU_BITS); else - sprintf(wndTitle, "Fasttracker II clone v%s - \"untitled\"", PROG_VER_STR); + sprintf(wndTitle, "Fasttracker II clone v%s (%d-bit) - \"untitled\"", PROG_VER_STR, CPU_BITS); } SDL_SetWindowTitle(video.window, wndTitle); @@ -1004,6 +1012,7 @@ bool setupRenderer(void) if (videoDriver != NULL && strcmp("KMSDRM", videoDriver) == 0) video.useDesktopMouseCoords = false; + SDL_SetRenderDrawColor(video.renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); return true; } @@ -1018,7 +1027,7 @@ void handleRedrawing(void) else if (ui.nibblesShown) { if (editor.NI_Play) - moveNibblePlayers(); + moveNibblesPlayers(); } else { @@ -1028,14 +1037,14 @@ void handleRedrawing(void) if (!ui.diskOpShown) { - drawSongRepS(); + drawSongLoopStart(); drawSongLength(); drawPosEdNums(editor.songPos); drawEditPattern(editor.editPattern); drawPatternLength(editor.editPattern); - drawSongBPM(editor.speed); - drawSongSpeed(editor.tempo); - drawGlobalVol(editor.globalVol); + drawSongBPM(editor.BPM); + drawSongSpeed(editor.speed); + drawGlobalVol(editor.globalVolume); if (!songPlaying || editor.wavIsRendering) setScrollBarPos(SB_POS_ED, editor.songPos, false); @@ -1057,7 +1066,7 @@ void handleRedrawing(void) { ui.updatePosEdScrollBar = false; setScrollBarPos(SB_POS_ED, song.songPos, false); - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + setScrollBarEnd(SB_POS_ED, (song.songLength - 1) + 5); } if (!ui.extended) @@ -1124,19 +1133,19 @@ static void drawReplayerData(void) if (ui.drawBPMFlag) { ui.drawBPMFlag = false; - drawSongBPM(editor.speed); + drawSongBPM(editor.BPM); } if (ui.drawSpeedFlag) { ui.drawSpeedFlag = false; - drawSongSpeed(editor.tempo); + drawSongSpeed(editor.speed); } if (ui.drawGlobVolFlag) { ui.drawGlobVolFlag = false; - drawGlobalVol(editor.globalVol); + drawGlobalVol(editor.globalVolume); } if (ui.drawPosEdFlag) @@ -1164,6 +1173,6 @@ static void drawReplayerData(void) { ui.updatePatternEditor = false; if (ui.patternEditorShown) - writePattern(editor.pattPos, editor.editPattern); + writePattern(editor.row, editor.editPattern); } } diff --git a/src/ft2_wav_renderer.c b/src/ft2_wav_renderer.c @@ -7,10 +7,11 @@ #include <stdint.h> #include <stdbool.h> #include "ft2_header.h" +#include "ft2_audio.h" #include "ft2_gui.h" #include "ft2_pattern_ed.h" #include "ft2_diskop.h" -#include "ft2_scopes.h" +#include "scopes/ft2_scopes.h" #include "ft2_config.h" #include "ft2_mouse.h" #include "ft2_sample_ed.h" @@ -36,10 +37,9 @@ typedef struct wavHeader_t uint32_t subchunk2ID, subchunk2Size; } wavHeader_t; -static char WAV_SysReqText[192]; static uint8_t WDBitDepth = 16, WDStartPos, WDStopPos, *wavRenderBuffer; static int16_t WDAmp; -static uint32_t WDFrequency = 48000; +static uint32_t WDFrequency = DEFAULT_AUDIO_FREQ; static SDL_Thread *thread; static void updateWavRenderer(void) @@ -125,7 +125,7 @@ void drawWavRenderer(void) void resetWavRenderer(void) { WDStartPos = 0; - WDStopPos = (uint8_t)song.len - 1; + WDStopPos = (uint8_t)song.songLength - 1; if (ui.wavRendererShown) updateWavRenderer(); @@ -143,7 +143,7 @@ void showWavRenderer(void) ui.scopesShown = false; WDStartPos = 0; - WDStopPos = (uint8_t)song.len - 1; + WDStopPos = (uint8_t)song.songLength - 1; drawWavRenderer(); } @@ -194,8 +194,8 @@ static bool dump_Init(uint32_t frq, int16_t amp, int16_t songPos) setAudioAmp(amp, config.masterVol, (WDBitDepth == 32)); stopVoices(); - song.globVol = 64; - P_SetSpeed(song.speed); + song.globalVolume = 64; + setMixerBPM(song.BPM); resetPlaybackTime(); return true; @@ -251,12 +251,12 @@ static void dump_Close(FILE *f, uint32_t totalSamples) stopPlaying(); // kludge: set speed to 6 if speed was set to 0 - if (song.tempo == 0) - song.tempo = 6; + if (song.speed == 0) + song.speed = 6; setBackOldAudioFreq(); - P_SetSpeed(song.speed); - setAudioAmp(config.boostLevel, config.masterVol, config.specialFlags & BITDEPTH_32); + setMixerBPM(song.BPM); + setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); editor.wavIsRendering = false; setMouseBusy(false); @@ -264,13 +264,13 @@ static void dump_Close(FILE *f, uint32_t totalSamples) static bool dump_EndOfTune(int16_t endSongPos) { - bool returnValue = (editor.wavReachedEndFlag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0); + bool returnValue = (editor.wavReachedEndFlag && song.row == 0 && song.tick == 1) || (song.speed == 0); // 8bitbubsy: FT2 bugfix for EEx (pattern delay) on first row of a pattern if (song.pattDelTime2 > 0) returnValue = false; - if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1) + if (song.songPos == endSongPos && song.row == 0 && song.tick == 1) editor.wavReachedEndFlag = true; return returnValue; @@ -291,12 +291,12 @@ void dump_TickReplayer(void) static void updateVisuals(void) { - editor.editPattern = (uint8_t)song.pattNr; - editor.pattPos = song.pattPos; + editor.editPattern = (uint8_t)song.pattNum; + editor.row = song.row; editor.songPos = song.songPos; + editor.BPM = song.BPM; editor.speed = song.speed; - editor.tempo = song.tempo; - editor.globalVol = song.globVol; + editor.globalVolume = song.globalVolume; ui.drawPosEdFlag = true; ui.drawPattNumLenFlag = true; @@ -324,7 +324,7 @@ static int32_t SDLCALL renderWavThread(void *ptr) uint32_t sampleCounter = 0; bool renderDone = false; uint8_t tickCounter = 4; - double dTickSampleCounter = 0.0; + int64_t tickSampleCounter64 = 0; editor.wavReachedEndFlag = false; while (!renderDone) @@ -341,17 +341,16 @@ static int32_t SDLCALL renderWavThread(void *ptr) break; } - if (dTickSampleCounter <= 0.0) + if (tickSampleCounter64 <= 0) // new replayer tick { - // new replayer tick dump_TickReplayer(); - dTickSampleCounter += audio.dSamplesPerTick; + tickSampleCounter64 += audio.samplesPerTick64; } - int32_t remainingTick = (int32_t)ceil(dTickSampleCounter); + int32_t remainingTick = (tickSampleCounter64 + UINT32_MAX) >> 32; // ceil (rounded upwards) mixReplayerTickToBuffer(remainingTick, ptr8, WDBitDepth); - dTickSampleCounter -= remainingTick; + tickSampleCounter64 -= (int64_t)remainingTick << 32; remainingTick *= 2; // stereo samplesInChunk += remainingTick; @@ -392,24 +391,10 @@ static int32_t SDLCALL renderWavThread(void *ptr) (void)ptr; } -static void createOverwriteText(char *name) -{ - char nameTmp[128]; - - // read entry name to a small buffer - uint32_t nameLen = (uint32_t)strlen(name); - memcpy(nameTmp, name, (nameLen >= sizeof (nameTmp)) ? sizeof (nameTmp) : (nameLen + 1)); - nameTmp[sizeof (nameTmp) - 1] = '\0'; - - trimEntryName(nameTmp, false); - - sprintf(WAV_SysReqText, "Overwrite file \"%s\"?", nameTmp); -} - static void wavRender(bool checkOverwrite) { - WDStartPos = (uint8_t)(MAX(0, MIN(WDStartPos, song.len - 1))); - WDStopPos = (uint8_t)(MAX(0, MIN(MAX(WDStartPos, WDStopPos), song.len - 1))); + WDStartPos = (uint8_t)(MAX(0, MIN(WDStartPos, song.songLength - 1))); + WDStopPos = (uint8_t)(MAX(0, MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1))); updateWavRenderer(); @@ -418,8 +403,9 @@ static void wavRender(bool checkOverwrite) char *filename = getDiskOpFilename(); if (checkOverwrite && fileExistsAnsi(filename)) { - createOverwriteText(filename); - if (okBox(2, "System request", WAV_SysReqText) != 1) + char buf[256]; + createFileOverwriteText(filename, buf); + if (okBox(2, "System request", buf) != 1) return; } @@ -457,8 +443,10 @@ void pbWavFreqUp(void) if (WDFrequency < MAX_WAV_RENDER_FREQ) { if (WDFrequency == 44100) WDFrequency = 48000; +#if CPU_64BIT else if (WDFrequency == 48000) WDFrequency = 96000; else if (WDFrequency == 96000) WDFrequency = 192000; +#endif updateWavRenderer(); } } @@ -467,10 +455,13 @@ void pbWavFreqDown(void) { if (WDFrequency > MIN_WAV_RENDER_FREQ) { +#if CPU_64BIT if (WDFrequency == 192000) WDFrequency = 96000; else if (WDFrequency == 96000) WDFrequency = 48000; else if (WDFrequency == 48000) WDFrequency = 44100; - +#else + if (WDFrequency == 48000) WDFrequency = 44100; +#endif updateWavRenderer(); } } @@ -495,11 +486,11 @@ void pbWavAmpDown(void) void pbWavSongStartUp(void) { - if (WDStartPos >= song.len-1) + if (WDStartPos >= song.songLength-1) return; WDStartPos++; - WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1)); + WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1)); updateWavRenderer(); } @@ -518,7 +509,7 @@ void pbWavSongEndUp(void) return; WDStopPos++; - WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1)); + WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1)); updateWavRenderer(); } @@ -528,7 +519,7 @@ void pbWavSongEndDown(void) return; WDStopPos--; - WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.len - 1)); + WDStopPos = (uint8_t)(MIN(MAX(WDStartPos, WDStopPos), song.songLength - 1)); updateWavRenderer(); } diff --git a/src/ft2_wav_renderer.h b/src/ft2_wav_renderer.h @@ -2,9 +2,15 @@ #include <stdint.h> #include "ft2_header.h" +#include "ft2_cpu.h" #define MIN_WAV_RENDER_FREQ 44100 + +#if CPU_64BIT #define MAX_WAV_RENDER_FREQ 192000 +#else +#define MAX_WAV_RENDER_FREQ 48000 +#endif #define MAX_WAV_RENDER_SAMPLES_PER_TICK (((MAX_WAV_RENDER_FREQ * 5) / 2) / MIN_BPM) @@ -15,7 +21,6 @@ void drawWavRenderer(void); void showWavRenderer(void); void hideWavRenderer(void); void exitWavRenderer(void); -void dump_RenderTick(uint32_t samplesPerTick, uint8_t *buffer); void pbWavRender(void); void pbWavExit(void); void pbWavFreqUp(void); diff --git a/src/helpdata/FT2.HLP b/src/helpdata/FT2.HLP @@ -9,7 +9,7 @@ >- 12-Point Volume- & Panning Envelope. >- Multisamples, up to 16 samples/instrument. >- 128 instruments. ->- "Unlimited" sample length. (1GB in this clone) +>- "Unlimited" sample length. (1GB per sample in this clone) >- 8 octaves. >- Variable pattern length. >- Built in sampler/sample editor. @@ -150,7 +150,7 @@ to the nearest halftone. Syntax: 4 + Rate + Depth Adds vibrato to the channel with a rate and speed. Set vibrato -control (E4) can be used to change the vibrato wave form (see +control (E4) can be used to change the vibrato waveform (see below). @X040@C001Tone portamento + volume slide @@ -241,11 +241,11 @@ rounded to the nearest halftone. >@X060@C002 Syntax: E4 + Type -This command controls the vibrato wave form. +This command controls the vibrato waveform. Type: 0 = Sine 1 = Ramp down 2 = Square -If you add 4 to the type, the wave form will not be retrigged when a +If you add 4 to the type, the waveform will not be retrigged when a new instrument is played. @X040@C001Set fine-tune @@ -268,7 +268,7 @@ start. Syntax: E7 + Type This command works exactly as set vibrato control, but the -tremolo wave form will be changed instead. +tremolo waveform will be changed instead. @X040@C001Retrig note >@X060@C002 @@ -377,7 +377,7 @@ will be divided by four. END ;*************************************************************************** ;*************************************************************************** -@LKeyboard +@LKeybindings >@X020@C002 >If you have an ambition to create music efficiently we strongly recommend @@ -392,6 +392,11 @@ using this program from the very first minute. references to non-ordinary keys might be wrong. Sh = shift key. > +@X040@C001Audio: +>@X060@C002 +>Ctrl & numpad+ @T160Increase master volume by 16. +>Ctrl & numpad- @T160Decrease master volume by 16. + @X040@C001Video: >@X060@C002 Alt+Enter @T160Toggle fullscreen mode @@ -600,14 +605,14 @@ A Fasttracker 2 instrument is: > 1 Volume envelope > 1 Panning envelope > 1 Auto-vibrato definition -> 1..16 Sample(s) +> 1..16 sample(s) > 1 Keyboard split definition > 1 MIDI definition >A Fasttracker 2 sample is: -> 1 Volume/Panning/Fine-tune definition -> 1 Relative tone. -> 1 Wave form. +> 1 Volume/Panning/Finetune definition +> 1 Relative note +> 1 Waveform >@X040@C001The volume envelope: >@X060@C002 @@ -632,10 +637,12 @@ vibrato off. >Same as above, except from that the vibrato is not connected to the panning envelope. ->@X040@C001Tune: +>@X040@C001Tune (finetune): >@X060@C002 ->The fine-tune resolution has been changed from a signed nibble +>The finetune resolution has been changed from a signed nibble (-8..+7) to a signed byte (-128..+127). +>NOTE: The last 3 bits are discarded during playback, so the true step +size is 8 instead of 1. >@X040@C001Fadeout: >@X060@C002 @@ -656,7 +663,7 @@ keyboard. >@X040@C001Important note: >@X060@C002 ->The volume, panning, tune and relative tone is defined for EACH +>The volume, panning, finetune and relative note is defined for EACH SAMPLE in an instrument. All other information is defined for the entire instrument. @@ -688,11 +695,11 @@ commands correctly. @X020@C001Sample Editor: > ->@X040@C001Play (Wave form, range, display): +>@X040@C001Play (Waveform, range, display): >@X060@C002 ->Plays the current sample with tone display above the "stop" +>Plays the current sample with the note displayed above the "stop" button. Note that respect is taken to the particular sample's -relative tone. +relative note. >@X040@C001Save range: >@X060@C002 @@ -742,11 +749,11 @@ destination is the current instr./sample. >@X060@C002 >Operates on the range (or the whole sample if no range is set). ->@X040@C001Convert: +>@X040@C001Sign: >@X060@C002 ->Converts the entire sample from/to signed/unsigned. +>Converts between signed/unsigned. ->@X040@C001Convert W: +>@X040@C001B. swap (byte swap): >@X060@C002 Swaps the byte order to/from Intel from/to Motorola standard on the entire sample. @@ -765,7 +772,7 @@ fail depending on the sample data. >@X040@C001Resample: >@X060@C002 -Operates on the entire sample. The sample's relative tone is +Operates on the entire sample. The sample's relative note is changed with respect to the resampling rate. >@X040@C001Mix sample: @@ -823,7 +830,7 @@ hex counting since there are only 2 digits in the line number column. >@X040@C001Scopes: >@X060@C002 "Std." (standard) will show the sample points as pixels (like FT2). -"Lined" will draw lines between the points, like an oscilloscope. +"Lined" will draw interpolated samples (linear interpolation. @X020@C001Configuration, Miscellaneous: > diff --git a/src/helpdata/ft2_help_data.h b/src/helpdata/ft2_help_data.h @@ -3,9 +3,9 @@ #include <stdint.h> -#define HELP_DATA_LEN 27385 +#define HELP_DATA_LEN 27622 -const uint8_t helpData[27385] = +const uint8_t helpData[27622] = { 0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, @@ -38,1547 +38,1568 @@ const uint8_t helpData[27385] = 0x70,0x20,0x74,0x6F,0x20,0x31,0x36,0x20,0x73,0x61,0x6D,0x70, 0x6C,0x65,0x73,0x2F,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, 0x6E,0x74,0x2E,0x13,0x3E,0x2D,0x20,0x31,0x32,0x38,0x20,0x69, - 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x31, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x2E,0x3C, 0x3E,0x2D,0x20,0x22,0x55,0x6E,0x6C,0x69,0x6D,0x69,0x74,0x65, 0x64,0x22,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x6C,0x65, - 0x6E,0x67,0x74,0x68,0x2E,0x20,0x28,0x31,0x47,0x42,0x20,0x69, - 0x6E,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6C,0x6F,0x6E,0x65, - 0x29,0x0D,0x3E,0x2D,0x20,0x38,0x20,0x6F,0x63,0x74,0x61,0x76, - 0x65,0x73,0x2E,0x1B,0x3E,0x2D,0x20,0x56,0x61,0x72,0x69,0x61, - 0x62,0x6C,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20, - 0x6C,0x65,0x6E,0x67,0x74,0x68,0x2E,0x22,0x3E,0x2D,0x20,0x42, - 0x75,0x69,0x6C,0x74,0x20,0x69,0x6E,0x20,0x73,0x61,0x6D,0x70, - 0x6C,0x65,0x72,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65, - 0x64,0x69,0x74,0x6F,0x72,0x2E,0x16,0x3E,0x2D,0x20,0x55,0x70, - 0x20,0x74,0x6F,0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x74,0x74, - 0x65,0x72,0x6E,0x73,0x2E,0x19,0x3E,0x2D,0x20,0x53,0x6F,0x6E, - 0x67,0x20,0x6C,0x65,0x6E,0x67,0x74,0x68,0x20,0x75,0x70,0x20, - 0x74,0x6F,0x20,0x32,0x35,0x36,0x2E,0x25,0x3E,0x2D,0x20,0x4E, - 0x65,0x77,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2F,0x70,0x61, - 0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x76,0x69,0x62,0x72,0x61,0x74, - 0x6F,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x0F,0x3E,0x2D, - 0x20,0x53,0x6F,0x6E,0x67,0x20,0x65,0x64,0x69,0x74,0x6F,0x72, - 0x2E,0x19,0x3E,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63, - 0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x6F, - 0x64,0x65,0x2E,0x1F,0x3E,0x2D,0x20,0x49,0x6D,0x70,0x72,0x6F, - 0x76,0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x69,0x6E,0x67,0x20, - 0x66,0x61,0x63,0x69,0x6C,0x69,0x74,0x69,0x65,0x73,0x2E,0x00, - 0x3C,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54, - 0x68,0x65,0x20,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65, - 0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x73,0x20,0x74,0x68, - 0x65,0x20,0x66,0x6F,0x6C,0x6C,0x6F,0x77,0x69,0x6E,0x67,0x20, - 0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61,0x74,0x73, - 0x3A,0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, - 0x31,0x4D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x2D, - 0x20,0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x6D,0x6F, - 0x64,0x75,0x6C,0x65,0x73,0x20,0x28,0x31,0x35,0x2F,0x33,0x31, - 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x29,0x2E,0x20,0x28, - 0x4D,0x4F,0x44,0x2C,0x4E,0x53,0x54,0x2C,0x53,0x54,0x4B,0x29, - 0x23,0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63, - 0x6B,0x65,0x72,0x20,0x49,0x49,0x20,0x6D,0x6F,0x64,0x75,0x6C, - 0x65,0x73,0x2E,0x20,0x28,0x58,0x4D,0x2C,0x4D,0x4F,0x44,0x29, - 0x21,0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63, - 0x6B,0x65,0x72,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E, - 0x20,0x28,0x4D,0x4F,0x44,0x2F,0x46,0x53,0x54,0x29,0x00,0x32, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78, - 0x70,0x65,0x72,0x69,0x6D,0x65,0x6E,0x74,0x61,0x6C,0x20,0x28, - 0x69,0x6D,0x70,0x65,0x72,0x66,0x65,0x63,0x74,0x29,0x20,0x6D, - 0x6F,0x64,0x75,0x6C,0x65,0x20,0x73,0x75,0x70,0x70,0x6F,0x72, - 0x74,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D, - 0x20,0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x33,0x20,0x6D, - 0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x33,0x4D, - 0x29,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20, - 0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x6D,0x6F, - 0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x54,0x4D,0x29, - 0x29,0x3E,0x2D,0x20,0x44,0x49,0x47,0x49,0x20,0x42,0x6F,0x6F, - 0x73,0x74,0x65,0x72,0x20,0x28,0x6E,0x6F,0x6E,0x2D,0x50,0x72, - 0x6F,0x29,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20, - 0x28,0x44,0x49,0x47,0x49,0x29,0x00,0x12,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65, - 0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x22,0x3E,0x2D,0x20,0x47,0x72,0x61,0x76,0x69,0x73, - 0x20,0x55,0x6C,0x74,0x72,0x61,0x73,0x6F,0x75,0x6E,0x64,0x20, - 0x50,0x61,0x74,0x63,0x68,0x65,0x73,0x2C,0x20,0x50,0x41,0x54, - 0x2E,0x33,0x3E,0x2D,0x20,0x53,0x4D,0x50,0x2F,0x53,0x41,0x4D, - 0x2F,0x52,0x41,0x57,0x2F,0x53,0x4E,0x44,0x20,0x64,0x61,0x74, - 0x61,0x20,0x66,0x69,0x6C,0x65,0x73,0x2C,0x20,0x73,0x69,0x67, - 0x6E,0x65,0x64,0x20,0x61,0x6E,0x64,0x20,0x75,0x6E,0x73,0x69, - 0x67,0x6E,0x65,0x64,0x2E,0x0D,0x3E,0x2D,0x20,0x57,0x41,0x56, - 0x20,0x66,0x69,0x6C,0x65,0x73,0x2E,0x0D,0x3E,0x2D,0x20,0x41, - 0x6D,0x69,0x67,0x61,0x20,0x49,0x46,0x46,0x2E,0x0E,0x3E,0x2D, - 0x20,0x41,0x70,0x70,0x6C,0x65,0x20,0x41,0x49,0x46,0x46,0x2E, - 0x00,0x32,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x46,0x54,0x32,0x20,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63, - 0x65,0x73,0x20,0x73,0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6E, - 0x65,0x77,0x20,0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D, - 0x61,0x74,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x1C,0x3E,0x2D,0x20,0x58,0x4D,0x20,0x20, - 0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65, - 0x64,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x2E,0x20,0x3E,0x2D, - 0x20,0x58,0x49,0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78, - 0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x72, - 0x75,0x6D,0x65,0x6E,0x74,0x2E,0x1D,0x3E,0x2D,0x20,0x58,0x50, - 0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E, - 0x64,0x65,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E, - 0x1B,0x3E,0x2D,0x20,0x58,0x54,0x20,0x20,0x40,0x54,0x31,0x31, - 0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x74,0x72, - 0x61,0x63,0x6B,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A, + 0x6E,0x67,0x74,0x68,0x2E,0x20,0x28,0x31,0x47,0x42,0x20,0x70, + 0x65,0x72,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x6E, + 0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x29, + 0x0D,0x3E,0x2D,0x20,0x38,0x20,0x6F,0x63,0x74,0x61,0x76,0x65, + 0x73,0x2E,0x1B,0x3E,0x2D,0x20,0x56,0x61,0x72,0x69,0x61,0x62, + 0x6C,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C, + 0x65,0x6E,0x67,0x74,0x68,0x2E,0x22,0x3E,0x2D,0x20,0x42,0x75, + 0x69,0x6C,0x74,0x20,0x69,0x6E,0x20,0x73,0x61,0x6D,0x70,0x6C, + 0x65,0x72,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64, + 0x69,0x74,0x6F,0x72,0x2E,0x16,0x3E,0x2D,0x20,0x55,0x70,0x20, + 0x74,0x6F,0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x74,0x74,0x65, + 0x72,0x6E,0x73,0x2E,0x19,0x3E,0x2D,0x20,0x53,0x6F,0x6E,0x67, + 0x20,0x6C,0x65,0x6E,0x67,0x74,0x68,0x20,0x75,0x70,0x20,0x74, + 0x6F,0x20,0x32,0x35,0x36,0x2E,0x25,0x3E,0x2D,0x20,0x4E,0x65, + 0x77,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2F,0x70,0x61,0x6E, + 0x6E,0x69,0x6E,0x67,0x2F,0x76,0x69,0x62,0x72,0x61,0x74,0x6F, + 0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x0F,0x3E,0x2D,0x20, + 0x53,0x6F,0x6E,0x67,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x2E, + 0x19,0x3E,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63,0x72, + 0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x20,0x6D,0x6F,0x64, + 0x65,0x2E,0x1F,0x3E,0x2D,0x20,0x49,0x6D,0x70,0x72,0x6F,0x76, + 0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x69,0x6E,0x67,0x20,0x66, + 0x61,0x63,0x69,0x6C,0x69,0x74,0x69,0x65,0x73,0x2E,0x00,0x3C, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68, + 0x65,0x20,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20, + 0x73,0x75,0x70,0x70,0x6F,0x72,0x74,0x73,0x20,0x74,0x68,0x65, + 0x20,0x66,0x6F,0x6C,0x6C,0x6F,0x77,0x69,0x6E,0x67,0x20,0x66, + 0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61,0x74,0x73,0x3A, + 0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x4D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x2D,0x20, + 0x53,0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x20,0x6D,0x6F,0x64, + 0x75,0x6C,0x65,0x73,0x20,0x28,0x31,0x35,0x2F,0x33,0x31,0x20, + 0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x29,0x2E,0x20,0x28,0x4D, + 0x4F,0x44,0x2C,0x4E,0x53,0x54,0x2C,0x53,0x54,0x4B,0x29,0x23, + 0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B, + 0x65,0x72,0x20,0x49,0x49,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65, + 0x73,0x2E,0x20,0x28,0x58,0x4D,0x2C,0x4D,0x4F,0x44,0x29,0x21, + 0x3E,0x2D,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B, + 0x65,0x72,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20, + 0x28,0x4D,0x4F,0x44,0x2F,0x46,0x53,0x54,0x29,0x00,0x32,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70, + 0x65,0x72,0x69,0x6D,0x65,0x6E,0x74,0x61,0x6C,0x20,0x28,0x69, + 0x6D,0x70,0x65,0x72,0x66,0x65,0x63,0x74,0x29,0x20,0x6D,0x6F, + 0x64,0x75,0x6C,0x65,0x20,0x73,0x75,0x70,0x70,0x6F,0x72,0x74, + 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, + 0x32,0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20, + 0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x33,0x20,0x6D,0x6F, + 0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x33,0x4D,0x29, + 0x22,0x3E,0x2D,0x20,0x53,0x63,0x72,0x65,0x61,0x6D,0x20,0x54, + 0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x6D,0x6F,0x64, + 0x75,0x6C,0x65,0x73,0x2E,0x20,0x28,0x53,0x54,0x4D,0x29,0x29, + 0x3E,0x2D,0x20,0x44,0x49,0x47,0x49,0x20,0x42,0x6F,0x6F,0x73, + 0x74,0x65,0x72,0x20,0x28,0x6E,0x6F,0x6E,0x2D,0x50,0x72,0x6F, + 0x29,0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x73,0x2E,0x20,0x28, + 0x44,0x49,0x47,0x49,0x29,0x00,0x12,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x73, + 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, + 0x32,0x22,0x3E,0x2D,0x20,0x47,0x72,0x61,0x76,0x69,0x73,0x20, + 0x55,0x6C,0x74,0x72,0x61,0x73,0x6F,0x75,0x6E,0x64,0x20,0x50, + 0x61,0x74,0x63,0x68,0x65,0x73,0x2C,0x20,0x50,0x41,0x54,0x2E, + 0x33,0x3E,0x2D,0x20,0x53,0x4D,0x50,0x2F,0x53,0x41,0x4D,0x2F, + 0x52,0x41,0x57,0x2F,0x53,0x4E,0x44,0x20,0x64,0x61,0x74,0x61, + 0x20,0x66,0x69,0x6C,0x65,0x73,0x2C,0x20,0x73,0x69,0x67,0x6E, + 0x65,0x64,0x20,0x61,0x6E,0x64,0x20,0x75,0x6E,0x73,0x69,0x67, + 0x6E,0x65,0x64,0x2E,0x0D,0x3E,0x2D,0x20,0x57,0x41,0x56,0x20, + 0x66,0x69,0x6C,0x65,0x73,0x2E,0x0D,0x3E,0x2D,0x20,0x41,0x6D, + 0x69,0x67,0x61,0x20,0x49,0x46,0x46,0x2E,0x0E,0x3E,0x2D,0x20, + 0x41,0x70,0x70,0x6C,0x65,0x20,0x41,0x49,0x46,0x46,0x2E,0x00, + 0x32,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46, + 0x54,0x32,0x20,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65, + 0x73,0x20,0x73,0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6E,0x65, + 0x77,0x20,0x66,0x69,0x6C,0x65,0x20,0x66,0x6F,0x72,0x6D,0x61, + 0x74,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x1C,0x3E,0x2D,0x20,0x58,0x4D,0x20,0x20,0x40, + 0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64, + 0x20,0x6D,0x6F,0x64,0x75,0x6C,0x65,0x2E,0x20,0x3E,0x2D,0x20, + 0x58,0x49,0x20,0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74, + 0x65,0x6E,0x64,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, + 0x6D,0x65,0x6E,0x74,0x2E,0x1D,0x3E,0x2D,0x20,0x58,0x50,0x20, + 0x20,0x40,0x54,0x31,0x31,0x30,0x45,0x78,0x74,0x65,0x6E,0x64, + 0x65,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1B, + 0x3E,0x2D,0x20,0x58,0x54,0x20,0x20,0x40,0x54,0x31,0x31,0x30, + 0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x74,0x72,0x61, + 0x63,0x6B,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x09,0x40,0x4C,0x45,0x66, - 0x66,0x65,0x63,0x74,0x73,0x00,0x18,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x53,0x68,0x6F,0x72,0x74,0x20,0x73, - 0x75,0x6D,0x6D,0x61,0x72,0x79,0x3A,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x0B,0x3E,0x30,0x20,0x41, - 0x72,0x70,0x65,0x67,0x67,0x69,0x6F,0x10,0x3E,0x31,0x20,0x50, - 0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70, - 0x12,0x3E,0x32,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, - 0x74,0x6F,0x20,0x64,0x6F,0x77,0x6E,0x12,0x3E,0x33,0x20,0x54, - 0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, - 0x74,0x6F,0x0A,0x3E,0x34,0x20,0x56,0x69,0x62,0x72,0x61,0x74, - 0x6F,0x1C,0x3E,0x35,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65, - 0x6E,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65, - 0x20,0x73,0x6C,0x69,0x64,0x65,0x19,0x3E,0x36,0x20,0x56,0x69, - 0x62,0x72,0x61,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75, - 0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x37,0x20, - 0x54,0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x17,0x3E,0x38,0x20,0x53, - 0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70, - 0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x39,0x20,0x53, - 0x61,0x6D,0x70,0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74, - 0x0F,0x3E,0x41,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73, - 0x6C,0x69,0x64,0x65,0x10,0x3E,0x42,0x20,0x50,0x6F,0x73,0x69, - 0x74,0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0D,0x3E,0x43, - 0x20,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x10, - 0x3E,0x44,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x62, - 0x72,0x65,0x61,0x6B,0x04,0x3E,0x45,0x20,0x2B,0x23,0x3E,0x40, - 0x58,0x30,0x38,0x30,0x30,0x20,0x46,0x69,0x6C,0x74,0x65,0x72, - 0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x20,0x28,0x41,0x6D,0x69, - 0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x15,0x3E,0x31, - 0x20,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D, - 0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x17,0x3E,0x32,0x20,0x46, - 0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, - 0x74,0x6F,0x20,0x64,0x6F,0x77,0x6E,0x18,0x3E,0x33,0x20,0x53, - 0x65,0x74,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F, - 0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x16,0x3E,0x34,0x20, - 0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20, - 0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x10,0x3E,0x35,0x20,0x53, - 0x65,0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65, - 0x0C,0x3E,0x36,0x20,0x4A,0x75,0x6D,0x70,0x20,0x6C,0x6F,0x6F, - 0x70,0x16,0x3E,0x37,0x20,0x53,0x65,0x74,0x20,0x74,0x72,0x65, - 0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C, - 0x09,0x3E,0x38,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x0E,0x3E, - 0x39,0x20,0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74, - 0x65,0x17,0x3E,0x41,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75, - 0x70,0x19,0x3E,0x42,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x64, - 0x6F,0x77,0x6E,0x0B,0x3E,0x43,0x20,0x4E,0x6F,0x74,0x65,0x20, - 0x63,0x75,0x74,0x0D,0x3E,0x44,0x20,0x4E,0x6F,0x74,0x65,0x20, - 0x64,0x65,0x6C,0x61,0x79,0x10,0x3E,0x45,0x20,0x50,0x61,0x74, - 0x74,0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x1D,0x3E, - 0x46,0x20,0x46,0x75,0x6E,0x6B,0x20,0x69,0x74,0x21,0x20,0x28, - 0x4E,0x6F,0x74,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E, - 0x74,0x65,0x64,0x29,0x06,0x3E,0x40,0x58,0x30,0x36,0x30,0x0B, - 0x46,0x20,0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x14, - 0x3E,0x47,0x20,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61, - 0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x16,0x3E,0x48,0x20, - 0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D, - 0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x4B,0x20,0x4B, - 0x65,0x79,0x20,0x6F,0x66,0x66,0x18,0x3E,0x4C,0x20,0x53,0x65, - 0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70, - 0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x50,0x20,0x50, - 0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65, - 0x14,0x3E,0x52,0x20,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65, - 0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x09,0x3E,0x54, - 0x20,0x54,0x72,0x65,0x6D,0x6F,0x72,0x04,0x3E,0x58,0x20,0x2B, - 0x20,0x3E,0x40,0x58,0x30,0x38,0x30,0x31,0x20,0x45,0x78,0x74, - 0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74, - 0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x1D,0x3E,0x32, - 0x20,0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20, - 0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x64, - 0x6F,0x77,0x6E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43, - 0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F, - 0x6C,0x75,0x6D,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, - 0x40,0x43,0x30,0x30,0x32,0x17,0x30,0x30,0x2E,0x2E,0x34,0x30, - 0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x2E,0x01,0x3E,0x1A,0x3E,0x2D,0x20,0x40, - 0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73, - 0x6C,0x69,0x64,0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x18,0x3E, - 0x2B,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D, - 0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x35, - 0x3E,0x44,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x69,0x6E,0x65, - 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64, - 0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x28,0x49,0x6E,0x64, - 0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79, - 0x6D,0x62,0x6F,0x6C,0x29,0x33,0x3E,0x55,0x20,0x40,0x54,0x31, - 0x36,0x30,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D, - 0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x20, - 0x28,0x49,0x6E,0x64,0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62, - 0x79,0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C,0x29,0x1A,0x3E,0x53, - 0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x69, - 0x62,0x72,0x61,0x74,0x6F,0x20,0x73,0x70,0x65,0x65,0x64,0x2E, - 0x10,0x3E,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x69,0x62, - 0x72,0x61,0x74,0x6F,0x2E,0x1D,0x3E,0x50,0x20,0x40,0x54,0x31, - 0x36,0x30,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E, - 0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x32, - 0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x6E,0x6E, - 0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x72,0x69, - 0x67,0x68,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63,0x61, - 0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62,0x6F, - 0x6C,0x29,0x31,0x3E,0x4C,0x20,0x40,0x54,0x31,0x36,0x30,0x50, - 0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65, - 0x20,0x6C,0x65,0x66,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x09,0x40,0x4C,0x45,0x66,0x66, + 0x65,0x63,0x74,0x73,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x53,0x68,0x6F,0x72,0x74,0x20,0x73,0x75, + 0x6D,0x6D,0x61,0x72,0x79,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x0B,0x3E,0x30,0x20,0x41,0x72, + 0x70,0x65,0x67,0x67,0x69,0x6F,0x10,0x3E,0x31,0x20,0x50,0x6F, + 0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x12, + 0x3E,0x32,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74, + 0x6F,0x20,0x64,0x6F,0x77,0x6E,0x12,0x3E,0x33,0x20,0x54,0x6F, + 0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74, + 0x6F,0x0A,0x3E,0x34,0x20,0x56,0x69,0x62,0x72,0x61,0x74,0x6F, + 0x1C,0x3E,0x35,0x20,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, + 0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20, + 0x73,0x6C,0x69,0x64,0x65,0x19,0x3E,0x36,0x20,0x56,0x69,0x62, + 0x72,0x61,0x74,0x6F,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D, + 0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x37,0x20,0x54, + 0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x17,0x3E,0x38,0x20,0x53,0x65, + 0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F, + 0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x39,0x20,0x53,0x61, + 0x6D,0x70,0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0F, + 0x3E,0x41,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C, + 0x69,0x64,0x65,0x10,0x3E,0x42,0x20,0x50,0x6F,0x73,0x69,0x74, + 0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0D,0x3E,0x43,0x20, + 0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x10,0x3E, + 0x44,0x20,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x62,0x72, + 0x65,0x61,0x6B,0x04,0x3E,0x45,0x20,0x2B,0x23,0x3E,0x40,0x58, + 0x30,0x38,0x30,0x30,0x20,0x46,0x69,0x6C,0x74,0x65,0x72,0x20, + 0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x20,0x28,0x41,0x6D,0x69,0x67, + 0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x15,0x3E,0x31,0x20, + 0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65, + 0x6E,0x74,0x6F,0x20,0x75,0x70,0x17,0x3E,0x32,0x20,0x46,0x69, + 0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74, + 0x6F,0x20,0x64,0x6F,0x77,0x6E,0x18,0x3E,0x33,0x20,0x53,0x65, + 0x74,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,0x20, + 0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x16,0x3E,0x34,0x20,0x53, + 0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63, + 0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x10,0x3E,0x35,0x20,0x53,0x65, + 0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x0C, + 0x3E,0x36,0x20,0x4A,0x75,0x6D,0x70,0x20,0x6C,0x6F,0x6F,0x70, + 0x16,0x3E,0x37,0x20,0x53,0x65,0x74,0x20,0x74,0x72,0x65,0x6D, + 0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x09, + 0x3E,0x38,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x0E,0x3E,0x39, + 0x20,0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65, + 0x17,0x3E,0x41,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70, + 0x19,0x3E,0x42,0x20,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x64,0x6F, + 0x77,0x6E,0x0B,0x3E,0x43,0x20,0x4E,0x6F,0x74,0x65,0x20,0x63, + 0x75,0x74,0x0D,0x3E,0x44,0x20,0x4E,0x6F,0x74,0x65,0x20,0x64, + 0x65,0x6C,0x61,0x79,0x10,0x3E,0x45,0x20,0x50,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x1D,0x3E,0x46, + 0x20,0x46,0x75,0x6E,0x6B,0x20,0x69,0x74,0x21,0x20,0x28,0x4E, + 0x6F,0x74,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74, + 0x65,0x64,0x29,0x06,0x3E,0x40,0x58,0x30,0x36,0x30,0x0B,0x46, + 0x20,0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x14,0x3E, + 0x47,0x20,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C, + 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x16,0x3E,0x48,0x20,0x47, + 0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x73,0x6C,0x69,0x64,0x65,0x0A,0x3E,0x4B,0x20,0x4B,0x65, + 0x79,0x20,0x6F,0x66,0x66,0x18,0x3E,0x4C,0x20,0x53,0x65,0x74, + 0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F, + 0x73,0x69,0x74,0x69,0x6F,0x6E,0x10,0x3E,0x50,0x20,0x50,0x61, + 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x14, + 0x3E,0x52,0x20,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65,0x74, + 0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x09,0x3E,0x54,0x20, + 0x54,0x72,0x65,0x6D,0x6F,0x72,0x04,0x3E,0x58,0x20,0x2B,0x20, + 0x3E,0x40,0x58,0x30,0x38,0x30,0x31,0x20,0x45,0x78,0x74,0x72, + 0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61, + 0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x1D,0x3E,0x32,0x20, + 0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70, + 0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x64,0x6F, + 0x77,0x6E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C, + 0x75,0x6D,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, + 0x43,0x30,0x30,0x32,0x17,0x30,0x30,0x2E,0x2E,0x34,0x30,0x20, + 0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x2E,0x01,0x3E,0x1A,0x3E,0x2D,0x20,0x40,0x54, + 0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C, + 0x69,0x64,0x65,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x18,0x3E,0x2B, + 0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x35,0x3E, + 0x44,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x69,0x6E,0x65,0x20, + 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65, + 0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69, 0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D, - 0x62,0x6F,0x6C,0x29,0x18,0x3E,0x4D,0x20,0x40,0x54,0x31,0x36, - 0x30,0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D, - 0x65,0x6E,0x74,0x6F,0x2E,0x00,0x00,0x1B,0x40,0x4C,0x40,0x58, - 0x30,0x30,0x30,0x44,0x65,0x74,0x61,0x69,0x6C,0x65,0x64,0x20, - 0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x00, - 0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41, - 0x72,0x70,0x65,0x67,0x67,0x69,0x6F,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x27,0x53,0x79,0x6E,0x74, - 0x61,0x78,0x3A,0x20,0x30,0x20,0x2B,0x20,0x31,0x73,0x74,0x20, - 0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x20,0x2B,0x20,0x32, - 0x6E,0x64,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x00, - 0x0D,0x45,0x78,0x2E,0x3A,0x20,0x43,0x2D,0x31,0x20,0x20,0x30, - 0x33,0x37,0x00,0x16,0x3E,0x31,0x30,0x20,0x50,0x6C,0x61,0x79, - 0x73,0x20,0x43,0x2D,0x31,0x20,0x74,0x69,0x63,0x6B,0x20,0x23, - 0x31,0x2E,0x26,0x3E,0x32,0x30,0x20,0x50,0x6C,0x61,0x79,0x73, - 0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x33,0x20,0x4E,0x6F,0x74, - 0x65,0x73,0x20,0x3D,0x20,0x44,0x23,0x31,0x20,0x74,0x69,0x63, - 0x6B,0x20,0x23,0x32,0x2E,0x26,0x3E,0x33,0x30,0x20,0x50,0x6C, - 0x61,0x79,0x73,0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x37,0x20, - 0x4E,0x6F,0x74,0x65,0x73,0x20,0x3D,0x20,0x47,0x2D,0x31,0x20, - 0x74,0x69,0x63,0x6B,0x20,0x23,0x33,0x2E,0x0B,0x3E,0x34,0x30, - 0x20,0x47,0x6F,0x74,0x6F,0x20,0x31,0x30,0x00,0x1C,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x72,0x74, - 0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F, - 0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x28, - 0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70, - 0x65,0x65,0x64,0x00,0x40,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65, - 0x6E,0x74,0x6F,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20, - 0x74,0x6F,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x68,0x65, - 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x69,0x74,0x63, - 0x68,0x20,0x75,0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E, - 0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x42,0x64,0x6F, - 0x6E,0x65,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x65, - 0x20,0x70,0x65,0x72,0x69,0x6F,0x64,0x20,0x76,0x61,0x6C,0x75, - 0x65,0x2E,0x20,0x49,0x66,0x20,0x41,0x6D,0x69,0x67,0x61,0x20, - 0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61, - 0x62,0x6C,0x65,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C, - 0x20,0x74,0x68,0x65,0x40,0x73,0x6C,0x69,0x64,0x69,0x6E,0x67, - 0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x6E,0x6F,0x6E, - 0x2D,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x28,0x74,0x68,0x65, - 0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x64,0x65,0x70,0x65,0x6E, - 0x64,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x66,0x72, - 0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x29,0x2E,0x00,0x19,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E, - 0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F, - 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x33,0x20,0x2B, - 0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73, - 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x69,0x73,0x20, - 0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65, - 0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74, - 0x65,0x2C,0x20,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20, - 0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x6F,0x20,0x69,0x74,0x73, - 0x43,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x2E,0x20, - 0x49,0x66,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F, - 0x20,0x28,0x45,0x33,0x29,0x20,0x69,0x73,0x20,0x75,0x73,0x65, - 0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75, - 0x65,0x6E,0x63,0x79,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65, - 0x20,0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x18,0x74,0x6F,0x20, - 0x74,0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20, - 0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E,0x00,0x11,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62, - 0x72,0x61,0x74,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x34,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20, - 0x44,0x65,0x70,0x74,0x68,0x00,0x3E,0x41,0x64,0x64,0x73,0x20, - 0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74, - 0x68,0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x77, - 0x69,0x74,0x68,0x20,0x61,0x20,0x72,0x61,0x74,0x65,0x20,0x61, - 0x6E,0x64,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x20,0x53,0x65, - 0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x3D,0x63,0x6F, - 0x6E,0x74,0x72,0x6F,0x6C,0x20,0x28,0x45,0x34,0x29,0x20,0x63, - 0x61,0x6E,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74, - 0x6F,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x65, - 0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x61,0x76, - 0x65,0x20,0x66,0x6F,0x72,0x6D,0x20,0x28,0x73,0x65,0x65,0x07, - 0x62,0x65,0x6C,0x6F,0x77,0x29,0x2E,0x00,0x28,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65,0x20, - 0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x2B, + 0x62,0x6F,0x6C,0x29,0x33,0x3E,0x55,0x20,0x40,0x54,0x31,0x36, + 0x30,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2E,0x20,0x28, + 0x49,0x6E,0x64,0x69,0x63,0x61,0x74,0x65,0x64,0x20,0x62,0x79, + 0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C,0x29,0x1A,0x3E,0x53,0x20, + 0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x74,0x20,0x76,0x69,0x62, + 0x72,0x61,0x74,0x6F,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x10, + 0x3E,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x56,0x69,0x62,0x72, + 0x61,0x74,0x6F,0x2E,0x1D,0x3E,0x50,0x20,0x40,0x54,0x31,0x36, + 0x30,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67, + 0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x32,0x3E, + 0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x6E,0x6E,0x69, + 0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x72,0x69,0x67, + 0x68,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63,0x61,0x74, + 0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62,0x6F,0x6C, + 0x29,0x31,0x3E,0x4C,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61, + 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x20, + 0x6C,0x65,0x66,0x74,0x2E,0x20,0x28,0x49,0x6E,0x64,0x69,0x63, + 0x61,0x74,0x65,0x64,0x20,0x62,0x79,0x20,0x73,0x79,0x6D,0x62, + 0x6F,0x6C,0x29,0x18,0x3E,0x4D,0x20,0x40,0x54,0x31,0x36,0x30, + 0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65, + 0x6E,0x74,0x6F,0x2E,0x00,0x00,0x1B,0x40,0x4C,0x40,0x58,0x30, + 0x30,0x30,0x44,0x65,0x74,0x61,0x69,0x6C,0x65,0x64,0x20,0x69, + 0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x00,0x12, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x72, + 0x70,0x65,0x67,0x67,0x69,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x27,0x53,0x79,0x6E,0x74,0x61, + 0x78,0x3A,0x20,0x30,0x20,0x2B,0x20,0x31,0x73,0x74,0x20,0x68, + 0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x20,0x2B,0x20,0x32,0x6E, + 0x64,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x00,0x0D, + 0x45,0x78,0x2E,0x3A,0x20,0x43,0x2D,0x31,0x20,0x20,0x30,0x33, + 0x37,0x00,0x16,0x3E,0x31,0x30,0x20,0x50,0x6C,0x61,0x79,0x73, + 0x20,0x43,0x2D,0x31,0x20,0x74,0x69,0x63,0x6B,0x20,0x23,0x31, + 0x2E,0x26,0x3E,0x32,0x30,0x20,0x50,0x6C,0x61,0x79,0x73,0x20, + 0x43,0x2D,0x31,0x20,0x2B,0x20,0x33,0x20,0x4E,0x6F,0x74,0x65, + 0x73,0x20,0x3D,0x20,0x44,0x23,0x31,0x20,0x74,0x69,0x63,0x6B, + 0x20,0x23,0x32,0x2E,0x26,0x3E,0x33,0x30,0x20,0x50,0x6C,0x61, + 0x79,0x73,0x20,0x43,0x2D,0x31,0x20,0x2B,0x20,0x37,0x20,0x4E, + 0x6F,0x74,0x65,0x73,0x20,0x3D,0x20,0x47,0x2D,0x31,0x20,0x74, + 0x69,0x63,0x6B,0x20,0x23,0x33,0x2E,0x0B,0x3E,0x34,0x30,0x20, + 0x47,0x6F,0x74,0x6F,0x20,0x31,0x30,0x00,0x1C,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x72,0x74,0x61, + 0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77, + 0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, + 0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x28,0x31, + 0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70,0x65, + 0x65,0x64,0x00,0x40,0x50,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, + 0x74,0x6F,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,0x74, + 0x6F,0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x74,0x68,0x65,0x20, + 0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x69,0x74,0x63,0x68, + 0x20,0x75,0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E, + 0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x42,0x64,0x6F,0x6E, + 0x65,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20, + 0x70,0x65,0x72,0x69,0x6F,0x64,0x20,0x76,0x61,0x6C,0x75,0x65, + 0x2E,0x20,0x49,0x66,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x66, + 0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62, + 0x6C,0x65,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C,0x20, + 0x74,0x68,0x65,0x40,0x73,0x6C,0x69,0x64,0x69,0x6E,0x67,0x20, + 0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x6E,0x6F,0x6E,0x2D, + 0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x28,0x74,0x68,0x65,0x20, + 0x73,0x70,0x65,0x65,0x64,0x20,0x64,0x65,0x70,0x65,0x6E,0x64, + 0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65, + 0x71,0x75,0x65,0x6E,0x63,0x79,0x29,0x2E,0x00,0x19,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65, + 0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x33,0x20,0x2B,0x20, + 0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73,0x20, + 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x69,0x73,0x20,0x75, + 0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65,0x72, + 0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65, + 0x2C,0x20,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73, + 0x6C,0x69,0x64,0x65,0x20,0x74,0x6F,0x20,0x69,0x74,0x73,0x43, + 0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x2E,0x20,0x49, + 0x66,0x20,0x67,0x6C,0x69,0x73,0x73,0x61,0x6E,0x64,0x6F,0x20, + 0x28,0x45,0x33,0x29,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64, + 0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75,0x65, + 0x6E,0x63,0x79,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20, + 0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x18,0x74,0x6F,0x20,0x74, + 0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,0x68, + 0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E,0x00,0x11,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72, + 0x61,0x74,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20, + 0x34,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20,0x44, + 0x65,0x70,0x74,0x68,0x00,0x3E,0x41,0x64,0x64,0x73,0x20,0x76, + 0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68, + 0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x77,0x69, + 0x74,0x68,0x20,0x61,0x20,0x72,0x61,0x74,0x65,0x20,0x61,0x6E, + 0x64,0x20,0x73,0x70,0x65,0x65,0x64,0x2E,0x20,0x53,0x65,0x74, + 0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x3C,0x63,0x6F,0x6E, + 0x74,0x72,0x6F,0x6C,0x20,0x28,0x45,0x34,0x29,0x20,0x63,0x61, + 0x6E,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x65,0x20, + 0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x61,0x76,0x65, + 0x66,0x6F,0x72,0x6D,0x20,0x28,0x73,0x65,0x65,0x07,0x62,0x65, + 0x6C,0x6F,0x77,0x29,0x2E,0x00,0x28,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x54,0x6F,0x6E,0x65,0x20,0x70,0x6F, + 0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x2B,0x20,0x76, + 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x35,0x20,0x2B,0x20, + 0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69,0x73,0x20, + 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C, + 0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x20,0x62,0x6F,0x74, + 0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61, + 0x6D,0x65,0x6E,0x74,0x6F,0x20,0x61,0x6E,0x64,0x20,0x76,0x6F, + 0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x27, + 0x54,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x69,0x73, + 0x20,0x75,0x73,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68, + 0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69, + 0x64,0x65,0x2E,0x00,0x20,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x2B, 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64, 0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x35,0x20, - 0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x40,0x54,0x68,0x69, + 0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x36,0x20, + 0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54,0x68,0x69, 0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69, 0x6C,0x6C,0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x20,0x62, - 0x6F,0x74,0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72, - 0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x61,0x6E,0x64,0x20, - 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65, - 0x2E,0x27,0x54,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20, - 0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20,0x66,0x6F,0x72,0x20, - 0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73, - 0x6C,0x69,0x64,0x65,0x2E,0x00,0x20,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F, - 0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C, - 0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, - 0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20, - 0x36,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54, - 0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20, - 0x77,0x69,0x6C,0x6C,0x20,0x65,0x78,0x65,0x63,0x75,0x74,0x65, - 0x20,0x62,0x6F,0x74,0x68,0x20,0x76,0x69,0x62,0x72,0x61,0x74, - 0x6F,0x20,0x61,0x6E,0x64,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, - 0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x20,0x54,0x68,0x65,0x23, - 0x73,0x70,0x65,0x65,0x64,0x20,0x69,0x73,0x20,0x75,0x73,0x65, - 0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x00, - 0x11,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54, - 0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36, - 0x30,0x40,0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61, - 0x78,0x3A,0x20,0x37,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20, - 0x2B,0x20,0x44,0x65,0x70,0x74,0x68,0x00,0x41,0x54,0x72,0x65, - 0x6D,0x6F,0x6C,0x6F,0x20,0x61,0x64,0x64,0x73,0x20,0x76,0x69, - 0x62,0x72,0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68,0x65, - 0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C, - 0x75,0x6D,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x79,0x6E, - 0x74,0x61,0x78,0x20,0x69,0x73,0x20,0x65,0x78,0x61,0x63,0x74, - 0x6C,0x79,0x1B,0x61,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68, - 0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F, - 0x6D,0x6D,0x61,0x6E,0x64,0x2E,0x00,0x1E,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x70,0x61, - 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69, - 0x6F,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x38, - 0x20,0x2B,0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00, - 0x3E,0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61, - 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69, - 0x6F,0x6E,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x63, - 0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x20,0x24,0x30,0x30,0x20, - 0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x66,0x74,0x6D, - 0x6F,0x73,0x74,0x3F,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E, - 0x20,0x61,0x6E,0x64,0x20,0x24,0x46,0x46,0x20,0x74,0x68,0x65, - 0x20,0x72,0x69,0x67,0x68,0x74,0x6D,0x6F,0x73,0x74,0x2E,0x20, - 0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F, - 0x6D,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72, - 0x64,0x73,0x20,0x28,0x65,0x78,0x2E,0x30,0x47,0x55,0x53,0x29, - 0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x61, - 0x73,0x20,0x6D,0x61,0x6E,0x79,0x20,0x61,0x73,0x20,0x32,0x35, - 0x36,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F, - 0x73,0x69,0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00,0x17,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70, - 0x6C,0x65,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79, - 0x6E,0x74,0x61,0x78,0x3A,0x20,0x39,0x20,0x2B,0x20,0x4F,0x66, - 0x66,0x73,0x65,0x74,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63, - 0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C, - 0x64,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F, - 0x67,0x65,0x74,0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20, - 0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E,0x20,0x54,0x68,0x65,0x20, - 0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x2D, - 0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x66,0x72, - 0x6F,0x6D,0x20,0x28,0x4F,0x66,0x66,0x73,0x65,0x74,0x2A,0x24, - 0x31,0x30,0x30,0x29,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64, - 0x20,0x6F,0x66,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x16,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C, - 0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79, - 0x6E,0x74,0x61,0x78,0x3A,0x20,0x41,0x20,0x2B,0x20,0x55,0x70, - 0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77, - 0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x3D,0x53,0x6C,0x69, - 0x64,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72, - 0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x75, - 0x70,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x45, - 0x69,0x74,0x68,0x65,0x72,0x20,0x75,0x70,0x20,0x73,0x70,0x65, - 0x65,0x64,0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x15,0x73, - 0x70,0x65,0x65,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20, - 0x62,0x65,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x17,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x73,0x69, - 0x74,0x69,0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14,0x53,0x79, - 0x6E,0x74,0x61,0x78,0x3A,0x20,0x42,0x20,0x2B,0x20,0x50,0x6F, - 0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x41,0x54,0x68,0x69,0x73, - 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C, - 0x6C,0x20,0x6A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x68, - 0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x73, - 0x6F,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E, - 0x20,0x61,0x6E,0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x74,0x68, - 0x65,0x1B,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x66,0x72, - 0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E, - 0x6E,0x69,0x6E,0x67,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C, - 0x75,0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, - 0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20, - 0x43,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x3E, - 0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72, - 0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E, - 0x20,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20, - 0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62, - 0x65,0x20,0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68, - 0x61,0x6E,0x04,0x24,0x34,0x30,0x2E,0x00,0x17,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x20,0x62,0x72,0x65,0x61,0x6B,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1C,0x53,0x79,0x6E, - 0x74,0x61,0x78,0x3A,0x20,0x44,0x20,0x2B,0x20,0x50,0x61,0x74, - 0x74,0x65,0x72,0x6E,0x2D,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F, - 0x6E,0x00,0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D, - 0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6A,0x75,0x6D, - 0x70,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x78, - 0x74,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x61,0x6E, - 0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20, - 0x74,0x68,0x65,0x13,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65, - 0x64,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x00, - 0x22,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53, - 0x65,0x74,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20,0x28,0x41, - 0x6D,0x69,0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x13, - 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x30,0x20,0x2B, - 0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x38,0x55,0x73,0x65, - 0x20,0x45,0x30,0x30,0x20,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75, - 0x72,0x20,0x74,0x75,0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20, - 0x73,0x6F,0x75,0x6E,0x64,0x20,0x72,0x65,0x61,0x6C,0x6C,0x79, - 0x20,0x62,0x61,0x64,0x20,0x6F,0x6E,0x20,0x61,0x6E,0x20,0x41, - 0x6D,0x69,0x67,0x61,0x21,0x00,0x21,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F, - 0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F, - 0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x45,0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B, - 0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3F,0x54,0x68,0x69,0x73, - 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72, - 0x6B,0x73,0x20,0x61,0x73,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D, - 0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E, - 0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6F,0x6E,0x6C, - 0x79,0x20,0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x75,0x70,0x05, - 0x6F,0x6E,0x63,0x65,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x69, - 0x73,0x73,0x61,0x6E,0x64,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72, - 0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x13,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45, - 0x33,0x20,0x2B,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x41, - 0x49,0x66,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x69,0x73, - 0x20,0x3D,0x31,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65, - 0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x77,0x68,0x65,0x6E,0x20, - 0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70, - 0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x77,0x69, - 0x6C,0x6C,0x20,0x62,0x65,0x20,0x72,0x6F,0x75,0x6E,0x64,0x65, - 0x64,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x61, - 0x72,0x65,0x73,0x74,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E, - 0x65,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, - 0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74, - 0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79, - 0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x34,0x20,0x2B,0x20,0x54, - 0x79,0x70,0x65,0x00,0x2C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F, - 0x6D,0x6D,0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F, - 0x6C,0x73,0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61, - 0x74,0x6F,0x20,0x77,0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D, - 0x2E,0x00,0x33,0x54,0x79,0x70,0x65,0x3A,0x20,0x30,0x20,0x3D, - 0x20,0x53,0x69,0x6E,0x65,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x31,0x20,0x3D,0x20,0x52,0x61,0x6D,0x70,0x20,0x64,0x6F,0x77, - 0x6E,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x32,0x20,0x3D,0x20, - 0x53,0x71,0x75,0x61,0x72,0x65,0x00,0x44,0x49,0x66,0x20,0x79, - 0x6F,0x75,0x20,0x61,0x64,0x64,0x20,0x34,0x20,0x74,0x6F,0x20, - 0x74,0x68,0x65,0x20,0x74,0x79,0x70,0x65,0x2C,0x20,0x74,0x68, - 0x65,0x20,0x77,0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D,0x20, - 0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20, - 0x72,0x65,0x74,0x72,0x69,0x67,0x67,0x65,0x64,0x20,0x77,0x68, - 0x65,0x6E,0x20,0x61,0x19,0x6E,0x65,0x77,0x20,0x69,0x6E,0x73, - 0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x73,0x20,0x70, - 0x6C,0x61,0x79,0x65,0x64,0x2E,0x00,0x17,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x66,0x69, - 0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74, - 0x61,0x78,0x3A,0x20,0x45,0x35,0x20,0x2B,0x20,0x54,0x75,0x6E, - 0x65,0x00,0x3F,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D, - 0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62, - 0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74, - 0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E, - 0x6F,0x74,0x65,0x2E,0x20,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x63,0x61,0x75,0x73,0x65,0x44,0x61,0x6E,0x6F,0x74,0x68, - 0x65,0x72,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65, - 0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x74,0x6F,0x20,0x62,0x65, - 0x20,0x75,0x73,0x65,0x64,0x2E,0x20,0x49,0x74,0x20,0x73,0x65, - 0x65,0x6D,0x73,0x20,0x71,0x75,0x69,0x74,0x65,0x20,0x75,0x6E, - 0x75,0x73,0x61,0x62,0x6C,0x65,0x20,0x74,0x6F,0x20,0x6D,0x65, - 0x2E,0x2E,0x2E,0x00,0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43, - 0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C, - 0x6F,0x6F,0x70,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, - 0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20, - 0x45,0x36,0x20,0x2B,0x20,0x43,0x6F,0x75,0x6E,0x74,0x00,0x45, - 0x49,0x66,0x20,0x63,0x6F,0x75,0x6E,0x74,0x20,0x69,0x73,0x20, - 0x7A,0x65,0x72,0x6F,0x2C,0x20,0x74,0x68,0x65,0x20,0x62,0x65, - 0x67,0x69,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x6F,0x66,0x20,0x74, - 0x68,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x62,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65, - 0x64,0x2E,0x20,0x57,0x68,0x65,0x6E,0x20,0x61,0x40,0x6E,0x6F, - 0x6E,0x2D,0x7A,0x65,0x72,0x6F,0x20,0x76,0x61,0x6C,0x75,0x65, - 0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x2C,0x20,0x74,0x68, - 0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69, - 0x6C,0x6C,0x20,0x62,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x65,0x64, - 0x20,0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x6C,0x6F, - 0x6F,0x70,0x06,0x73,0x74,0x61,0x72,0x74,0x2E,0x00,0x1D,0x40, + 0x6F,0x74,0x68,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20, + 0x61,0x6E,0x64,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73, + 0x6C,0x69,0x64,0x65,0x2E,0x20,0x54,0x68,0x65,0x23,0x73,0x70, + 0x65,0x65,0x64,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20, + 0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75, + 0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2E,0x00,0x11,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x72,0x65, + 0x6D,0x6F,0x6C,0x6F,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, + 0x43,0x30,0x30,0x32,0x18,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, + 0x20,0x37,0x20,0x2B,0x20,0x52,0x61,0x74,0x65,0x20,0x2B,0x20, + 0x44,0x65,0x70,0x74,0x68,0x00,0x41,0x54,0x72,0x65,0x6D,0x6F, + 0x6C,0x6F,0x20,0x61,0x64,0x64,0x73,0x20,0x76,0x69,0x62,0x72, + 0x61,0x74,0x6F,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x63, + 0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D, + 0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x61, + 0x78,0x20,0x69,0x73,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79, + 0x1B,0x61,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20, + 0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F,0x6D,0x6D, + 0x61,0x6E,0x64,0x2E,0x00,0x1E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x70,0x61,0x6E,0x6E, + 0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x38,0x20,0x2B, + 0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x3E,0x53, + 0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E, + 0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E, + 0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20,0x63,0x68,0x61, + 0x6E,0x6E,0x65,0x6C,0x2E,0x20,0x24,0x30,0x30,0x20,0x69,0x73, + 0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x66,0x74,0x6D,0x6F,0x73, + 0x74,0x3F,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x61, + 0x6E,0x64,0x20,0x24,0x46,0x46,0x20,0x74,0x68,0x65,0x20,0x72, + 0x69,0x67,0x68,0x74,0x6D,0x6F,0x73,0x74,0x2E,0x20,0x4E,0x6F, + 0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F,0x6D,0x65, + 0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72,0x64,0x73, + 0x20,0x28,0x65,0x78,0x2E,0x30,0x47,0x55,0x53,0x29,0x20,0x63, + 0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x61,0x73,0x20, + 0x6D,0x61,0x6E,0x79,0x20,0x61,0x73,0x20,0x32,0x35,0x36,0x20, + 0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69, + 0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00,0x17,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65, + 0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74, + 0x61,0x78,0x3A,0x20,0x39,0x20,0x2B,0x20,0x4F,0x66,0x66,0x73, + 0x65,0x74,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D, + 0x6D,0x61,0x6E,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20, + 0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x6F,0x67,0x65, + 0x74,0x68,0x65,0x72,0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20, + 0x6E,0x6F,0x74,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61, + 0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x2D,0x62,0x65, + 0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x66,0x72,0x6F,0x6D, + 0x20,0x28,0x4F,0x66,0x66,0x73,0x65,0x74,0x2A,0x24,0x31,0x30, + 0x30,0x29,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x20,0x6F, + 0x66,0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x16,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D, + 0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79,0x6E,0x74, + 0x61,0x78,0x3A,0x20,0x41,0x20,0x2B,0x20,0x55,0x70,0x20,0x73, + 0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77,0x6E,0x20, + 0x73,0x70,0x65,0x65,0x64,0x00,0x3D,0x53,0x6C,0x69,0x64,0x65, + 0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E, + 0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x75,0x70,0x20, + 0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x20,0x45,0x69,0x74, + 0x68,0x65,0x72,0x20,0x75,0x70,0x20,0x73,0x70,0x65,0x65,0x64, + 0x20,0x6F,0x72,0x20,0x64,0x6F,0x77,0x6E,0x15,0x73,0x70,0x65, + 0x65,0x64,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65, + 0x20,0x7A,0x65,0x72,0x6F,0x2E,0x00,0x17,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6F,0x73,0x69,0x74,0x69, + 0x6F,0x6E,0x20,0x6A,0x75,0x6D,0x70,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14,0x53,0x79,0x6E,0x74, + 0x61,0x78,0x3A,0x20,0x42,0x20,0x2B,0x20,0x50,0x6F,0x73,0x69, + 0x74,0x69,0x6F,0x6E,0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63, + 0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20, + 0x6A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x68,0x65,0x20, + 0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x73,0x6F,0x6E, + 0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x20,0x61, + 0x6E,0x64,0x20,0x70,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x1B, + 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x66,0x72,0x6F,0x6D, + 0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E,0x6E,0x69, + 0x6E,0x67,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D, + 0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, + 0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x43,0x20, + 0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x3E,0x53,0x65, + 0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65, + 0x6E,0x74,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E,0x20,0x54, + 0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x68, + 0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20, + 0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68,0x61,0x6E, + 0x04,0x24,0x34,0x30,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x20,0x62,0x72,0x65,0x61,0x6B,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x1C,0x53,0x79,0x6E,0x74,0x61, + 0x78,0x3A,0x20,0x44,0x20,0x2B,0x20,0x50,0x61,0x74,0x74,0x65, + 0x72,0x6E,0x2D,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00, + 0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E, + 0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6A,0x75,0x6D,0x70,0x20, + 0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x78,0x74,0x20, + 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x61,0x6E,0x64,0x20, + 0x70,0x6C,0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20,0x74,0x68, + 0x65,0x13,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20, + 0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x22,0x40, 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74, - 0x20,0x74,0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E, - 0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x45,0x37,0x20,0x2B,0x20,0x54,0x79,0x70,0x65,0x00,0x3A, - 0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64, - 0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74, - 0x6C,0x79,0x20,0x61,0x73,0x20,0x73,0x65,0x74,0x20,0x76,0x69, - 0x62,0x72,0x61,0x74,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F, - 0x6C,0x2C,0x20,0x62,0x75,0x74,0x20,0x74,0x68,0x65,0x2A,0x74, - 0x72,0x65,0x6D,0x6F,0x6C,0x6F,0x20,0x77,0x61,0x76,0x65,0x20, - 0x66,0x6F,0x72,0x6D,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65, - 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x69,0x6E,0x73, - 0x74,0x65,0x61,0x64,0x2E,0x00,0x15,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x52,0x65,0x74,0x72,0x69,0x67,0x20, - 0x6E,0x6F,0x74,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x15,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x45,0x39,0x20,0x2B,0x20,0x49,0x6E,0x74,0x65,0x72,0x76, - 0x61,0x6C,0x00,0x2D,0x52,0x65,0x74,0x72,0x69,0x67,0x73,0x20, - 0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x77,0x69,0x74, - 0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66, - 0x69,0x65,0x64,0x20,0x69,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C, - 0x2E,0x00,0x23,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, - 0x31,0x46,0x69,0x6E,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, - 0x20,0x73,0x6C,0x69,0x64,0x65,0x20,0x75,0x70,0x2F,0x64,0x6F, + 0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20,0x28,0x41,0x6D,0x69, + 0x67,0x61,0x20,0x6F,0x6E,0x6C,0x79,0x21,0x29,0x0B,0x3E,0x40, + 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x13,0x53,0x79, + 0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x30,0x20,0x2B,0x20,0x53, + 0x74,0x61,0x74,0x75,0x73,0x00,0x38,0x55,0x73,0x65,0x20,0x45, + 0x30,0x30,0x20,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75,0x72,0x20, + 0x74,0x75,0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x6F, + 0x75,0x6E,0x64,0x20,0x72,0x65,0x61,0x6C,0x6C,0x79,0x20,0x62, + 0x61,0x64,0x20,0x6F,0x6E,0x20,0x61,0x6E,0x20,0x41,0x6D,0x69, + 0x67,0x61,0x21,0x00,0x21,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x46,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74, + 0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F, 0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, 0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45, - 0x28,0x41,0x20,0x6F,0x72,0x20,0x42,0x29,0x20,0x2B,0x20,0x53, - 0x70,0x65,0x65,0x64,0x00,0x44,0x54,0x68,0x69,0x73,0x20,0x63, + 0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53, + 0x70,0x65,0x65,0x64,0x00,0x3F,0x54,0x68,0x69,0x73,0x20,0x63, 0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73, - 0x20,0x61,0x73,0x20,0x74,0x68,0x65,0x20,0x75,0x73,0x75,0x61, - 0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69, - 0x64,0x65,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x77, - 0x69,0x6C,0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x73,0x6C,0x69, - 0x64,0x65,0x05,0x6F,0x6E,0x63,0x65,0x2E,0x00,0x12,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65, - 0x20,0x63,0x75,0x74,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, + 0x20,0x61,0x73,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, + 0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C,0x20, + 0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6F,0x6E,0x6C,0x79,0x20, + 0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x75,0x70,0x05,0x6F,0x6E, + 0x63,0x65,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x69,0x73,0x73, + 0x61,0x6E,0x64,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x13,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x33,0x20, + 0x2B,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x00,0x41,0x49,0x66, + 0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x69,0x73,0x20,0x3D, + 0x31,0x2C,0x20,0x74,0x68,0x65,0x20,0x66,0x72,0x65,0x71,0x75, + 0x65,0x6E,0x63,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x75,0x73, + 0x69,0x6E,0x67,0x20,0x74,0x6F,0x6E,0x65,0x20,0x70,0x6F,0x72, + 0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x77,0x69,0x6C,0x6C, + 0x20,0x62,0x65,0x20,0x72,0x6F,0x75,0x6E,0x64,0x65,0x64,0x20, + 0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x6E,0x65,0x61,0x72,0x65, + 0x73,0x74,0x20,0x68,0x61,0x6C,0x66,0x74,0x6F,0x6E,0x65,0x2E, + 0x00,0x1D,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x53,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20, + 0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74, + 0x61,0x78,0x3A,0x20,0x45,0x34,0x20,0x2B,0x20,0x54,0x79,0x70, + 0x65,0x00,0x2B,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D, + 0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73, + 0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F, + 0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x2E,0x00,0x33, + 0x54,0x79,0x70,0x65,0x3A,0x20,0x30,0x20,0x3D,0x20,0x53,0x69, + 0x6E,0x65,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x31,0x20,0x3D, + 0x20,0x52,0x61,0x6D,0x70,0x20,0x64,0x6F,0x77,0x6E,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x32,0x20,0x3D,0x20,0x53,0x71,0x75, + 0x61,0x72,0x65,0x00,0x43,0x49,0x66,0x20,0x79,0x6F,0x75,0x20, + 0x61,0x64,0x64,0x20,0x34,0x20,0x74,0x6F,0x20,0x74,0x68,0x65, + 0x20,0x74,0x79,0x70,0x65,0x2C,0x20,0x74,0x68,0x65,0x20,0x77, + 0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,0x77,0x69,0x6C,0x6C, + 0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,0x72,0x65,0x74,0x72, + 0x69,0x67,0x67,0x65,0x64,0x20,0x77,0x68,0x65,0x6E,0x20,0x61, + 0x19,0x6E,0x65,0x77,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x20,0x69,0x73,0x20,0x70,0x6C,0x61,0x79,0x65, + 0x64,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x53,0x65,0x74,0x20,0x66,0x69,0x6E,0x65,0x2D,0x74, + 0x75,0x6E,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20, + 0x45,0x35,0x20,0x2B,0x20,0x54,0x75,0x6E,0x65,0x00,0x3F,0x54, + 0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20, + 0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x75,0x73, + 0x65,0x64,0x20,0x74,0x6F,0x67,0x65,0x74,0x68,0x65,0x72,0x20, + 0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E, + 0x20,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x63,0x61,0x75, + 0x73,0x65,0x44,0x61,0x6E,0x6F,0x74,0x68,0x65,0x72,0x20,0x66, + 0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x76,0x61,0x6C, + 0x75,0x65,0x20,0x74,0x6F,0x20,0x62,0x65,0x20,0x75,0x73,0x65, + 0x64,0x2E,0x20,0x49,0x74,0x20,0x73,0x65,0x65,0x6D,0x73,0x20, + 0x71,0x75,0x69,0x74,0x65,0x20,0x75,0x6E,0x75,0x73,0x61,0x62, + 0x6C,0x65,0x20,0x74,0x6F,0x20,0x6D,0x65,0x2E,0x2E,0x2E,0x00, + 0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50, + 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x6F,0x6F,0x70,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x36,0x20,0x2B, + 0x20,0x43,0x6F,0x75,0x6E,0x74,0x00,0x45,0x49,0x66,0x20,0x63, + 0x6F,0x75,0x6E,0x74,0x20,0x69,0x73,0x20,0x7A,0x65,0x72,0x6F, + 0x2C,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x67,0x69,0x6E,0x6E, + 0x69,0x6E,0x67,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x6C, + 0x6F,0x6F,0x70,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20, + 0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x2E,0x20,0x57, + 0x68,0x65,0x6E,0x20,0x61,0x40,0x6E,0x6F,0x6E,0x2D,0x7A,0x65, + 0x72,0x6F,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x69,0x73,0x20, + 0x75,0x73,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x70,0x61, + 0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62, + 0x65,0x20,0x6C,0x6F,0x6F,0x70,0x65,0x64,0x20,0x66,0x72,0x6F, + 0x6D,0x20,0x74,0x68,0x65,0x20,0x6C,0x6F,0x6F,0x70,0x06,0x73, + 0x74,0x61,0x72,0x74,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x74,0x72,0x65, + 0x6D,0x6F,0x6C,0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x37,0x20, + 0x2B,0x20,0x54,0x79,0x70,0x65,0x00,0x3A,0x54,0x68,0x69,0x73, + 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72, + 0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x61, + 0x73,0x20,0x73,0x65,0x74,0x20,0x76,0x69,0x62,0x72,0x61,0x74, + 0x6F,0x20,0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x2C,0x20,0x62, + 0x75,0x74,0x20,0x74,0x68,0x65,0x29,0x74,0x72,0x65,0x6D,0x6F, + 0x6C,0x6F,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20, + 0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E, + 0x67,0x65,0x64,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E, + 0x00,0x15,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x52,0x65,0x74,0x72,0x69,0x67,0x20,0x6E,0x6F,0x74,0x65,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x15, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x39,0x20,0x2B, + 0x20,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x00,0x2D,0x52, + 0x65,0x74,0x72,0x69,0x67,0x73,0x20,0x74,0x68,0x65,0x20,0x6E, + 0x6F,0x74,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65, + 0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x69, + 0x6E,0x74,0x65,0x72,0x76,0x61,0x6C,0x2E,0x00,0x23,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x6E,0x65, + 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64, + 0x65,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40, + 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x19,0x53,0x79, + 0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x28,0x41,0x20,0x6F,0x72, + 0x20,0x42,0x29,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00, + 0x44,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E, + 0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x61,0x73,0x20,0x74, + 0x68,0x65,0x20,0x75,0x73,0x75,0x61,0x6C,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2C,0x20,0x62, + 0x75,0x74,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6F, + 0x6E,0x6C,0x79,0x20,0x73,0x6C,0x69,0x64,0x65,0x05,0x6F,0x6E, + 0x63,0x65,0x2E,0x00,0x12,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20,0x63,0x75,0x74,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x43,0x20,0x2B, + 0x20,0x54,0x69,0x63,0x6B,0x00,0x43,0x43,0x75,0x74,0x73,0x20, + 0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x61,0x74,0x20, + 0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65, + 0x64,0x20,0x74,0x69,0x63,0x6B,0x2E,0x20,0x4E,0x6F,0x74,0x65, + 0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74,0x20,0x77,0x69,0x6C, + 0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x73,0x65,0x74,0x20,0x74, + 0x68,0x65,0x34,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74,0x6F, + 0x20,0x7A,0x65,0x72,0x6F,0x2C,0x20,0x61,0x6E,0x64,0x20,0x74, + 0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69, + 0x6C,0x6C,0x20,0x73,0x74,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20, + 0x70,0x6C,0x61,0x79,0x65,0x64,0x2E,0x00,0x14,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20, + 0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78, + 0x3A,0x20,0x45,0x44,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B,0x73, + 0x00,0x3E,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61, + 0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64,0x65,0x6C,0x61, + 0x79,0x20,0x74,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74, + 0x68,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20, + 0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x69, + 0x63,0x6B,0x73,0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x20, + 0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78, + 0x3A,0x20,0x45,0x45,0x20,0x2B,0x20,0x4E,0x6F,0x74,0x65,0x73, + 0x00,0x41,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61, + 0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64,0x65,0x6C,0x61, + 0x79,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72, + 0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74, + 0x65,0x64,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66, + 0x20,0x6E,0x6F,0x74,0x65,0x73,0x2E,0x00,0x13,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x73, + 0x70,0x65,0x65,0x64,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, 0x43,0x30,0x30,0x32,0x11,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x45,0x43,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B,0x00,0x43, - 0x43,0x75,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x6E,0x6F,0x74, - 0x65,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65, - 0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x74,0x69,0x63,0x6B,0x2E, - 0x20,0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69, - 0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6F,0x6E,0x6C,0x79,0x20, - 0x73,0x65,0x74,0x20,0x74,0x68,0x65,0x34,0x76,0x6F,0x6C,0x75, - 0x6D,0x65,0x20,0x74,0x6F,0x20,0x7A,0x65,0x72,0x6F,0x2C,0x20, - 0x61,0x6E,0x64,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70, - 0x6C,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x74,0x69,0x6C, - 0x6C,0x20,0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x2E, - 0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x4E,0x6F,0x74,0x65,0x20,0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53, - 0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x44,0x20,0x2B,0x20, - 0x54,0x69,0x63,0x6B,0x73,0x00,0x3E,0x54,0x68,0x69,0x73,0x20, - 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x64,0x65,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x20,0x6E, - 0x6F,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x73,0x65,0x6C,0x65, - 0x63,0x74,0x65,0x64,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20, - 0x6F,0x66,0x20,0x74,0x69,0x63,0x6B,0x73,0x2E,0x00,0x17,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74, - 0x74,0x65,0x72,0x6E,0x20,0x64,0x65,0x6C,0x61,0x79,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53, - 0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x45,0x45,0x20,0x2B,0x20, - 0x4E,0x6F,0x74,0x65,0x73,0x00,0x41,0x54,0x68,0x69,0x73,0x20, - 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x64,0x65,0x6C,0x61,0x79,0x20,0x74,0x68,0x65,0x20,0x70, - 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x65,0x20,0x73, - 0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6E,0x75,0x6D,0x62, - 0x65,0x72,0x20,0x6F,0x66,0x20,0x6E,0x6F,0x74,0x65,0x73,0x2E, - 0x00,0x13,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x53,0x65,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x11,0x53,0x79, - 0x6E,0x74,0x61,0x78,0x3A,0x20,0x46,0x20,0x2B,0x20,0x56,0x61, - 0x6C,0x75,0x65,0x00,0x42,0x54,0x68,0x69,0x73,0x20,0x63,0x6F, - 0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73, - 0x65,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64, - 0x20,0x6F,0x72,0x20,0x42,0x50,0x4D,0x20,0x76,0x61,0x6C,0x75, - 0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x6E, - 0x67,0x2E,0x20,0x49,0x66,0x20,0x76,0x61,0x6C,0x75,0x65,0x3F, - 0x69,0x73,0x20,0x6C,0x65,0x73,0x73,0x20,0x74,0x68,0x61,0x6E, - 0x20,0x24,0x32,0x30,0x2C,0x20,0x74,0x68,0x65,0x20,0x73,0x70, - 0x65,0x65,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20, - 0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x20,0x4F,0x74,0x68, - 0x65,0x72,0x77,0x69,0x73,0x65,0x2C,0x20,0x74,0x68,0x65,0x20, - 0x42,0x50,0x4D,0x16,0x76,0x61,0x6C,0x75,0x65,0x20,0x77,0x69, - 0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65, - 0x64,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, - 0x30,0x31,0x53,0x65,0x74,0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C, - 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x12,0x53,0x79,0x6E,0x74, - 0x61,0x78,0x3A,0x20,0x47,0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75, - 0x6D,0x65,0x00,0x42,0x53,0x65,0x74,0x73,0x20,0x74,0x68,0x65, + 0x20,0x46,0x20,0x2B,0x20,0x56,0x61,0x6C,0x75,0x65,0x00,0x42, + 0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64, + 0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x65,0x74,0x20,0x74,0x68, + 0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x6F,0x72,0x20,0x42, + 0x50,0x4D,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x6F,0x66,0x20, + 0x74,0x68,0x65,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x20,0x49,0x66, + 0x20,0x76,0x61,0x6C,0x75,0x65,0x3F,0x69,0x73,0x20,0x6C,0x65, + 0x73,0x73,0x20,0x74,0x68,0x61,0x6E,0x20,0x24,0x32,0x30,0x2C, + 0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x77, + 0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67, + 0x65,0x64,0x2E,0x20,0x4F,0x74,0x68,0x65,0x72,0x77,0x69,0x73, + 0x65,0x2C,0x20,0x74,0x68,0x65,0x20,0x42,0x50,0x4D,0x16,0x76, + 0x61,0x6C,0x75,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x00,0x1B,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74, 0x20,0x67,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75, - 0x6D,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75, - 0x6D,0x65,0x20,0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F, - 0x74,0x20,0x62,0x65,0x20,0x67,0x72,0x65,0x61,0x74,0x65,0x72, - 0x20,0x74,0x68,0x61,0x6E,0x20,0x24,0x34,0x30,0x2E,0x00,0x1D, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x47,0x6C, - 0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20, - 0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, - 0x40,0x43,0x30,0x30,0x32,0x21,0x53,0x79,0x6E,0x74,0x61,0x78, - 0x3A,0x20,0x48,0x20,0x2B,0x20,0x55,0x70,0x20,0x73,0x70,0x65, - 0x65,0x64,0x20,0x2B,0x20,0x44,0x6F,0x77,0x6E,0x20,0x73,0x70, - 0x65,0x65,0x64,0x00,0x3D,0x54,0x68,0x69,0x73,0x20,0x63,0x6F, - 0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20, - 0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x61,0x73,0x20,0x76, - 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65,0x2C, - 0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x73,0x6C,0x69,0x64, - 0x65,0x73,0x20,0x74,0x68,0x65,0x16,0x67,0x6C,0x6F,0x62,0x61, - 0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x73, - 0x74,0x65,0x61,0x64,0x2E,0x00,0x11,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x4B,0x65,0x79,0x20,0x6F,0x66,0x66, + 0x6D,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x12,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x47, + 0x20,0x2B,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x00,0x42,0x53, + 0x65,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x67,0x6C,0x6F,0x62, + 0x61,0x6C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2E,0x20,0x54, + 0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x68, + 0x6F,0x75,0x6C,0x64,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20, + 0x67,0x72,0x65,0x61,0x74,0x65,0x72,0x20,0x74,0x68,0x61,0x6E, + 0x20,0x24,0x34,0x30,0x2E,0x00,0x1D,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x47,0x6C,0x6F,0x62,0x61,0x6C,0x20, + 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x73,0x6C,0x69,0x64,0x65, 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x10,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x4B,0x20,0x2B, - 0x20,0x54,0x69,0x63,0x6B,0x00,0x3C,0x54,0x68,0x69,0x73,0x20, - 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x74,0x72,0x69,0x67,0x67,0x65,0x72,0x20,0x61,0x20,0x22, - 0x4B,0x65,0x79,0x20,0x6F,0x66,0x66,0x22,0x20,0x61,0x74,0x20, - 0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x63,0x69,0x66,0x69,0x65, - 0x64,0x20,0x74,0x69,0x63,0x6B,0x2E,0x00,0x1F,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x65,0x74,0x20,0x65, - 0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F,0x73,0x69, - 0x74,0x69,0x6F,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x14,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x4C,0x20,0x2B,0x20,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F, - 0x6E,0x00,0x3E,0x43,0x68,0x61,0x6E,0x67,0x65,0x73,0x20,0x74, - 0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20, - 0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x4D,0x61, - 0x67,0x6E,0x75,0x73,0x20,0x74,0x6F,0x6C,0x64,0x20,0x6D,0x65, - 0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74,0x20,0x77,0x6F,0x75, - 0x6C,0x64,0x20,0x62,0x65,0x0C,0x76,0x65,0x72,0x79,0x20,0x75, - 0x73,0x61,0x62,0x6C,0x65,0x2E,0x00,0x17,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x6E,0x6E,0x69,0x6E, - 0x67,0x20,0x73,0x6C,0x69,0x64,0x65,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x24,0x53,0x79,0x6E,0x74, - 0x61,0x78,0x3A,0x20,0x50,0x20,0x2B,0x20,0x52,0x69,0x67,0x68, - 0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20,0x4C,0x65, - 0x66,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x42,0x54,0x68, - 0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73, - 0x6C,0x69,0x64,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x61, - 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69, - 0x6F,0x6E,0x2E,0x20,0x49,0x74,0x20,0x77,0x6F,0x72,0x6B,0x73, - 0x20,0x6C,0x69,0x6B,0x65,0x20,0x74,0x68,0x65,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x3C,0x73,0x6C,0x69,0x64,0x65,0x2E,0x20, - 0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x6F, - 0x6D,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x63,0x61,0x72, - 0x64,0x73,0x20,0x6D,0x61,0x79,0x20,0x6E,0x6F,0x74,0x20,0x68, - 0x61,0x6E,0x64,0x6C,0x65,0x20,0x32,0x35,0x36,0x20,0x70,0x61, - 0x6E,0x6E,0x69,0x6E,0x67,0x0A,0x70,0x6F,0x73,0x69,0x74,0x69, - 0x6F,0x6E,0x73,0x2E,0x00,0x16,0x40,0x58,0x30,0x34,0x30,0x40, - 0x43,0x30,0x30,0x31,0x4D,0x75,0x6C,0x74,0x69,0x20,0x72,0x65, - 0x74,0x72,0x69,0x67,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x24,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A, - 0x20,0x52,0x20,0x2B,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20, - 0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x2B,0x20,0x49,0x6E,0x74, - 0x65,0x72,0x76,0x61,0x6C,0x00,0x32,0x54,0x68,0x69,0x73,0x20, - 0x69,0x73,0x20,0x61,0x6E,0x20,0x65,0x78,0x74,0x65,0x6E,0x64, - 0x65,0x64,0x20,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x20,0x6F, - 0x66,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x74,0x72,0x69,0x67, - 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2E,0x00,0x0E,0x56, - 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65, - 0x3A,0x1F,0x3E,0x40,0x58,0x31,0x30,0x30,0x30,0x20,0x3D,0x20, - 0x4E,0x6F,0x6E,0x65,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x38, - 0x20,0x3D,0x20,0x55,0x6E,0x75,0x73,0x65,0x64,0x16,0x3E,0x31, - 0x20,0x3D,0x20,0x2D,0x31,0x20,0x20,0x20,0x20,0x40,0x54,0x33, - 0x30,0x30,0x39,0x20,0x3D,0x20,0x2B,0x31,0x16,0x3E,0x32,0x20, - 0x3D,0x20,0x2D,0x32,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30, - 0x30,0x41,0x20,0x3D,0x20,0x2B,0x32,0x16,0x3E,0x33,0x20,0x3D, - 0x20,0x2D,0x34,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30, - 0x42,0x20,0x3D,0x20,0x2B,0x34,0x16,0x3E,0x34,0x20,0x3D,0x20, - 0x2D,0x38,0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x43, - 0x20,0x3D,0x20,0x2B,0x38,0x17,0x3E,0x35,0x20,0x3D,0x20,0x2D, - 0x31,0x36,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x44,0x20, - 0x3D,0x20,0x2B,0x31,0x36,0x18,0x3E,0x36,0x20,0x3D,0x20,0x2A, - 0x32,0x2F,0x33,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x45,0x20, - 0x3D,0x20,0x2A,0x33,0x2F,0x32,0x16,0x3E,0x37,0x20,0x3D,0x20, - 0x2A,0x31,0x2F,0x32,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x46, - 0x20,0x3D,0x20,0x2A,0x32,0x00,0x10,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x54,0x72,0x65,0x6D,0x6F,0x72,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E, - 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x54,0x20,0x2B,0x20, - 0x4F,0x6E,0x20,0x74,0x69,0x6D,0x65,0x20,0x2B,0x20,0x4F,0x66, - 0x66,0x20,0x74,0x69,0x6D,0x65,0x00,0x3E,0x54,0x68,0x69,0x73, - 0x20,0x77,0x65,0x69,0x72,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61, - 0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x65,0x74,0x20, - 0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74, - 0x6F,0x20,0x7A,0x65,0x72,0x6F,0x20,0x64,0x75,0x72,0x69,0x6E, - 0x67,0x20,0x6F,0x66,0x66,0x20,0x74,0x69,0x6D,0x65,0x36,0x6E, - 0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x69,0x63, - 0x6B,0x73,0x2E,0x20,0x49,0x74,0x20,0x69,0x73,0x20,0x69,0x6E, - 0x63,0x6C,0x75,0x64,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x53, - 0x54,0x4D,0x20,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x69, - 0x6C,0x69,0x74,0x79,0x2E,0x00,0x27,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x74,0x72,0x61,0x20,0x66, - 0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E, - 0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x19,0x53, - 0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x58,0x28,0x31,0x20,0x6F, - 0x72,0x20,0x32,0x29,0x20,0x2B,0x20,0x53,0x70,0x65,0x65,0x64, + 0x21,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x48,0x20,0x2B, + 0x20,0x55,0x70,0x20,0x73,0x70,0x65,0x65,0x64,0x20,0x2B,0x20, + 0x44,0x6F,0x77,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x00,0x3D, + 0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64, + 0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x65,0x78,0x61,0x63,0x74, + 0x6C,0x79,0x20,0x61,0x73,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x73,0x6C,0x69,0x64,0x65,0x2C,0x20,0x62,0x75,0x74,0x20, + 0x69,0x74,0x20,0x73,0x6C,0x69,0x64,0x65,0x73,0x20,0x74,0x68, + 0x65,0x16,0x67,0x6C,0x6F,0x62,0x61,0x6C,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E, + 0x00,0x11,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x4B,0x65,0x79,0x20,0x6F,0x66,0x66,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x10,0x53,0x79,0x6E,0x74, + 0x61,0x78,0x3A,0x20,0x4B,0x20,0x2B,0x20,0x54,0x69,0x63,0x6B, 0x00,0x3C,0x54,0x68,0x69,0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61, - 0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x61,0x73,0x20, - 0x66,0x69,0x6E,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65, - 0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C, - 0x20,0x62,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x70,0x65, - 0x65,0x64,0x18,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x64, - 0x69,0x76,0x69,0x64,0x65,0x64,0x20,0x62,0x79,0x20,0x66,0x6F, - 0x75,0x72,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A, + 0x6E,0x64,0x20,0x77,0x69,0x6C,0x6C,0x20,0x74,0x72,0x69,0x67, + 0x67,0x65,0x72,0x20,0x61,0x20,0x22,0x4B,0x65,0x79,0x20,0x6F, + 0x66,0x66,0x22,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73, + 0x70,0x65,0x63,0x69,0x66,0x69,0x65,0x64,0x20,0x74,0x69,0x63, + 0x6B,0x2E,0x00,0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x53,0x65,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F, + 0x70,0x65,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x14, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x4C,0x20,0x2B,0x20, + 0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x3E,0x43,0x68, + 0x61,0x6E,0x67,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x65,0x6E, + 0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x70,0x6F,0x73,0x69,0x74, + 0x69,0x6F,0x6E,0x2E,0x20,0x4D,0x61,0x67,0x6E,0x75,0x73,0x20, + 0x74,0x6F,0x6C,0x64,0x20,0x6D,0x65,0x20,0x74,0x68,0x61,0x74, + 0x20,0x69,0x74,0x20,0x77,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65, + 0x0C,0x76,0x65,0x72,0x79,0x20,0x75,0x73,0x61,0x62,0x6C,0x65, + 0x2E,0x00,0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, + 0x31,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x73,0x6C,0x69, + 0x64,0x65,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x24,0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x50, + 0x20,0x2B,0x20,0x52,0x69,0x67,0x68,0x74,0x20,0x73,0x70,0x65, + 0x65,0x64,0x20,0x2B,0x20,0x4C,0x65,0x66,0x74,0x20,0x73,0x70, + 0x65,0x65,0x64,0x00,0x42,0x54,0x68,0x69,0x73,0x20,0x63,0x6F, + 0x6D,0x6D,0x61,0x6E,0x64,0x20,0x73,0x6C,0x69,0x64,0x65,0x73, + 0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67, + 0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x49, + 0x74,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69,0x6B,0x65, + 0x20,0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x3C, + 0x73,0x6C,0x69,0x64,0x65,0x2E,0x20,0x4E,0x6F,0x74,0x65,0x20, + 0x74,0x68,0x61,0x74,0x20,0x73,0x6F,0x6D,0x65,0x20,0x73,0x6F, + 0x75,0x6E,0x64,0x20,0x63,0x61,0x72,0x64,0x73,0x20,0x6D,0x61, + 0x79,0x20,0x6E,0x6F,0x74,0x20,0x68,0x61,0x6E,0x64,0x6C,0x65, + 0x20,0x32,0x35,0x36,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67, + 0x0A,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x73,0x2E,0x00, + 0x16,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D, + 0x75,0x6C,0x74,0x69,0x20,0x72,0x65,0x74,0x72,0x69,0x67,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x24, + 0x53,0x79,0x6E,0x74,0x61,0x78,0x3A,0x20,0x52,0x20,0x2B,0x20, + 0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x67, + 0x65,0x20,0x2B,0x20,0x49,0x6E,0x74,0x65,0x72,0x76,0x61,0x6C, + 0x00,0x32,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x61,0x6E, + 0x20,0x65,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x76,0x65, + 0x72,0x73,0x69,0x6F,0x6E,0x20,0x6F,0x66,0x20,0x74,0x68,0x65, + 0x20,0x72,0x65,0x74,0x72,0x69,0x67,0x20,0x63,0x6F,0x6D,0x6D, + 0x61,0x6E,0x64,0x2E,0x00,0x0E,0x56,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x3A,0x1F,0x3E,0x40,0x58, + 0x31,0x30,0x30,0x30,0x20,0x3D,0x20,0x4E,0x6F,0x6E,0x65,0x20, + 0x20,0x40,0x54,0x33,0x30,0x30,0x38,0x20,0x3D,0x20,0x55,0x6E, + 0x75,0x73,0x65,0x64,0x16,0x3E,0x31,0x20,0x3D,0x20,0x2D,0x31, + 0x20,0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x39,0x20,0x3D, + 0x20,0x2B,0x31,0x16,0x3E,0x32,0x20,0x3D,0x20,0x2D,0x32,0x20, + 0x20,0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x41,0x20,0x3D,0x20, + 0x2B,0x32,0x16,0x3E,0x33,0x20,0x3D,0x20,0x2D,0x34,0x20,0x20, + 0x20,0x20,0x40,0x54,0x33,0x30,0x30,0x42,0x20,0x3D,0x20,0x2B, + 0x34,0x16,0x3E,0x34,0x20,0x3D,0x20,0x2D,0x38,0x20,0x20,0x20, + 0x20,0x40,0x54,0x33,0x30,0x30,0x43,0x20,0x3D,0x20,0x2B,0x38, + 0x17,0x3E,0x35,0x20,0x3D,0x20,0x2D,0x31,0x36,0x20,0x20,0x20, + 0x40,0x54,0x33,0x30,0x30,0x44,0x20,0x3D,0x20,0x2B,0x31,0x36, + 0x18,0x3E,0x36,0x20,0x3D,0x20,0x2A,0x32,0x2F,0x33,0x20,0x20, + 0x40,0x54,0x33,0x30,0x30,0x45,0x20,0x3D,0x20,0x2A,0x33,0x2F, + 0x32,0x16,0x3E,0x37,0x20,0x3D,0x20,0x2A,0x31,0x2F,0x32,0x20, + 0x20,0x40,0x54,0x33,0x30,0x30,0x46,0x20,0x3D,0x20,0x2A,0x32, + 0x00,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x54,0x72,0x65,0x6D,0x6F,0x72,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x53,0x79,0x6E,0x74,0x61, + 0x78,0x3A,0x20,0x54,0x20,0x2B,0x20,0x4F,0x6E,0x20,0x74,0x69, + 0x6D,0x65,0x20,0x2B,0x20,0x4F,0x66,0x66,0x20,0x74,0x69,0x6D, + 0x65,0x00,0x3E,0x54,0x68,0x69,0x73,0x20,0x77,0x65,0x69,0x72, + 0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x69, + 0x6C,0x6C,0x20,0x73,0x65,0x74,0x20,0x74,0x68,0x65,0x20,0x76, + 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x74,0x6F,0x20,0x7A,0x65,0x72, + 0x6F,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20,0x6F,0x66,0x66, + 0x20,0x74,0x69,0x6D,0x65,0x36,0x6E,0x75,0x6D,0x62,0x65,0x72, + 0x20,0x6F,0x66,0x20,0x74,0x69,0x63,0x6B,0x73,0x2E,0x20,0x49, + 0x74,0x20,0x69,0x73,0x20,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65, + 0x64,0x20,0x66,0x6F,0x72,0x20,0x53,0x54,0x4D,0x20,0x63,0x6F, + 0x6D,0x70,0x61,0x74,0x69,0x62,0x69,0x6C,0x69,0x74,0x79,0x2E, + 0x00,0x27,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x45,0x78,0x74,0x72,0x61,0x20,0x66,0x69,0x6E,0x65,0x20,0x70, + 0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70, + 0x2F,0x64,0x6F,0x77,0x6E,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x19,0x53,0x79,0x6E,0x74,0x61,0x78, + 0x3A,0x20,0x58,0x28,0x31,0x20,0x6F,0x72,0x20,0x32,0x29,0x20, + 0x2B,0x20,0x53,0x70,0x65,0x65,0x64,0x00,0x3C,0x54,0x68,0x69, + 0x73,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x77,0x6F, + 0x72,0x6B,0x73,0x20,0x61,0x73,0x20,0x66,0x69,0x6E,0x65,0x20, + 0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75, + 0x70,0x2F,0x64,0x6F,0x77,0x6E,0x2C,0x20,0x62,0x75,0x74,0x20, + 0x74,0x68,0x65,0x20,0x73,0x70,0x65,0x65,0x64,0x18,0x77,0x69, + 0x6C,0x6C,0x20,0x62,0x65,0x20,0x64,0x69,0x76,0x69,0x64,0x65, + 0x64,0x20,0x62,0x79,0x20,0x66,0x6F,0x75,0x72,0x2E,0x00,0x03, + 0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x0A,0x40,0x4C,0x4B,0x65,0x79, - 0x62,0x6F,0x61,0x72,0x64,0x00,0x0B,0x3E,0x40,0x58,0x30,0x32, - 0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x3E,0x49,0x66,0x20,0x79, - 0x6F,0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x61,0x6E,0x20,0x61, - 0x6D,0x62,0x69,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,0x63, - 0x72,0x65,0x61,0x74,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20, - 0x65,0x66,0x66,0x69,0x63,0x69,0x65,0x6E,0x74,0x6C,0x79,0x20, - 0x77,0x65,0x20,0x73,0x74,0x72,0x6F,0x6E,0x67,0x6C,0x79,0x20, - 0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x44,0x74,0x68, - 0x61,0x74,0x20,0x79,0x6F,0x75,0x20,0x6C,0x65,0x61,0x72,0x6E, - 0x20,0x41,0x4C,0x4C,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79, - 0x62,0x6F,0x61,0x72,0x64,0x20,0x66,0x75,0x6E,0x63,0x74,0x69, - 0x6F,0x6E,0x73,0x2E,0x20,0x4D,0x61,0x6E,0x79,0x20,0x6F,0x66, - 0x20,0x74,0x68,0x65,0x6D,0x20,0x61,0x72,0x65,0x20,0x74,0x68, - 0x65,0x20,0x73,0x61,0x6D,0x65,0x45,0x66,0x72,0x6F,0x6D,0x20, - 0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20, - 0x31,0x20,0x61,0x6E,0x64,0x20,0x50,0x72,0x6F,0x54,0x72,0x61, - 0x63,0x6B,0x65,0x72,0x20,0x74,0x6F,0x20,0x65,0x6E,0x73,0x75, - 0x72,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x79,0x6F,0x75,0x20, - 0x66,0x65,0x65,0x6C,0x20,0x63,0x6F,0x6D,0x66,0x6F,0x72,0x74, - 0x61,0x62,0x6C,0x65,0x2E,0x75,0x73,0x69,0x6E,0x67,0x20,0x74, - 0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20, - 0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x76,0x65,0x72, - 0x79,0x20,0x66,0x69,0x72,0x73,0x74,0x20,0x6D,0x69,0x6E,0x75, - 0x74,0x65,0x2E,0x01,0x3E,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30, - 0x40,0x43,0x30,0x30,0x31,0x26,0x3E,0x59,0x6F,0x75,0x20,0x73, - 0x68,0x6F,0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x61,0x77,0x61, - 0x72,0x65,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x66,0x61, - 0x63,0x74,0x20,0x74,0x68,0x61,0x74,0x3A,0x01,0x3E,0x48,0x3E, - 0x40,0x43,0x30,0x30,0x32,0x54,0x68,0x69,0x73,0x20,0x68,0x65, - 0x6C,0x70,0x20,0x74,0x65,0x78,0x74,0x20,0x69,0x73,0x20,0x77, - 0x72,0x69,0x74,0x74,0x65,0x6E,0x20,0x75,0x73,0x69,0x6E,0x67, - 0x20,0x61,0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x6B, - 0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x2E,0x20,0x54,0x68,0x65, - 0x72,0x65,0x66,0x6F,0x72,0x65,0x20,0x73,0x6F,0x6D,0x65,0x2F, - 0x72,0x65,0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x73,0x20,0x74, - 0x6F,0x20,0x6E,0x6F,0x6E,0x2D,0x6F,0x72,0x64,0x69,0x6E,0x61, - 0x72,0x79,0x20,0x6B,0x65,0x79,0x73,0x20,0x6D,0x69,0x67,0x68, - 0x74,0x20,0x62,0x65,0x20,0x77,0x72,0x6F,0x6E,0x67,0x2E,0x0F, - 0x53,0x68,0x20,0x3D,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x6B, - 0x65,0x79,0x2E,0x01,0x3E,0x10,0x40,0x58,0x30,0x34,0x30,0x40, - 0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25,0x41, - 0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x40,0x54,0x31, - 0x36,0x30,0x54,0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C, - 0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65, - 0x01,0x3E,0x2C,0x3E,0x28,0x4F,0x72,0x20,0x22,0x4C,0x65,0x66, - 0x74,0x20,0x43,0x74,0x72,0x6C,0x20,0x2B,0x20,0x4C,0x65,0x66, - 0x74,0x20,0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x2B,0x20, - 0x46,0x22,0x20,0x6F,0x6E,0x20,0x4D,0x61,0x63,0x73,0x29,0x00, - 0x17,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43, - 0x75,0x72,0x73,0x6F,0x72,0x20,0x6D,0x6F,0x76,0x65,0x73,0x3A, - 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x1D,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31, - 0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x69,0x6E,0x20,0x70,0x61, - 0x74,0x74,0x65,0x72,0x6E,0x2E,0x32,0x3E,0x43,0x74,0x72,0x6C, - 0x2B,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31, - 0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2D,0x70,0x6C, - 0x61,0x79,0x20,0x66,0x72,0x6F,0x6D,0x20,0x46,0x39,0x2E,0x2E, - 0x46,0x31,0x32,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x2F,0x3E,0x53, - 0x68,0x2B,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54, - 0x31,0x36,0x30,0x53,0x74,0x6F,0x72,0x65,0x20,0x63,0x75,0x72, - 0x72,0x65,0x6E,0x74,0x20,0x6C,0x69,0x6E,0x65,0x20,0x69,0x6E, - 0x20,0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x2E,0x24,0x3E,0x50, - 0x61,0x67,0x65,0x55,0x70,0x20,0x20,0x40,0x54,0x31,0x36,0x30, - 0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65, - 0x73,0x20,0x75,0x70,0x77,0x61,0x72,0x64,0x73,0x2E,0x27,0x3E, - 0x50,0x61,0x67,0x65,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31, - 0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69, - 0x6E,0x65,0x73,0x20,0x64,0x6F,0x77,0x6E,0x77,0x61,0x72,0x64, - 0x73,0x2E,0x1B,0x3E,0x48,0x6F,0x6D,0x65,0x20,0x20,0x40,0x54, - 0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6C, - 0x69,0x6E,0x65,0x20,0x30,0x2E,0x1D,0x3E,0x45,0x6E,0x64,0x20, - 0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74, - 0x6F,0x20,0x6C,0x61,0x73,0x74,0x20,0x6C,0x69,0x6E,0x65,0x2E, - 0x1E,0x3E,0x54,0x61,0x62,0x20,0x20,0x40,0x54,0x31,0x36,0x30, - 0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6E,0x65,0x78,0x74, - 0x20,0x74,0x72,0x61,0x63,0x6B,0x2E,0x33,0x3E,0x41,0x6C,0x74, - 0x2B,0x51,0x2E,0x2E,0x49,0x20,0x40,0x54,0x31,0x36,0x30,0x4A, - 0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61,0x63,0x6B, - 0x20,0x28,0x30,0x2E,0x2E,0x37,0x29,0x20,0x4D,0x4F,0x44,0x20, - 0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x2E,0x34, - 0x3E,0x41,0x6C,0x74,0x2B,0x41,0x2E,0x2E,0x4B,0x20,0x40,0x54, - 0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74, - 0x72,0x61,0x63,0x6B,0x20,0x28,0x38,0x2E,0x2E,0x31,0x35,0x29, - 0x20,0x4D,0x4F,0x44,0x20,0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E, - 0x65,0x6C,0x73,0x2E,0x00,0x19,0x40,0x58,0x30,0x34,0x30,0x40, - 0x43,0x30,0x30,0x31,0x43,0x75,0x74,0x2F,0x43,0x6F,0x70,0x79, - 0x2F,0x50,0x61,0x73,0x74,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x34,0x44,0x65,0x6C,0x65, - 0x74,0x65,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C, - 0x65,0x74,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72,0x20, - 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C,0x75,0x6D, - 0x6E,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E, - 0x39,0x3E,0x53,0x68,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20, - 0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20, - 0x6E,0x6F,0x74,0x65,0x2C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, - 0x20,0x61,0x6E,0x64,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20, - 0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x35,0x3E, - 0x43,0x74,0x72,0x6C,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20, - 0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20, - 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x6E,0x64,0x20,0x65, - 0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74,0x20,0x63,0x75,0x72, - 0x73,0x6F,0x72,0x2E,0x29,0x3E,0x41,0x6C,0x74,0x2B,0x44,0x65, - 0x6C,0x65,0x74,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65, - 0x6C,0x65,0x74,0x65,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20, - 0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x24,0x3E, - 0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54,0x31,0x36, - 0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x6E,0x6F,0x74,0x65, - 0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x27, - 0x3E,0x53,0x68,0x2B,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20, - 0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20, - 0x6C,0x69,0x6E,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73, - 0x6F,0x72,0x2E,0x25,0x3E,0x42,0x61,0x63,0x6B,0x73,0x70,0x61, - 0x63,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65, - 0x74,0x65,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20, - 0x6E,0x6F,0x74,0x65,0x2E,0x28,0x3E,0x53,0x68,0x2B,0x42,0x61, - 0x63,0x6B,0x73,0x70,0x61,0x63,0x65,0x20,0x40,0x54,0x31,0x36, - 0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x70,0x72,0x65,0x76, - 0x69,0x6F,0x75,0x73,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1C,0x3E, - 0x41,0x6C,0x74,0x2B,0x43,0x75,0x72,0x73,0x6F,0x72,0x20,0x40, - 0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x62,0x6C,0x6F, - 0x63,0x6B,0x2E,0x16,0x3E,0x53,0x68,0x2B,0x46,0x33,0x20,0x40, - 0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x74,0x72,0x61,0x63, - 0x6B,0x2E,0x17,0x3E,0x53,0x68,0x2B,0x46,0x34,0x20,0x40,0x54, - 0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x74,0x72,0x61,0x63, - 0x6B,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x46,0x35,0x20,0x40,0x54, - 0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x74,0x72,0x61, - 0x63,0x6B,0x2E,0x1A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x33, - 0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x70,0x61, - 0x74,0x74,0x65,0x72,0x6E,0x2E,0x1B,0x3E,0x43,0x74,0x72,0x6C, - 0x2B,0x46,0x34,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70, - 0x79,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1C,0x3E, - 0x43,0x74,0x72,0x6C,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36, - 0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x70,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x33,0x20, - 0x40,0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x62,0x6C,0x6F, - 0x63,0x6B,0x2E,0x18,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x34,0x20, - 0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x62,0x6C, - 0x6F,0x63,0x6B,0x2E,0x19,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x35, - 0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20, - 0x62,0x6C,0x6F,0x63,0x6B,0x2E,0x20,0x3E,0x41,0x6C,0x74,0x2B, - 0x43,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B, - 0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x74,0x72,0x61, - 0x63,0x6B,0x2E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43, - 0x30,0x30,0x31,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E, - 0x65,0x6F,0x75,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, - 0x40,0x43,0x30,0x30,0x32,0x1C,0x52,0x69,0x67,0x68,0x74,0x20, - 0x63,0x74,0x72,0x6C,0x2E,0x20,0x20,0x40,0x54,0x31,0x36,0x30, - 0x50,0x6C,0x61,0x79,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x1D,0x3E, - 0x41,0x6C,0x74,0x20,0x47,0x72,0x20,0x20,0x20,0x20,0x40,0x54, - 0x31,0x36,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74, - 0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20, - 0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30, - 0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x2E,0x19,0x3E,0x53,0x70,0x61,0x63,0x65,0x20,0x20, - 0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x74,0x6F,0x70,0x2F, - 0x45,0x64,0x69,0x74,0x2E,0x1B,0x3E,0x46,0x31,0x2E,0x2E,0x46, - 0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63, - 0x74,0x20,0x6F,0x63,0x74,0x61,0x76,0x65,0x2E,0x27,0x3E,0x4B, - 0x65,0x79,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x45,0x73,0x63, + 0x2A,0x0D,0x40,0x4C,0x4B,0x65,0x79,0x62,0x69,0x6E,0x64,0x69, + 0x6E,0x67,0x73,0x00,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30,0x40, + 0x43,0x30,0x30,0x32,0x4A,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75, + 0x20,0x68,0x61,0x76,0x65,0x20,0x61,0x6E,0x20,0x61,0x6D,0x62, + 0x69,0x74,0x69,0x6F,0x6E,0x20,0x74,0x6F,0x20,0x63,0x72,0x65, + 0x61,0x74,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x65,0x66, + 0x66,0x69,0x63,0x69,0x65,0x6E,0x74,0x6C,0x79,0x20,0x77,0x65, + 0x20,0x73,0x74,0x72,0x6F,0x6E,0x67,0x6C,0x79,0x20,0x72,0x65, + 0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x44,0x74,0x68,0x61,0x74, + 0x20,0x79,0x6F,0x75,0x20,0x6C,0x65,0x61,0x72,0x6E,0x20,0x41, + 0x4C,0x4C,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F, + 0x61,0x72,0x64,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E, + 0x73,0x2E,0x20,0x4D,0x61,0x6E,0x79,0x20,0x6F,0x66,0x20,0x74, + 0x68,0x65,0x6D,0x20,0x61,0x72,0x65,0x20,0x74,0x68,0x65,0x20, + 0x73,0x61,0x6D,0x65,0x45,0x66,0x72,0x6F,0x6D,0x20,0x46,0x61, + 0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x31,0x20, + 0x61,0x6E,0x64,0x20,0x50,0x72,0x6F,0x54,0x72,0x61,0x63,0x6B, + 0x65,0x72,0x20,0x74,0x6F,0x20,0x65,0x6E,0x73,0x75,0x72,0x65, + 0x20,0x74,0x68,0x61,0x74,0x20,0x79,0x6F,0x75,0x20,0x66,0x65, + 0x65,0x6C,0x20,0x63,0x6F,0x6D,0x66,0x6F,0x72,0x74,0x61,0x62, + 0x6C,0x65,0x2E,0x75,0x73,0x69,0x6E,0x67,0x20,0x74,0x68,0x69, + 0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x66,0x72, + 0x6F,0x6D,0x20,0x74,0x68,0x65,0x20,0x76,0x65,0x72,0x79,0x20, + 0x66,0x69,0x72,0x73,0x74,0x20,0x6D,0x69,0x6E,0x75,0x74,0x65, + 0x2E,0x01,0x3E,0x0B,0x3E,0x40,0x58,0x30,0x32,0x30,0x40,0x43, + 0x30,0x30,0x31,0x26,0x3E,0x59,0x6F,0x75,0x20,0x73,0x68,0x6F, + 0x75,0x6C,0x64,0x20,0x62,0x65,0x20,0x61,0x77,0x61,0x72,0x65, + 0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x66,0x61,0x63,0x74, + 0x20,0x74,0x68,0x61,0x74,0x3A,0x01,0x3E,0x48,0x3E,0x40,0x43, + 0x30,0x30,0x32,0x54,0x68,0x69,0x73,0x20,0x68,0x65,0x6C,0x70, + 0x20,0x74,0x65,0x78,0x74,0x20,0x69,0x73,0x20,0x77,0x72,0x69, + 0x74,0x74,0x65,0x6E,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61, + 0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x6B,0x65,0x79, + 0x62,0x6F,0x61,0x72,0x64,0x2E,0x20,0x54,0x68,0x65,0x72,0x65, + 0x66,0x6F,0x72,0x65,0x20,0x73,0x6F,0x6D,0x65,0x2F,0x72,0x65, + 0x66,0x65,0x72,0x65,0x6E,0x63,0x65,0x73,0x20,0x74,0x6F,0x20, + 0x6E,0x6F,0x6E,0x2D,0x6F,0x72,0x64,0x69,0x6E,0x61,0x72,0x79, + 0x20,0x6B,0x65,0x79,0x73,0x20,0x6D,0x69,0x67,0x68,0x74,0x20, + 0x62,0x65,0x20,0x77,0x72,0x6F,0x6E,0x67,0x2E,0x0F,0x53,0x68, + 0x20,0x3D,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x6B,0x65,0x79, + 0x2E,0x01,0x3E,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x41,0x75,0x64,0x69,0x6F,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x32,0x3E,0x43,0x74, + 0x72,0x6C,0x20,0x26,0x20,0x6E,0x75,0x6D,0x70,0x61,0x64,0x2B, 0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61, - 0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64, - 0x2E,0x22,0x3E,0x53,0x68,0x2B,0x28,0x31,0x2F,0x32,0x29,0x20, - 0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63,0x72,0x65,0x61,0x73, - 0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E, - 0x29,0x3E,0x43,0x61,0x70,0x73,0x4C,0x6F,0x63,0x6B,0x20,0x6F, - 0x72,0x20,0x3C,0x3E,0x20,0x40,0x54,0x31,0x36,0x30,0x45,0x6E, - 0x74,0x65,0x72,0x20,0x4B,0x65,0x79,0x6F,0x66,0x66,0x2D,0x22, - 0x6E,0x6F,0x74,0x65,0x22,0x2E,0x25,0x3E,0x53,0x68,0x2B,0x4C, - 0x65,0x66,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63, - 0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20,0x70, - 0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x26,0x3E,0x53,0x68, - 0x2B,0x52,0x69,0x67,0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30, - 0x44,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E, - 0x67,0x20,0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x28, - 0x3E,0x43,0x74,0x72,0x6C,0x2B,0x4C,0x65,0x66,0x74,0x20,0x40, + 0x73,0x65,0x20,0x6D,0x61,0x73,0x74,0x65,0x72,0x20,0x76,0x6F, + 0x6C,0x75,0x6D,0x65,0x20,0x62,0x79,0x20,0x31,0x36,0x2E,0x32, + 0x3E,0x43,0x74,0x72,0x6C,0x20,0x26,0x20,0x6E,0x75,0x6D,0x70, + 0x61,0x64,0x2D,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63, + 0x72,0x65,0x61,0x73,0x65,0x20,0x6D,0x61,0x73,0x74,0x65,0x72, + 0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x62,0x79,0x20,0x31, + 0x36,0x2E,0x00,0x10,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25,0x41,0x6C,0x74, + 0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x40,0x54,0x31,0x36,0x30, + 0x54,0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73, + 0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x01,0x3E, + 0x2C,0x3E,0x28,0x4F,0x72,0x20,0x22,0x4C,0x65,0x66,0x74,0x20, + 0x43,0x74,0x72,0x6C,0x20,0x2B,0x20,0x4C,0x65,0x66,0x74,0x20, + 0x43,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x2B,0x20,0x46,0x22, + 0x20,0x6F,0x6E,0x20,0x4D,0x61,0x63,0x73,0x29,0x00,0x17,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x75,0x72, + 0x73,0x6F,0x72,0x20,0x6D,0x6F,0x76,0x65,0x73,0x3A,0x0B,0x3E, + 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1D,0x46, + 0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36,0x30, + 0x4A,0x75,0x6D,0x70,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x2E,0x32,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46, + 0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36,0x30, + 0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2D,0x70,0x6C,0x61,0x79, + 0x20,0x66,0x72,0x6F,0x6D,0x20,0x46,0x39,0x2E,0x2E,0x46,0x31, + 0x32,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x2F,0x3E,0x53,0x68,0x2B, + 0x46,0x39,0x2E,0x2E,0x46,0x31,0x32,0x20,0x40,0x54,0x31,0x36, + 0x30,0x53,0x74,0x6F,0x72,0x65,0x20,0x63,0x75,0x72,0x72,0x65, + 0x6E,0x74,0x20,0x6C,0x69,0x6E,0x65,0x20,0x69,0x6E,0x20,0x46, + 0x39,0x2E,0x2E,0x46,0x31,0x32,0x2E,0x24,0x3E,0x50,0x61,0x67, + 0x65,0x55,0x70,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75, + 0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65,0x73,0x20, + 0x75,0x70,0x77,0x61,0x72,0x64,0x73,0x2E,0x27,0x3E,0x50,0x61, + 0x67,0x65,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30, + 0x4A,0x75,0x6D,0x70,0x20,0x31,0x36,0x2D,0x6C,0x69,0x6E,0x65, + 0x73,0x20,0x64,0x6F,0x77,0x6E,0x77,0x61,0x72,0x64,0x73,0x2E, + 0x1B,0x3E,0x48,0x6F,0x6D,0x65,0x20,0x20,0x40,0x54,0x31,0x36, + 0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x6C,0x69,0x6E, + 0x65,0x20,0x30,0x2E,0x1D,0x3E,0x45,0x6E,0x64,0x20,0x20,0x40, + 0x54,0x31,0x36,0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20, + 0x6C,0x61,0x73,0x74,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1E,0x3E, + 0x54,0x61,0x62,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75, + 0x6D,0x70,0x20,0x74,0x6F,0x20,0x6E,0x65,0x78,0x74,0x20,0x74, + 0x72,0x61,0x63,0x6B,0x2E,0x33,0x3E,0x41,0x6C,0x74,0x2B,0x51, + 0x2E,0x2E,0x49,0x20,0x40,0x54,0x31,0x36,0x30,0x4A,0x75,0x6D, + 0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x28, + 0x30,0x2E,0x2E,0x37,0x29,0x20,0x4D,0x4F,0x44,0x20,0x4E,0x2D, + 0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x2E,0x34,0x3E,0x41, + 0x6C,0x74,0x2B,0x41,0x2E,0x2E,0x4B,0x20,0x40,0x54,0x31,0x36, + 0x30,0x4A,0x75,0x6D,0x70,0x20,0x74,0x6F,0x20,0x74,0x72,0x61, + 0x63,0x6B,0x20,0x28,0x38,0x2E,0x2E,0x31,0x35,0x29,0x20,0x4D, + 0x4F,0x44,0x20,0x4E,0x2D,0x43,0x68,0x61,0x6E,0x6E,0x65,0x6C, + 0x73,0x2E,0x00,0x19,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, + 0x30,0x31,0x43,0x75,0x74,0x2F,0x43,0x6F,0x70,0x79,0x2F,0x50, + 0x61,0x73,0x74,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x34,0x44,0x65,0x6C,0x65,0x74,0x65, + 0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74, + 0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72,0x20,0x76,0x6F, + 0x6C,0x75,0x6D,0x65,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20, + 0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x39,0x3E, + 0x53,0x68,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54, + 0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x6E,0x6F, + 0x74,0x65,0x2C,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61, + 0x6E,0x64,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74, + 0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x35,0x3E,0x43,0x74, + 0x72,0x6C,0x2B,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54, + 0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x76,0x6F, + 0x6C,0x75,0x6D,0x65,0x20,0x61,0x6E,0x64,0x20,0x65,0x66,0x66, + 0x65,0x63,0x74,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F, + 0x72,0x2E,0x29,0x3E,0x41,0x6C,0x74,0x2B,0x44,0x65,0x6C,0x65, + 0x74,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65, + 0x74,0x65,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x20,0x61,0x74, + 0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x24,0x3E,0x49,0x6E, + 0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x49, + 0x6E,0x73,0x65,0x72,0x74,0x20,0x6E,0x6F,0x74,0x65,0x20,0x61, + 0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x27,0x3E,0x53, + 0x68,0x2B,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x20,0x40,0x54, + 0x31,0x36,0x30,0x49,0x6E,0x73,0x65,0x72,0x74,0x20,0x6C,0x69, + 0x6E,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72,0x73,0x6F,0x72, + 0x2E,0x25,0x3E,0x42,0x61,0x63,0x6B,0x73,0x70,0x61,0x63,0x65, + 0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x6C,0x65,0x74,0x65, + 0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x6E,0x6F, + 0x74,0x65,0x2E,0x28,0x3E,0x53,0x68,0x2B,0x42,0x61,0x63,0x6B, + 0x73,0x70,0x61,0x63,0x65,0x20,0x40,0x54,0x31,0x36,0x30,0x44, + 0x65,0x6C,0x65,0x74,0x65,0x20,0x70,0x72,0x65,0x76,0x69,0x6F, + 0x75,0x73,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x1C,0x3E,0x41,0x6C, + 0x74,0x2B,0x43,0x75,0x72,0x73,0x6F,0x72,0x20,0x40,0x54,0x31, + 0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x62,0x6C,0x6F,0x63,0x6B, + 0x2E,0x16,0x3E,0x53,0x68,0x2B,0x46,0x33,0x20,0x40,0x54,0x31, + 0x36,0x30,0x43,0x75,0x74,0x20,0x74,0x72,0x61,0x63,0x6B,0x2E, + 0x17,0x3E,0x53,0x68,0x2B,0x46,0x34,0x20,0x40,0x54,0x31,0x36, + 0x30,0x43,0x6F,0x70,0x79,0x20,0x74,0x72,0x61,0x63,0x6B,0x2E, + 0x18,0x3E,0x53,0x68,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36, + 0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x74,0x72,0x61,0x63,0x6B, + 0x2E,0x1A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x33,0x20,0x40, + 0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x2E,0x1B,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46, + 0x34,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20, + 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x1C,0x3E,0x43,0x74, + 0x72,0x6C,0x2B,0x46,0x35,0x20,0x40,0x54,0x31,0x36,0x30,0x50, + 0x61,0x73,0x74,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x33,0x20,0x40,0x54, + 0x31,0x36,0x30,0x43,0x75,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B, + 0x2E,0x18,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x34,0x20,0x40,0x54, + 0x31,0x36,0x30,0x43,0x6F,0x70,0x79,0x20,0x62,0x6C,0x6F,0x63, + 0x6B,0x2E,0x19,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x35,0x20,0x40, + 0x54,0x31,0x36,0x30,0x50,0x61,0x73,0x74,0x65,0x20,0x62,0x6C, + 0x6F,0x63,0x6B,0x2E,0x20,0x3E,0x41,0x6C,0x74,0x2B,0x43,0x20, + 0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x72,0x6B,0x20,0x63, + 0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x74,0x72,0x61,0x63,0x6B, + 0x2E,0x00,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, + 0x31,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F, + 0x75,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x1C,0x52,0x69,0x67,0x68,0x74,0x20,0x63,0x74, + 0x72,0x6C,0x2E,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x6C, + 0x61,0x79,0x20,0x73,0x6F,0x6E,0x67,0x2E,0x1D,0x3E,0x41,0x6C, + 0x74,0x20,0x47,0x72,0x20,0x20,0x20,0x20,0x40,0x54,0x31,0x36, + 0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74,0x65,0x72, + 0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x73,0x68, + 0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x65, + 0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x2E,0x19,0x3E,0x53,0x70,0x61,0x63,0x65,0x20,0x20,0x20,0x20, + 0x40,0x54,0x31,0x36,0x30,0x53,0x74,0x6F,0x70,0x2F,0x45,0x64, + 0x69,0x74,0x2E,0x1B,0x3E,0x46,0x31,0x2E,0x2E,0x46,0x37,0x20, + 0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20, + 0x6F,0x63,0x74,0x61,0x76,0x65,0x2E,0x27,0x3E,0x4B,0x65,0x79, + 0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x45,0x73,0x63,0x20,0x40, 0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61,0x73,0x65, - 0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D, - 0x62,0x65,0x72,0x2E,0x29,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x52, + 0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E,0x22, + 0x3E,0x53,0x68,0x2B,0x28,0x31,0x2F,0x32,0x29,0x20,0x40,0x54, + 0x31,0x36,0x30,0x44,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20, + 0x63,0x75,0x72,0x73,0x6F,0x72,0x61,0x64,0x64,0x2E,0x29,0x3E, + 0x43,0x61,0x70,0x73,0x4C,0x6F,0x63,0x6B,0x20,0x6F,0x72,0x20, + 0x3C,0x3E,0x20,0x40,0x54,0x31,0x36,0x30,0x45,0x6E,0x74,0x65, + 0x72,0x20,0x4B,0x65,0x79,0x6F,0x66,0x66,0x2D,0x22,0x6E,0x6F, + 0x74,0x65,0x22,0x2E,0x25,0x3E,0x53,0x68,0x2B,0x4C,0x65,0x66, + 0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x63,0x72,0x65, + 0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20,0x70,0x6F,0x73, + 0x69,0x74,0x69,0x6F,0x6E,0x2E,0x26,0x3E,0x53,0x68,0x2B,0x52, 0x69,0x67,0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65, - 0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x70,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x2E,0x00,0x2C, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69, - 0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x20, - 0x28,0x6F,0x6E,0x20,0x61,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65, - 0x79,0x62,0x6F,0x61,0x72,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x52,0x69,0x67, - 0x68,0x74,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x20, - 0x40,0x54,0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x73,0x6F, - 0x6E,0x67,0x2E,0x32,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x61, - 0x6C,0x74,0x20,0x28,0x6F,0x72,0x20,0x6C,0x65,0x66,0x74,0x20, - 0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x29,0x20,0x20,0x20,0x20, - 0x40,0x54,0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61, - 0x74,0x74,0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68, - 0x74,0x20,0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x32, - 0x34,0x30,0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74, - 0x74,0x65,0x72,0x6E,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x57,0x69,0x6E,0x64,0x6F,0x77,0x20, - 0x73,0x77,0x69,0x74,0x63,0x68,0x69,0x6E,0x67,0x3A,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x05,0x43, - 0x74,0x72,0x6C,0x2B,0x16,0x3E,0x41,0x20,0x40,0x54,0x31,0x36, - 0x30,0x41,0x64,0x76,0x61,0x6E,0x63,0x65,0x64,0x20,0x65,0x64, - 0x69,0x74,0x2E,0x0E,0x3E,0x42,0x20,0x40,0x54,0x31,0x36,0x30, - 0x41,0x62,0x6F,0x75,0x74,0x2E,0x16,0x3E,0x43,0x20,0x40,0x54, - 0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61, - 0x74,0x69,0x6F,0x6E,0x2E,0x18,0x3E,0x44,0x20,0x40,0x54,0x31, - 0x36,0x30,0x44,0x69,0x73,0x6B,0x20,0x6F,0x70,0x65,0x72,0x61, - 0x74,0x69,0x6F,0x6E,0x73,0x2E,0x20,0x3E,0x45,0x20,0x40,0x54, - 0x31,0x36,0x30,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64, - 0x69,0x74,0x6F,0x72,0x20,0x65,0x78,0x74,0x65,0x6E,0x73,0x69, - 0x6F,0x6E,0x2E,0x0D,0x3E,0x48,0x20,0x40,0x54,0x31,0x36,0x30, - 0x48,0x65,0x6C,0x70,0x2E,0x1A,0x3E,0x49,0x20,0x40,0x54,0x31, - 0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, - 0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x2E,0x2B,0x3E,0x4D,0x20, - 0x40,0x54,0x31,0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x20,0x65, - 0x78,0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E,0x2E,0x20,0x28,0x4D, - 0x49,0x44,0x49,0x29,0x10,0x3E,0x4E,0x20,0x40,0x54,0x31,0x36, - 0x30,0x4E,0x69,0x62,0x62,0x6C,0x65,0x73,0x2E,0x10,0x3E,0x50, - 0x20,0x40,0x54,0x31,0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72, - 0x6E,0x2E,0x0D,0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x54, - 0x72,0x69,0x6D,0x2E,0x16,0x3E,0x53,0x20,0x40,0x54,0x31,0x36, + 0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x73,0x6F,0x6E,0x67,0x20, + 0x70,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x2E,0x28,0x3E,0x43, + 0x74,0x72,0x6C,0x2B,0x4C,0x65,0x66,0x74,0x20,0x40,0x54,0x31, + 0x36,0x30,0x49,0x6E,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x70, + 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62,0x65, + 0x72,0x2E,0x29,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x52,0x69,0x67, + 0x68,0x74,0x20,0x40,0x54,0x31,0x36,0x30,0x44,0x65,0x63,0x72, + 0x65,0x61,0x73,0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x2E,0x00,0x2C,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x73,0x63, + 0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x20,0x28,0x6F, + 0x6E,0x20,0x61,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79,0x62, + 0x6F,0x61,0x72,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x52,0x69,0x67,0x68,0x74, + 0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x20,0x20,0x40,0x54, + 0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x73,0x6F,0x6E,0x67, + 0x2E,0x32,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x61,0x6C,0x74, + 0x20,0x28,0x6F,0x72,0x20,0x6C,0x65,0x66,0x74,0x20,0x63,0x6F, + 0x6D,0x6D,0x61,0x6E,0x64,0x29,0x20,0x20,0x20,0x20,0x40,0x54, + 0x32,0x34,0x30,0x50,0x6C,0x61,0x79,0x20,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x2E,0x22,0x3E,0x52,0x69,0x67,0x68,0x74,0x20, + 0x73,0x68,0x69,0x66,0x74,0x20,0x20,0x40,0x54,0x32,0x34,0x30, + 0x52,0x65,0x63,0x6F,0x72,0x64,0x20,0x70,0x61,0x74,0x74,0x65, + 0x72,0x6E,0x2E,0x00,0x1B,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x57,0x69,0x6E,0x64,0x6F,0x77,0x20,0x73,0x77, + 0x69,0x74,0x63,0x68,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x05,0x43,0x74,0x72, + 0x6C,0x2B,0x16,0x3E,0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x41, + 0x64,0x76,0x61,0x6E,0x63,0x65,0x64,0x20,0x65,0x64,0x69,0x74, + 0x2E,0x0E,0x3E,0x42,0x20,0x40,0x54,0x31,0x36,0x30,0x41,0x62, + 0x6F,0x75,0x74,0x2E,0x16,0x3E,0x43,0x20,0x40,0x54,0x31,0x36, + 0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69, + 0x6F,0x6E,0x2E,0x18,0x3E,0x44,0x20,0x40,0x54,0x31,0x36,0x30, + 0x44,0x69,0x73,0x6B,0x20,0x6F,0x70,0x65,0x72,0x61,0x74,0x69, + 0x6F,0x6E,0x73,0x2E,0x20,0x3E,0x45,0x20,0x40,0x54,0x31,0x36, 0x30,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74, - 0x6F,0x72,0x2E,0x12,0x3E,0x54,0x20,0x40,0x54,0x31,0x36,0x30, - 0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x2E,0x23,0x3E, - 0x58,0x20,0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x69,0x6E,0x20, - 0x73,0x63,0x72,0x65,0x65,0x6E,0x2E,0x20,0x28,0x61,0x6C,0x6D, - 0x6F,0x73,0x74,0x20,0x61,0x6C,0x74,0x2B,0x58,0x29,0x27,0x3E, - 0x5A,0x20,0x40,0x54,0x31,0x36,0x30,0x46,0x75,0x6C,0x6C,0x20, - 0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x2E, - 0x20,0x28,0x5A,0x20,0x66,0x6F,0x72,0x20,0x73,0x69,0x5A,0x65, - 0x3F,0x29,0x19,0x3E,0x31,0x20,0x40,0x54,0x31,0x36,0x30,0x43, + 0x6F,0x72,0x20,0x65,0x78,0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E, + 0x2E,0x0D,0x3E,0x48,0x20,0x40,0x54,0x31,0x36,0x30,0x48,0x65, + 0x6C,0x70,0x2E,0x1A,0x3E,0x49,0x20,0x40,0x54,0x31,0x36,0x30, + 0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x65, + 0x64,0x69,0x74,0x6F,0x72,0x2E,0x2B,0x3E,0x4D,0x20,0x40,0x54, + 0x31,0x36,0x30,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E, + 0x74,0x20,0x65,0x64,0x69,0x74,0x6F,0x72,0x20,0x65,0x78,0x74, + 0x65,0x6E,0x73,0x69,0x6F,0x6E,0x2E,0x20,0x28,0x4D,0x49,0x44, + 0x49,0x29,0x10,0x3E,0x4E,0x20,0x40,0x54,0x31,0x36,0x30,0x4E, + 0x69,0x62,0x62,0x6C,0x65,0x73,0x2E,0x10,0x3E,0x50,0x20,0x40, + 0x54,0x31,0x36,0x30,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E, + 0x0D,0x3E,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x69, + 0x6D,0x2E,0x16,0x3E,0x53,0x20,0x40,0x54,0x31,0x36,0x30,0x53, + 0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,0x6F,0x72, + 0x2E,0x12,0x3E,0x54,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72, + 0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x2E,0x23,0x3E,0x58,0x20, + 0x40,0x54,0x31,0x36,0x30,0x4D,0x61,0x69,0x6E,0x20,0x73,0x63, + 0x72,0x65,0x65,0x6E,0x2E,0x20,0x28,0x61,0x6C,0x6D,0x6F,0x73, + 0x74,0x20,0x61,0x6C,0x74,0x2B,0x58,0x29,0x27,0x3E,0x5A,0x20, + 0x40,0x54,0x31,0x36,0x30,0x46,0x75,0x6C,0x6C,0x20,0x73,0x63, + 0x72,0x65,0x65,0x6E,0x20,0x65,0x64,0x69,0x74,0x2E,0x20,0x28, + 0x5A,0x20,0x66,0x6F,0x72,0x20,0x73,0x69,0x5A,0x65,0x3F,0x29, + 0x19,0x3E,0x31,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x6E, + 0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x23, + 0x31,0x2E,0x19,0x3E,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x43, 0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, - 0x20,0x23,0x31,0x2E,0x19,0x3E,0x32,0x20,0x40,0x54,0x31,0x36, + 0x20,0x23,0x32,0x2E,0x19,0x3E,0x33,0x20,0x40,0x54,0x31,0x36, 0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69, - 0x6F,0x6E,0x20,0x23,0x32,0x2E,0x19,0x3E,0x33,0x20,0x40,0x54, + 0x6F,0x6E,0x20,0x23,0x33,0x2E,0x19,0x3E,0x34,0x20,0x40,0x54, 0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61, - 0x74,0x69,0x6F,0x6E,0x20,0x23,0x33,0x2E,0x19,0x3E,0x34,0x20, - 0x40,0x54,0x31,0x36,0x30,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75, - 0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x23,0x34,0x2E,0x00,0x2D, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x73,0x65,0x6C, - 0x65,0x63,0x74,0x20,0x28,0x4E,0x75,0x6D,0x65,0x72,0x69,0x63, - 0x20,0x6B,0x65,0x79,0x70,0x61,0x64,0x29,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x54,0x6F, - 0x70,0x20,0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31, - 0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73, - 0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63, - 0x6B,0x2E,0x32,0x3E,0x27,0x2B,0x27,0x20,0x2B,0x54,0x6F,0x70, - 0x20,0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36, - 0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74, - 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B, - 0x20,0x2B,0x20,0x34,0x2E,0x23,0x3E,0x45,0x6E,0x74,0x65,0x72, - 0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74, - 0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20, - 0x62,0x61,0x6E,0x6B,0x2E,0x1D,0x3E,0x30,0x20,0x40,0x54,0x31, - 0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x6F,0x20, - 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x26, - 0x3E,0x31,0x2E,0x2E,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x53, + 0x74,0x69,0x6F,0x6E,0x20,0x23,0x34,0x2E,0x00,0x2D,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x73,0x65,0x6C,0x65,0x63, + 0x74,0x20,0x28,0x4E,0x75,0x6D,0x65,0x72,0x69,0x63,0x20,0x6B, + 0x65,0x79,0x70,0x61,0x64,0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x54,0x6F,0x70,0x20, + 0x34,0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36,0x30, + 0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72, + 0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E, + 0x32,0x3E,0x27,0x2B,0x27,0x20,0x2B,0x54,0x6F,0x70,0x20,0x34, + 0x20,0x6B,0x65,0x79,0x73,0x20,0x40,0x54,0x31,0x36,0x30,0x53, 0x65,0x6C,0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, - 0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63, - 0x6B,0x2E,0x19,0x3E,0x2C,0x20,0x40,0x54,0x31,0x36,0x30,0x43, - 0x6C,0x65,0x61,0x72,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x2C,0x20,0x40, - 0x54,0x31,0x36,0x30,0x43,0x6C,0x65,0x61,0x72,0x20,0x73,0x61, - 0x6D,0x70,0x6C,0x65,0x2E,0x27,0x3E,0x53,0x68,0x2B,0x55,0x70, - 0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74, - 0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x69,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x25,0x3E,0x53, - 0x68,0x2B,0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30, - 0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x65,0x78,0x74,0x20, - 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00, - 0x1F,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43, - 0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F,0x56,0x6F,0x6C,0x75,0x6D, - 0x65,0x20,0x6D,0x61,0x63,0x72,0x6F,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2D,0x41,0x6C,0x74, - 0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x57, - 0x72,0x69,0x74,0x65,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64, - 0x2F,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63, - 0x75,0x72,0x73,0x6F,0x72,0x2E,0x30,0x3E,0x53,0x68,0x2B,0x41, - 0x6C,0x74,0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36, - 0x30,0x52,0x65,0x61,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E, - 0x64,0x2F,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20, - 0x63,0x75,0x72,0x73,0x6F,0x72,0x2E,0x00,0x1C,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x61,0x6C,0x65, - 0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, - 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x25,0x53,0x68,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30, - 0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76, - 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x74,0x72,0x61, - 0x63,0x6B,0x2E,0x2A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20, - 0x40,0x54,0x31,0x36,0x30,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66, - 0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69, - 0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x27,0x3E, - 0x41,0x6C,0x74,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53, - 0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F, - 0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63, - 0x6B,0x2E,0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, - 0x30,0x31,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x3A, - 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x36,0x53,0x68,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30, - 0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75, - 0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, - 0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63, - 0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x35,0x3E,0x53,0x68,0x2B, + 0x6D,0x65,0x6E,0x74,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x2B, + 0x20,0x34,0x2E,0x23,0x3E,0x45,0x6E,0x74,0x65,0x72,0x20,0x40, + 0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x69, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x62,0x61, + 0x6E,0x6B,0x2E,0x1D,0x3E,0x30,0x20,0x40,0x54,0x31,0x36,0x30, + 0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x6E,0x6F,0x20,0x69,0x6E, + 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x26,0x3E,0x31, + 0x2E,0x2E,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65,0x6C, + 0x65,0x63,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, + 0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E, + 0x19,0x3E,0x2C,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x6C,0x65, + 0x61,0x72,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E, + 0x74,0x2E,0x18,0x3E,0x53,0x68,0x2B,0x2C,0x20,0x40,0x54,0x31, + 0x36,0x30,0x43,0x6C,0x65,0x61,0x72,0x20,0x73,0x61,0x6D,0x70, + 0x6C,0x65,0x2E,0x27,0x3E,0x53,0x68,0x2B,0x55,0x70,0x20,0x40, + 0x54,0x31,0x36,0x30,0x53,0x65,0x6C,0x65,0x63,0x74,0x20,0x70, + 0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x69,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x25,0x3E,0x53,0x68,0x2B, + 0x44,0x6F,0x77,0x6E,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x65, + 0x6C,0x65,0x63,0x74,0x20,0x6E,0x65,0x78,0x74,0x20,0x69,0x6E, + 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00,0x1F,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6D, + 0x6D,0x61,0x6E,0x64,0x2F,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20, + 0x6D,0x61,0x63,0x72,0x6F,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x2D,0x41,0x6C,0x74,0x2B,0x31, + 0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x57,0x72,0x69, + 0x74,0x65,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F,0x76, + 0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63,0x75,0x72, + 0x73,0x6F,0x72,0x2E,0x30,0x3E,0x53,0x68,0x2B,0x41,0x6C,0x74, + 0x2B,0x31,0x2E,0x2E,0x30,0x20,0x40,0x54,0x31,0x36,0x30,0x52, + 0x65,0x61,0x64,0x20,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x2F, + 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x61,0x74,0x20,0x63,0x75, + 0x72,0x73,0x6F,0x72,0x2E,0x00,0x1C,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66, + 0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x3A,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x25, + 0x53,0x68,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x63, + 0x61,0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C, + 0x75,0x6D,0x65,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B, + 0x2E,0x2A,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54, + 0x31,0x36,0x30,0x53,0x63,0x61,0x6C,0x65,0x2D,0x66,0x61,0x64, + 0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x6E,0x20, + 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E,0x27,0x3E,0x41,0x6C, + 0x74,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x63,0x61, + 0x6C,0x65,0x2D,0x66,0x61,0x64,0x65,0x20,0x76,0x6F,0x6C,0x75, + 0x6D,0x65,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x2E, + 0x00,0x14,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x3A,0x0B,0x3E, + 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x36,0x53, + 0x68,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72, + 0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72, + 0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, + 0x6E,0x74,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20, + 0x64,0x6F,0x77,0x6E,0x2E,0x35,0x3E,0x53,0x68,0x2B,0x46,0x38, + 0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70, + 0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20, + 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69, + 0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x3B, + 0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x37,0x20,0x40,0x54,0x31, + 0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20, + 0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x70,0x61, + 0x74,0x74,0x65,0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x39, + 0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x38,0x20,0x40,0x54,0x31, + 0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20, + 0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x70,0x61, + 0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70,0x2E,0x38,0x3E,0x41, + 0x6C,0x74,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36,0x30,0x54, + 0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72, + 0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B, + 0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x41,0x6C,0x74,0x2B, 0x46,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E, 0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E, 0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, - 0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70, - 0x2E,0x3B,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x37,0x20,0x40, - 0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73, - 0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20, - 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E, - 0x2E,0x39,0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x38,0x20,0x40, - 0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73, - 0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20, - 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70,0x2E,0x38, - 0x3E,0x41,0x6C,0x74,0x2B,0x46,0x37,0x20,0x40,0x54,0x31,0x36, - 0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63, - 0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72, - 0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F, - 0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x41,0x6C, - 0x74,0x2B,0x46,0x38,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72, - 0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x63,0x75,0x72,0x72, - 0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, - 0x6E,0x74,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20, - 0x75,0x70,0x2E,0x34,0x3E,0x53,0x68,0x2B,0x46,0x31,0x20,0x40, - 0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73, - 0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, - 0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x72,0x61, - 0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x32,0x3E,0x53,0x68, + 0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70, + 0x2E,0x34,0x3E,0x53,0x68,0x2B,0x46,0x31,0x20,0x40,0x54,0x31, + 0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20, + 0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, + 0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x72,0x61,0x63,0x6B, + 0x20,0x64,0x6F,0x77,0x6E,0x2E,0x32,0x3E,0x53,0x68,0x2B,0x46, + 0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73, + 0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73, + 0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20, + 0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x38,0x3E,0x43, + 0x74,0x72,0x6C,0x2B,0x46,0x31,0x20,0x40,0x54,0x31,0x36,0x30, + 0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C, + 0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, + 0x73,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x43,0x74,0x72,0x6C, 0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61, 0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69, 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69, - 0x6E,0x20,0x74,0x72,0x61,0x63,0x6B,0x20,0x75,0x70,0x2E,0x38, - 0x3E,0x43,0x74,0x72,0x6C,0x2B,0x46,0x31,0x20,0x40,0x54,0x31, - 0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20, - 0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, - 0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x36,0x3E,0x43,0x74, - 0x72,0x6C,0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54, - 0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C, - 0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73, - 0x20,0x69,0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20, - 0x75,0x70,0x2E,0x35,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x31,0x20, - 0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F, - 0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72, - 0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x62,0x6C, - 0x6F,0x63,0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x33,0x3E,0x41, - 0x6C,0x74,0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54, - 0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C, - 0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73, - 0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70, - 0x2E,0x01,0x3E,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, - 0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69, - 0x74,0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x1A,0x41,0x6C,0x74,0x2F,0x43,0x74,0x72, - 0x6C,0x2B,0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x61,0x6E, - 0x67,0x65,0x20,0x61,0x6C,0x6C,0x2E,0x17,0x3E,0x41,0x6C,0x74, - 0x2B,0x53,0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x68,0x6F,0x77, - 0x20,0x72,0x61,0x6E,0x67,0x65,0x2E,0x15,0x3E,0x41,0x6C,0x74, - 0x2B,0x5A,0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D, - 0x20,0x6F,0x75,0x74,0x2E,0x1A,0x3E,0x41,0x6C,0x74,0x2B,0x58, - 0x20,0x6F,0x72,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40, - 0x54,0x31,0x36,0x30,0x43,0x75,0x74,0x2E,0x16,0x3E,0x41,0x6C, - 0x74,0x2F,0x43,0x74,0x72,0x6C,0x2B,0x43,0x20,0x40,0x54,0x31, - 0x36,0x30,0x43,0x6F,0x70,0x79,0x2E,0x17,0x3E,0x41,0x6C,0x74, - 0x2F,0x43,0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54,0x31,0x36, - 0x30,0x50,0x61,0x73,0x74,0x65,0x2E,0x11,0x3E,0x41,0x6C,0x74, - 0x2B,0x52,0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x72,0x6F,0x70, - 0x2E,0x2A,0x3E,0x4D,0x6F,0x75,0x73,0x65,0x20,0x77,0x68,0x65, - 0x65,0x6C,0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D, - 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61, - 0x20,0x69,0x6E,0x2F,0x6F,0x75,0x74,0x2E,0x00,0x21,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65, - 0x20,0x66,0x6F,0x72,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79, - 0x62,0x6F,0x61,0x72,0x64,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3A,0x55,0x73,0x65,0x20, - 0x6C,0x65,0x66,0x74,0x20,0x63,0x74,0x72,0x6C,0x2E,0x20,0x66, - 0x6F,0x72,0x20,0x41,0x2F,0x58,0x2F,0x43,0x2F,0x56,0x20,0x28, - 0x6D,0x61,0x72,0x6B,0x20,0x61,0x6C,0x6C,0x2F,0x63,0x75,0x74, - 0x2F,0x63,0x6F,0x70,0x79,0x2F,0x70,0x61,0x73,0x74,0x65,0x29, - 0x20,0x6B,0x65,0x79,0x73,0x2E,0x4B,0x3E,0x54,0x68,0x69,0x73, - 0x20,0x61,0x70,0x70,0x6C,0x69,0x65,0x73,0x20,0x74,0x6F,0x20, - 0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65, - 0x64,0x69,0x74,0x6F,0x72,0x20,0x28,0x41,0x2F,0x43,0x2F,0x56, - 0x20,0x6F,0x6E,0x6C,0x79,0x29,0x20,0x61,0x6E,0x64,0x20,0x74, - 0x65,0x78,0x74,0x20,0x6D,0x61,0x72,0x6B,0x69,0x6E,0x67,0x20, - 0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x55,0x49,0x2E,0x00,0x03, - 0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x6E,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x75,0x70, + 0x2E,0x35,0x3E,0x41,0x6C,0x74,0x2B,0x46,0x31,0x20,0x40,0x54, + 0x31,0x36,0x30,0x54,0x72,0x61,0x6E,0x73,0x70,0x6F,0x73,0x65, + 0x20,0x61,0x6C,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x73,0x20,0x69,0x6E,0x20,0x62,0x6C,0x6F,0x63, + 0x6B,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x33,0x3E,0x41,0x6C,0x74, + 0x2B,0x46,0x32,0x20,0x40,0x54,0x31,0x36,0x30,0x54,0x72,0x61, + 0x6E,0x73,0x70,0x6F,0x73,0x65,0x20,0x61,0x6C,0x6C,0x20,0x69, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20,0x69, + 0x6E,0x20,0x62,0x6C,0x6F,0x63,0x6B,0x20,0x75,0x70,0x2E,0x01, + 0x3E,0x18,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, + 0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69,0x74,0x6F, + 0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x1A,0x41,0x6C,0x74,0x2F,0x43,0x74,0x72,0x6C,0x2B, + 0x41,0x20,0x40,0x54,0x31,0x36,0x30,0x52,0x61,0x6E,0x67,0x65, + 0x20,0x61,0x6C,0x6C,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2B,0x53, + 0x20,0x40,0x54,0x31,0x36,0x30,0x53,0x68,0x6F,0x77,0x20,0x72, + 0x61,0x6E,0x67,0x65,0x2E,0x15,0x3E,0x41,0x6C,0x74,0x2B,0x5A, + 0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,0x20,0x6F, + 0x75,0x74,0x2E,0x1A,0x3E,0x41,0x6C,0x74,0x2B,0x58,0x20,0x6F, + 0x72,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x20,0x40,0x54,0x31, + 0x36,0x30,0x43,0x75,0x74,0x2E,0x16,0x3E,0x41,0x6C,0x74,0x2F, + 0x43,0x74,0x72,0x6C,0x2B,0x43,0x20,0x40,0x54,0x31,0x36,0x30, + 0x43,0x6F,0x70,0x79,0x2E,0x17,0x3E,0x41,0x6C,0x74,0x2F,0x43, + 0x74,0x72,0x6C,0x2B,0x56,0x20,0x40,0x54,0x31,0x36,0x30,0x50, + 0x61,0x73,0x74,0x65,0x2E,0x11,0x3E,0x41,0x6C,0x74,0x2B,0x52, + 0x20,0x40,0x54,0x31,0x36,0x30,0x43,0x72,0x6F,0x70,0x2E,0x2A, + 0x3E,0x4D,0x6F,0x75,0x73,0x65,0x20,0x77,0x68,0x65,0x65,0x6C, + 0x20,0x40,0x54,0x31,0x36,0x30,0x5A,0x6F,0x6F,0x6D,0x20,0x73, + 0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x20,0x69, + 0x6E,0x2F,0x6F,0x75,0x74,0x2E,0x00,0x21,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x4E,0x6F,0x74,0x65,0x20,0x66, + 0x6F,0x72,0x20,0x4D,0x61,0x63,0x20,0x6B,0x65,0x79,0x62,0x6F, + 0x61,0x72,0x64,0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x3A,0x55,0x73,0x65,0x20,0x6C,0x65, + 0x66,0x74,0x20,0x63,0x74,0x72,0x6C,0x2E,0x20,0x66,0x6F,0x72, + 0x20,0x41,0x2F,0x58,0x2F,0x43,0x2F,0x56,0x20,0x28,0x6D,0x61, + 0x72,0x6B,0x20,0x61,0x6C,0x6C,0x2F,0x63,0x75,0x74,0x2F,0x63, + 0x6F,0x70,0x79,0x2F,0x70,0x61,0x73,0x74,0x65,0x29,0x20,0x6B, + 0x65,0x79,0x73,0x2E,0x4B,0x3E,0x54,0x68,0x69,0x73,0x20,0x61, + 0x70,0x70,0x6C,0x69,0x65,0x73,0x20,0x74,0x6F,0x20,0x74,0x68, + 0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x65,0x64,0x69, + 0x74,0x6F,0x72,0x20,0x28,0x41,0x2F,0x43,0x2F,0x56,0x20,0x6F, + 0x6E,0x6C,0x79,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,0x65,0x78, + 0x74,0x20,0x6D,0x61,0x72,0x6B,0x69,0x6E,0x67,0x20,0x69,0x6E, + 0x20,0x74,0x68,0x65,0x20,0x55,0x49,0x2E,0x00,0x03,0x45,0x4E, + 0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x1B,0x40,0x4C,0x48,0x6F,0x77,0x20,0x74,0x6F,0x20,0x75, - 0x73,0x65,0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B, - 0x65,0x72,0x20,0x49,0x49,0x0B,0x3E,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x41,0x6C,0x6C,0x20,0x22, - 0x6E,0x6F,0x74,0x2D,0x74,0x6F,0x6F,0x2D,0x74,0x72,0x69,0x76, - 0x69,0x61,0x6C,0x22,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F, - 0x6E,0x73,0x20,0x61,0x72,0x65,0x20,0x70,0x72,0x65,0x73,0x65, - 0x6E,0x74,0x65,0x64,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x28, - 0x6F,0x72,0x64,0x65,0x72,0x65,0x64,0x20,0x69,0x6E,0x22,0x77, - 0x69,0x6E,0x64,0x6F,0x77,0x73,0x29,0x20,0x77,0x69,0x74,0x68, - 0x20,0x61,0x20,0x73,0x68,0x6F,0x72,0x74,0x20,0x64,0x65,0x73, - 0x63,0x72,0x69,0x70,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x17,0x3E, - 0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x61, - 0x69,0x6E,0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3A,0x01,0x3E, - 0x22,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x42,0x50,0x4D,0x20,0x28,0x42,0x65,0x61,0x74,0x73,0x20,0x70, - 0x65,0x72,0x20,0x6D,0x69,0x6E,0x75,0x74,0x65,0x29,0x3A,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40, - 0x54,0x68,0x65,0x20,0x42,0x50,0x4D,0x20,0x73,0x65,0x74,0x74, - 0x69,0x6E,0x67,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20, - 0x68,0x6F,0x77,0x20,0x66,0x61,0x73,0x74,0x20,0x28,0x74,0x69, - 0x63,0x6B,0x73,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x29,0x20, - 0x74,0x68,0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x70,0x6C, - 0x61,0x79,0x65,0x72,0x1C,0x77,0x69,0x6C,0x6C,0x20,0x72,0x75, - 0x6E,0x2E,0x20,0x31,0x32,0x35,0x20,0x42,0x50,0x4D,0x20,0x3C, - 0x2D,0x3E,0x20,0x35,0x30,0x20,0x48,0x7A,0x2E,0x28,0x3E,0x4E, - 0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61, - 0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x73,0x65, - 0x63,0x6F,0x6E,0x64,0x20,0x3D,0x20,0x42,0x50,0x4D,0x2A,0x32, - 0x2F,0x35,0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, - 0x30,0x30,0x31,0x53,0x70,0x64,0x2C,0x20,0x53,0x70,0x65,0x65, - 0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x2C,0x53,0x70,0x65,0x65,0x64,0x20,0x3D,0x20,0x6E, - 0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61, - 0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x70,0x61, - 0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x00, - 0x0F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x41,0x64,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, - 0x43,0x30,0x30,0x32,0x3E,0x22,0x41,0x64,0x64,0x22,0x20,0x69, - 0x73,0x20,0x74,0x68,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72, - 0x20,0x6F,0x66,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20, - 0x6C,0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75, - 0x72,0x73,0x6F,0x72,0x20,0x6A,0x75,0x6D,0x70,0x73,0x20,0x77, - 0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x0C,0x65,0x64,0x69,0x74, - 0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x2E,0x00,0x0F,0x3E,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x74,0x6E, - 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x1B,0x54,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E, - 0x74,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75, - 0x6D,0x62,0x65,0x72,0x2E,0x00,0x0E,0x3E,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x4C,0x6E,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x43,0x54,0x68, - 0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20, - 0x6C,0x69,0x6E,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68, - 0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x70,0x61, - 0x74,0x74,0x65,0x72,0x6E,0x2E,0x20,0x55,0x70,0x20,0x74,0x6F, - 0x20,0x24,0x31,0x30,0x30,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E, - 0x20,0x4E,0x6F,0x74,0x65,0x40,0x74,0x68,0x61,0x74,0x20,0x46, - 0x54,0x32,0x20,0x77,0x6F,0x6E,0x27,0x74,0x20,0x77,0x61,0x72, - 0x6E,0x20,0x79,0x6F,0x75,0x20,0x69,0x66,0x20,0x79,0x6F,0x75, - 0x20,0x64,0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x74,0x68, - 0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x2E,0x20,0x54,0x68, - 0x65,0x20,0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x74,0x37,0x74, - 0x68,0x65,0x20,0x62,0x6F,0x74,0x74,0x6F,0x6D,0x20,0x6C,0x69, - 0x6E,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74, - 0x68,0x72,0x6F,0x77,0x6E,0x20,0x6F,0x75,0x74,0x20,0x74,0x6F, - 0x20,0x74,0x68,0x65,0x20,0x62,0x69,0x6E,0x61,0x72,0x79,0x20, - 0x73,0x70,0x61,0x63,0x65,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70,0x64,0x3A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x1B, + 0x40,0x4C,0x48,0x6F,0x77,0x20,0x74,0x6F,0x20,0x75,0x73,0x65, + 0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72, + 0x20,0x49,0x49,0x0B,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x32,0x40,0x3E,0x41,0x6C,0x6C,0x20,0x22,0x6E,0x6F, + 0x74,0x2D,0x74,0x6F,0x6F,0x2D,0x74,0x72,0x69,0x76,0x69,0x61, + 0x6C,0x22,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73, + 0x20,0x61,0x72,0x65,0x20,0x70,0x72,0x65,0x73,0x65,0x6E,0x74, + 0x65,0x64,0x20,0x62,0x65,0x6C,0x6F,0x77,0x20,0x28,0x6F,0x72, + 0x64,0x65,0x72,0x65,0x64,0x20,0x69,0x6E,0x22,0x77,0x69,0x6E, + 0x64,0x6F,0x77,0x73,0x29,0x20,0x77,0x69,0x74,0x68,0x20,0x61, + 0x20,0x73,0x68,0x6F,0x72,0x74,0x20,0x64,0x65,0x73,0x63,0x72, + 0x69,0x70,0x74,0x69,0x6F,0x6E,0x2E,0x00,0x17,0x3E,0x40,0x58, + 0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x61,0x69,0x6E, + 0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3A,0x01,0x3E,0x22,0x3E, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x42,0x50, + 0x4D,0x20,0x28,0x42,0x65,0x61,0x74,0x73,0x20,0x70,0x65,0x72, + 0x20,0x6D,0x69,0x6E,0x75,0x74,0x65,0x29,0x3A,0x0B,0x3E,0x40, + 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x54,0x68, + 0x65,0x20,0x42,0x50,0x4D,0x20,0x73,0x65,0x74,0x74,0x69,0x6E, + 0x67,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x68,0x6F, + 0x77,0x20,0x66,0x61,0x73,0x74,0x20,0x28,0x74,0x69,0x63,0x6B, + 0x73,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x29,0x20,0x74,0x68, + 0x65,0x20,0x6D,0x75,0x73,0x69,0x63,0x20,0x70,0x6C,0x61,0x79, + 0x65,0x72,0x1C,0x77,0x69,0x6C,0x6C,0x20,0x72,0x75,0x6E,0x2E, + 0x20,0x31,0x32,0x35,0x20,0x42,0x50,0x4D,0x20,0x3C,0x2D,0x3E, + 0x20,0x35,0x30,0x20,0x48,0x7A,0x2E,0x28,0x3E,0x4E,0x75,0x6D, + 0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,0x79,0x65, + 0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x73,0x65,0x63,0x6F, + 0x6E,0x64,0x20,0x3D,0x20,0x42,0x50,0x4D,0x2A,0x32,0x2F,0x35, + 0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, + 0x31,0x53,0x70,0x64,0x2C,0x20,0x53,0x70,0x65,0x65,0x64,0x3A, 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x44,0x45,0x78,0x70,0x61,0x6E,0x64,0x20,0x70,0x61,0x74,0x74, - 0x65,0x72,0x6E,0x2E,0x20,0x49,0x6E,0x73,0x65,0x72,0x74,0x73, - 0x20,0x61,0x20,0x62,0x6C,0x61,0x6E,0x6B,0x20,0x6C,0x69,0x6E, - 0x65,0x20,0x61,0x66,0x74,0x65,0x72,0x20,0x65,0x61,0x63,0x68, - 0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E, - 0x65,0x2E,0x20,0x55,0x73,0x65,0x66,0x75,0x6C,0x3C,0x69,0x66, - 0x20,0x79,0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F, - 0x20,0x63,0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x61,0x20,0x70, - 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20, - 0x72,0x75,0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65, - 0x64,0x20,0x32,0x2A,0x78,0x20,0x74,0x6F,0x20,0x61,0x1D,0x70, - 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20, - 0x72,0x75,0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65, - 0x64,0x20,0x78,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x53,0x68,0x6E,0x6B,0x3A,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2E,0x53, - 0x68,0x72,0x69,0x6E,0x6B,0x20,0x70,0x61,0x74,0x74,0x65,0x72, - 0x6E,0x2E,0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x73,0x20,0x61, - 0x6C,0x6C,0x20,0x6F,0x64,0x64,0x20,0x70,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x00,0x2A,0x3E, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68, - 0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, - 0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x73,0x65,0x6C,0x65, - 0x63,0x74,0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, - 0x40,0x43,0x30,0x30,0x32,0x3A,0x54,0x68,0x65,0x20,0x69,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61, - 0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B, - 0x20,0x6F,0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x61,0x6D, - 0x65,0x20,0x73,0x74,0x72,0x69,0x6E,0x67,0x2C,0x20,0x69,0x73, - 0x20,0x74,0x68,0x65,0x17,0x64,0x65,0x73,0x74,0x69,0x6E,0x61, - 0x74,0x69,0x6F,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x2E,0x3D,0x3E,0x54,0x68,0x65,0x20,0x69,0x6E, - 0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61, - 0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B, - 0x20,0x6F,0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x75,0x6D, - 0x62,0x65,0x72,0x2C,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20, - 0x73,0x6F,0x75,0x72,0x63,0x65,0x0B,0x69,0x6E,0x73,0x74,0x72, - 0x75,0x6D,0x65,0x6E,0x74,0x2E,0x1F,0x3E,0x54,0x68,0x65,0x20, - 0x73,0x61,0x6D,0x65,0x20,0x67,0x6F,0x65,0x73,0x20,0x66,0x6F, - 0x72,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65, - 0x73,0x2E,0x42,0x3E,0x59,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E, - 0x67,0x65,0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x6D,0x65,0x20, - 0x6F,0x6E,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, - 0x6D,0x65,0x6E,0x74,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20, - 0x62,0x79,0x20,0x63,0x6C,0x69,0x63,0x6B,0x69,0x6E,0x67,0x20, - 0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x07,0x62,0x75, - 0x74,0x74,0x6F,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x32, - 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73, - 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x22,0x3E,0x4C,0x65,0x66,0x74,0x20,0x62,0x75,0x74,0x74, - 0x6F,0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61, - 0x6E,0x6E,0x65,0x6C,0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x2E, - 0x35,0x3E,0x52,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74, - 0x6F,0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61, - 0x6E,0x6E,0x65,0x6C,0x20,0x6D,0x75,0x6C,0x74,0x69,0x2D,0x72, - 0x65,0x63,0x6F,0x72,0x64,0x2F,0x65,0x64,0x69,0x74,0x20,0x6F, - 0x6E,0x2F,0x6F,0x66,0x66,0x2E,0x42,0x3E,0x4C,0x65,0x66,0x74, - 0x2B,0x72,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F, - 0x6E,0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x61,0x6C,0x6C,0x20, - 0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x6F,0x66,0x66, - 0x20,0x65,0x78,0x63,0x65,0x70,0x74,0x20,0x74,0x68,0x65,0x20, - 0x73,0x65,0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6F,0x6E,0x65, - 0x2E,0x00,0x1C,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30, - 0x31,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20, - 0x45,0x64,0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x22,0x3E,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x57,0x68,0x61, - 0x74,0x20,0x69,0x73,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74, - 0x72,0x75,0x6D,0x65,0x6E,0x74,0x3F,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x41,0x20,0x46, + 0x2C,0x53,0x70,0x65,0x65,0x64,0x20,0x3D,0x20,0x6E,0x75,0x6D, + 0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x70,0x6C,0x61,0x79,0x65, + 0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x2F,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E,0x00,0x0F,0x3E, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x64, + 0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x3E,0x22,0x41,0x64,0x64,0x22,0x20,0x69,0x73,0x20, + 0x74,0x68,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F, + 0x66,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69, + 0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x73, + 0x6F,0x72,0x20,0x6A,0x75,0x6D,0x70,0x73,0x20,0x77,0x68,0x65, + 0x6E,0x20,0x79,0x6F,0x75,0x0C,0x65,0x64,0x69,0x74,0x20,0x61, + 0x20,0x6E,0x6F,0x74,0x65,0x2E,0x00,0x0F,0x3E,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x74,0x6E,0x3A,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B, + 0x54,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20, + 0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6E,0x75,0x6D,0x62, + 0x65,0x72,0x2E,0x00,0x0E,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x4C,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x43,0x54,0x68,0x65,0x20, + 0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x6F,0x66,0x20,0x6C,0x69, + 0x6E,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x20, + 0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x2E,0x20,0x55,0x70,0x20,0x74,0x6F,0x20,0x24, + 0x31,0x30,0x30,0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x20,0x4E, + 0x6F,0x74,0x65,0x40,0x74,0x68,0x61,0x74,0x20,0x46,0x54,0x32, + 0x20,0x77,0x6F,0x6E,0x27,0x74,0x20,0x77,0x61,0x72,0x6E,0x20, + 0x79,0x6F,0x75,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x64, + 0x65,0x63,0x72,0x65,0x61,0x73,0x65,0x20,0x74,0x68,0x69,0x73, + 0x20,0x76,0x61,0x6C,0x75,0x65,0x2E,0x20,0x54,0x68,0x65,0x20, + 0x6E,0x6F,0x74,0x65,0x73,0x20,0x61,0x74,0x37,0x74,0x68,0x65, + 0x20,0x62,0x6F,0x74,0x74,0x6F,0x6D,0x20,0x6C,0x69,0x6E,0x65, + 0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74,0x68,0x72, + 0x6F,0x77,0x6E,0x20,0x6F,0x75,0x74,0x20,0x74,0x6F,0x20,0x74, + 0x68,0x65,0x20,0x62,0x69,0x6E,0x61,0x72,0x79,0x20,0x73,0x70, + 0x61,0x63,0x65,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x45,0x78,0x70,0x64,0x3A,0x0B,0x3E, + 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x44,0x45, + 0x78,0x70,0x61,0x6E,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72, + 0x6E,0x2E,0x20,0x49,0x6E,0x73,0x65,0x72,0x74,0x73,0x20,0x61, + 0x20,0x62,0x6C,0x61,0x6E,0x6B,0x20,0x6C,0x69,0x6E,0x65,0x20, + 0x61,0x66,0x74,0x65,0x72,0x20,0x65,0x61,0x63,0x68,0x20,0x70, + 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x6C,0x69,0x6E,0x65,0x2E, + 0x20,0x55,0x73,0x65,0x66,0x75,0x6C,0x3C,0x69,0x66,0x20,0x79, + 0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x63, + 0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x61,0x20,0x70,0x61,0x74, + 0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,0x72,0x75, + 0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x20, + 0x32,0x2A,0x78,0x20,0x74,0x6F,0x20,0x61,0x1D,0x70,0x61,0x74, + 0x74,0x65,0x72,0x6E,0x20,0x74,0x68,0x61,0x74,0x20,0x72,0x75, + 0x6E,0x73,0x20,0x69,0x6E,0x20,0x73,0x70,0x65,0x65,0x64,0x20, + 0x78,0x2E,0x00,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x53,0x68,0x6E,0x6B,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x2E,0x53,0x68,0x72, + 0x69,0x6E,0x6B,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x2E, + 0x20,0x44,0x65,0x6C,0x65,0x74,0x65,0x73,0x20,0x61,0x6C,0x6C, + 0x20,0x6F,0x64,0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E, + 0x20,0x6C,0x69,0x6E,0x65,0x73,0x2E,0x00,0x2A,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20, + 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2F,0x73, + 0x61,0x6D,0x70,0x6C,0x65,0x20,0x73,0x65,0x6C,0x65,0x63,0x74, + 0x6F,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x3A,0x54,0x68,0x65,0x20,0x69,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,0x74,0x20, + 0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,0x20,0x6F, + 0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x61,0x6D,0x65,0x20, + 0x73,0x74,0x72,0x69,0x6E,0x67,0x2C,0x20,0x69,0x73,0x20,0x74, + 0x68,0x65,0x17,0x64,0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69, + 0x6F,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E, + 0x74,0x2E,0x3D,0x3E,0x54,0x68,0x65,0x20,0x69,0x6E,0x73,0x74, + 0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x74,0x68,0x61,0x74,0x20, + 0x68,0x61,0x73,0x20,0x61,0x20,0x6D,0x61,0x72,0x6B,0x20,0x6F, + 0x6E,0x20,0x69,0x74,0x27,0x73,0x20,0x6E,0x75,0x6D,0x62,0x65, + 0x72,0x2C,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F, + 0x75,0x72,0x63,0x65,0x0B,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x2E,0x1F,0x3E,0x54,0x68,0x65,0x20,0x73,0x61, + 0x6D,0x65,0x20,0x67,0x6F,0x65,0x73,0x20,0x66,0x6F,0x72,0x20, + 0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x2E, + 0x42,0x3E,0x59,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E,0x67,0x65, + 0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x6D,0x65,0x20,0x6F,0x6E, + 0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, + 0x6E,0x74,0x2F,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x62,0x79, + 0x20,0x63,0x6C,0x69,0x63,0x6B,0x69,0x6E,0x67,0x20,0x74,0x68, + 0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x07,0x62,0x75,0x74,0x74, + 0x6F,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x32,0x30,0x40, + 0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73,0x3A,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x22, + 0x3E,0x4C,0x65,0x66,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E, + 0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,0x6E,0x6E, + 0x65,0x6C,0x20,0x6F,0x6E,0x2F,0x6F,0x66,0x66,0x2E,0x35,0x3E, + 0x52,0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E, + 0x3A,0x20,0x54,0x75,0x72,0x6E,0x20,0x63,0x68,0x61,0x6E,0x6E, + 0x65,0x6C,0x20,0x6D,0x75,0x6C,0x74,0x69,0x2D,0x72,0x65,0x63, + 0x6F,0x72,0x64,0x2F,0x65,0x64,0x69,0x74,0x20,0x6F,0x6E,0x2F, + 0x6F,0x66,0x66,0x2E,0x42,0x3E,0x4C,0x65,0x66,0x74,0x2B,0x72, + 0x69,0x67,0x68,0x74,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x3A, + 0x20,0x54,0x75,0x72,0x6E,0x20,0x61,0x6C,0x6C,0x20,0x63,0x68, + 0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x6F,0x66,0x66,0x20,0x65, + 0x78,0x63,0x65,0x70,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x65, + 0x6C,0x65,0x63,0x74,0x65,0x64,0x20,0x6F,0x6E,0x65,0x2E,0x00, + 0x1C,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x49, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x45,0x64, + 0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x22,0x3E,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x57,0x68,0x61,0x74,0x20, + 0x69,0x73,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, + 0x6D,0x65,0x6E,0x74,0x3F,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x1E,0x41,0x20,0x46,0x61,0x73, + 0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x20,0x69, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x73, + 0x3A,0x15,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75, + 0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x16, + 0x3E,0x20,0x20,0x20,0x31,0x20,0x50,0x61,0x6E,0x6E,0x69,0x6E, + 0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x1D,0x3E, + 0x20,0x20,0x20,0x31,0x20,0x41,0x75,0x74,0x6F,0x2D,0x76,0x69, + 0x62,0x72,0x61,0x74,0x6F,0x20,0x64,0x65,0x66,0x69,0x6E,0x69, + 0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31,0x2E,0x2E, + 0x31,0x36,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x28,0x73,0x29, + 0x1F,0x3E,0x20,0x20,0x20,0x31,0x20,0x4B,0x65,0x79,0x62,0x6F, + 0x61,0x72,0x64,0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x64,0x65, + 0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x15,0x3E,0x20,0x20, + 0x20,0x31,0x20,0x4D,0x49,0x44,0x49,0x20,0x64,0x65,0x66,0x69, + 0x6E,0x69,0x74,0x69,0x6F,0x6E,0x00,0x1B,0x3E,0x41,0x20,0x46, 0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32, - 0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20, - 0x69,0x73,0x3A,0x15,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F, - 0x6C,0x75,0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70, - 0x65,0x16,0x3E,0x20,0x20,0x20,0x31,0x20,0x50,0x61,0x6E,0x6E, - 0x69,0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65, - 0x1D,0x3E,0x20,0x20,0x20,0x31,0x20,0x41,0x75,0x74,0x6F,0x2D, - 0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x64,0x65,0x66,0x69, - 0x6E,0x69,0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31, - 0x2E,0x2E,0x31,0x36,0x20,0x53,0x61,0x6D,0x70,0x6C,0x65,0x28, - 0x73,0x29,0x1F,0x3E,0x20,0x20,0x20,0x31,0x20,0x4B,0x65,0x79, - 0x62,0x6F,0x61,0x72,0x64,0x20,0x73,0x70,0x6C,0x69,0x74,0x20, - 0x64,0x65,0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x15,0x3E, - 0x20,0x20,0x20,0x31,0x20,0x4D,0x49,0x44,0x49,0x20,0x64,0x65, - 0x66,0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x00,0x1B,0x3E,0x41, - 0x20,0x46,0x61,0x73,0x74,0x74,0x72,0x61,0x63,0x6B,0x65,0x72, - 0x20,0x32,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x73, - 0x3A,0x29,0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75, - 0x6D,0x65,0x2F,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x46, - 0x69,0x6E,0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x64,0x65,0x66, - 0x69,0x6E,0x69,0x74,0x69,0x6F,0x6E,0x14,0x3E,0x20,0x20,0x20, - 0x31,0x20,0x52,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x74, - 0x6F,0x6E,0x65,0x2E,0x10,0x3E,0x20,0x20,0x20,0x31,0x20,0x57, - 0x61,0x76,0x65,0x20,0x66,0x6F,0x72,0x6D,0x2E,0x00,0x1F,0x3E, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68, - 0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x65,0x6E,0x76, - 0x65,0x6C,0x6F,0x70,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, - 0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x41,0x6E,0x20,0x69, - 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x27,0x73,0x20, - 0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x69,0x73,0x20,0x64,0x65, - 0x66,0x69,0x6E,0x65,0x64,0x20,0x62,0x79,0x20,0x69,0x74,0x73, - 0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x63,0x75, - 0x72,0x76,0x65,0x2E,0x20,0x49,0x66,0x20,0x74,0x68,0x65,0x3E, - 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x68, - 0x61,0x73,0x20,0x61,0x20,0x73,0x75,0x73,0x74,0x61,0x69,0x6E, - 0x20,0x70,0x6F,0x69,0x6E,0x74,0x2C,0x20,0x74,0x68,0x65,0x20, - 0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x77,0x69,0x6C, - 0x6C,0x20,0x73,0x74,0x6F,0x70,0x20,0x61,0x74,0x20,0x74,0x68, - 0x61,0x74,0x42,0x70,0x6F,0x69,0x6E,0x74,0x20,0x75,0x6E,0x74, - 0x69,0x6C,0x20,0x61,0x20,0x6B,0x65,0x79,0x2D,0x6F,0x66,0x66, - 0x20,0x6E,0x6F,0x74,0x65,0x20,0x68,0x61,0x73,0x20,0x62,0x65, - 0x65,0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x2E,0x20,0x57, - 0x68,0x65,0x6E,0x20,0x61,0x20,0x6B,0x65,0x79,0x2D,0x6F,0x66, - 0x66,0x20,0x6E,0x6F,0x74,0x65,0x20,0x69,0x73,0x1D,0x70,0x6C, - 0x61,0x79,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20,0x22,0x66, - 0x61,0x64,0x65,0x6F,0x75,0x74,0x22,0x20,0x62,0x65,0x67,0x69, - 0x6E,0x73,0x2E,0x44,0x3E,0x4F,0x6E,0x65,0x20,0x70,0x69,0x78, - 0x65,0x6C,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x65,0x6E, - 0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F, - 0x77,0x20,0x63,0x6F,0x72,0x72,0x65,0x73,0x70,0x6F,0x6E,0x64, - 0x73,0x20,0x74,0x6F,0x20,0x6F,0x6E,0x65,0x20,0x70,0x6C,0x61, - 0x79,0x65,0x72,0x2D,0x74,0x69,0x63,0x6B,0x2E,0x20,0x49,0x66, - 0x3C,0x74,0x68,0x65,0x20,0x42,0x50,0x4D,0x20,0x69,0x73,0x20, - 0x31,0x32,0x35,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x20, - 0x63,0x6F,0x6E,0x73,0x75,0x6D,0x65,0x20,0x35,0x30,0x20,0x70, - 0x69,0x78,0x65,0x6C,0x2F,0x73,0x65,0x63,0x6F,0x6E,0x64,0x2E, - 0x20,0x54,0x68,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x27, - 0x73,0x1A,0x22,0x73,0x69,0x7A,0x65,0x22,0x20,0x69,0x73,0x20, - 0x61,0x62,0x6F,0x75,0x74,0x20,0x36,0x20,0x73,0x65,0x63,0x6F, - 0x6E,0x64,0x73,0x2E,0x3E,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75, - 0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x74,0x68,0x65,0x20,0x72, - 0x69,0x67,0x68,0x74,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x62, - 0x75,0x74,0x74,0x6F,0x6E,0x20,0x61,0x74,0x20,0x74,0x68,0x65, - 0x20,0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x62, - 0x75,0x74,0x74,0x6F,0x6E,0x73,0x2C,0x3F,0x79,0x6F,0x75,0x27, - 0x6C,0x6C,0x20,0x73,0x74,0x6F,0x72,0x65,0x20,0x74,0x68,0x65, - 0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x65,0x6E,0x76, - 0x65,0x6C,0x6F,0x70,0x65,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74, - 0x68,0x61,0x74,0x20,0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E, - 0x65,0x20,0x63,0x65,0x6C,0x6C,0x2E,0x20,0x54,0x68,0x65,0x30, - 0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x61, - 0x72,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x20,0x69,0x6E, - 0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x75, - 0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x69,0x6C,0x65,0x2E, - 0x43,0x3E,0x50,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20, - 0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x31,0x20,0x69,0x73,0x20, - 0x74,0x68,0x65,0x20,0x64,0x65,0x66,0x61,0x75,0x6C,0x74,0x20, - 0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x2E,0x20,0x54,0x68, - 0x69,0x73,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20,0x74,0x68,0x61, - 0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x42,0x6C,0x6F,0x61, - 0x64,0x20,0x61,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2C,0x20, - 0x69,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20,0x67,0x65,0x74,0x20, - 0x61,0x6C,0x6C,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65, - 0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E, - 0x20,0x66,0x72,0x6F,0x6D,0x20,0x70,0x72,0x65,0x64,0x65,0x66, - 0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x31, - 0x2C,0x20,0x69,0x6E,0x63,0x6C,0x75,0x64,0x69,0x6E,0x67,0x20, - 0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x2E, - 0x42,0x3E,0x4E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20, - 0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x74,0x75,0x72,0x6E,0x20, - 0x74,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2D,0x65, - 0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x6F,0x66,0x66,0x2C, - 0x20,0x79,0x6F,0x75,0x20,0x64,0x6F,0x6E,0x27,0x74,0x20,0x74, - 0x75,0x72,0x6E,0x20,0x74,0x68,0x65,0x0C,0x76,0x69,0x62,0x72, - 0x61,0x74,0x6F,0x20,0x6F,0x66,0x66,0x2E,0x00,0x20,0x3E,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65, - 0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x65,0x6E,0x76, - 0x65,0x6C,0x6F,0x70,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, - 0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E,0x53,0x61,0x6D,0x65, - 0x20,0x61,0x73,0x20,0x61,0x62,0x6F,0x76,0x65,0x2C,0x20,0x65, - 0x78,0x63,0x65,0x70,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20,0x74, - 0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x76,0x69,0x62,0x72, - 0x61,0x74,0x6F,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x63, - 0x6F,0x6E,0x6E,0x65,0x63,0x74,0x65,0x64,0x20,0x74,0x6F,0x15, - 0x74,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x20, - 0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x2E,0x00,0x10,0x3E, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x75, - 0x6E,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, - 0x30,0x30,0x32,0x3F,0x3E,0x54,0x68,0x65,0x20,0x66,0x69,0x6E, - 0x65,0x2D,0x74,0x75,0x6E,0x65,0x20,0x72,0x65,0x73,0x6F,0x6C, - 0x75,0x74,0x69,0x6F,0x6E,0x20,0x68,0x61,0x73,0x20,0x62,0x65, - 0x65,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x66, - 0x72,0x6F,0x6D,0x20,0x61,0x20,0x73,0x69,0x67,0x6E,0x65,0x64, - 0x20,0x6E,0x69,0x62,0x62,0x6C,0x65,0x27,0x28,0x2D,0x38,0x2E, - 0x2E,0x2B,0x37,0x29,0x20,0x74,0x6F,0x20,0x61,0x20,0x73,0x69, - 0x67,0x6E,0x65,0x64,0x20,0x62,0x79,0x74,0x65,0x20,0x28,0x2D, - 0x31,0x32,0x38,0x2E,0x2E,0x2B,0x31,0x32,0x37,0x29,0x2E,0x00, - 0x13,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x46,0x61,0x64,0x65,0x6F,0x75,0x74,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B,0x3E,0x54,0x68, - 0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x66,0x61, - 0x64,0x65,0x6F,0x75,0x74,0x20,0x73,0x70,0x65,0x65,0x64,0x2E, - 0x00,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, - 0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x73,0x77,0x65, - 0x65,0x70,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, - 0x30,0x30,0x32,0x3E,0x3E,0x54,0x68,0x69,0x73,0x20,0x69,0x73, - 0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x28,0x69, - 0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63, - 0x6B,0x73,0x29,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x69,0x6C, - 0x6C,0x20,0x62,0x79,0x70,0x61,0x73,0x73,0x20,0x75,0x6E,0x74, - 0x69,0x6C,0x20,0x74,0x68,0x65,0x2D,0x61,0x75,0x74,0x6F,0x2D, - 0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x72,0x65,0x61,0x63,0x68,0x20,0x69,0x74,0x27,0x73,0x20, - 0x66,0x69,0x6E,0x61,0x6C,0x20,0x61,0x6D,0x70,0x6C,0x69,0x74, - 0x75,0x64,0x65,0x2E,0x00,0x1E,0x3E,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x70,0x69,0x61, - 0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x3A, + 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x69,0x73,0x3A,0x28, + 0x3E,0x20,0x20,0x20,0x31,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65, + 0x2F,0x50,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2F,0x46,0x69,0x6E, + 0x65,0x74,0x75,0x6E,0x65,0x20,0x64,0x65,0x66,0x69,0x6E,0x69, + 0x74,0x69,0x6F,0x6E,0x13,0x3E,0x20,0x20,0x20,0x31,0x20,0x52, + 0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F,0x74,0x65, + 0x0E,0x3E,0x20,0x20,0x20,0x31,0x20,0x57,0x61,0x76,0x65,0x66, + 0x6F,0x72,0x6D,0x00,0x1F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75, + 0x6D,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x3A, 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x3F,0x3E,0x54,0x68,0x65,0x20,0x70,0x69,0x61,0x6E,0x6F,0x20, - 0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x20,0x64,0x65,0x66, - 0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79, - 0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x66,0x6F,0x72,0x20,0x61, - 0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, - 0x2E,0x20,0x54,0x6F,0x3F,0x63,0x68,0x61,0x6E,0x67,0x65,0x20, - 0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x20,0x73,0x70,0x6C,0x69, - 0x74,0x2C,0x20,0x63,0x68,0x6F,0x6F,0x73,0x65,0x20,0x61,0x20, - 0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x74,0x68,0x69, - 0x6E,0x20,0x74,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, - 0x6D,0x65,0x6E,0x74,0x20,0x61,0x6E,0x64,0x1C,0x74,0x68,0x65, - 0x6E,0x20,0x22,0x64,0x72,0x61,0x77,0x22,0x20,0x6F,0x6E,0x20, - 0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64, - 0x2E,0x42,0x3E,0x54,0x68,0x65,0x20,0x6E,0x6F,0x74,0x65,0x73, - 0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x77,0x69,0x74,0x68, - 0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74, - 0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20, - 0x61,0x72,0x65,0x20,0x69,0x6E,0x64,0x69,0x63,0x61,0x74,0x65, - 0x64,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x09,0x6B,0x65,0x79, - 0x62,0x6F,0x61,0x72,0x64,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6D,0x70,0x6F,0x72, - 0x74,0x61,0x6E,0x74,0x20,0x6E,0x6F,0x74,0x65,0x3A,0x0B,0x3E, - 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x40,0x3E, - 0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20, - 0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2C,0x20,0x74,0x75,0x6E, - 0x65,0x20,0x61,0x6E,0x64,0x20,0x72,0x65,0x6C,0x61,0x74,0x69, - 0x76,0x65,0x20,0x74,0x6F,0x6E,0x65,0x20,0x69,0x73,0x20,0x64, - 0x65,0x66,0x69,0x6E,0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x45, - 0x41,0x43,0x48,0x41,0x53,0x41,0x4D,0x50,0x4C,0x45,0x20,0x69, - 0x6E,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x2E,0x20,0x41,0x6C,0x6C,0x20,0x6F,0x74,0x68, - 0x65,0x72,0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69, - 0x6F,0x6E,0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65, - 0x64,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65,0x12,0x65,0x6E, - 0x74,0x69,0x72,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x2E,0x00,0x31,0x40,0x58,0x30,0x32,0x30,0x40, - 0x43,0x30,0x30,0x31,0x49,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, - 0x6E,0x74,0x20,0x45,0x64,0x69,0x74,0x6F,0x72,0x20,0x45,0x78, - 0x74,0x65,0x6E,0x73,0x69,0x6F,0x6E,0x3A,0x20,0x28,0x49,0x2E, - 0x45,0x2E,0x45,0x78,0x74,0x2E,0x29,0x01,0x3E,0x10,0x3E,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x49,0x44, - 0x49,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x28,0x3E,0x27,0x70,0x2E,0x27,0x20,0x73,0x74,0x61, - 0x6E,0x64,0x73,0x20,0x66,0x6F,0x72,0x20,0x22,0x70,0x72,0x6F, - 0x67,0x72,0x61,0x6D,0x22,0x20,0x28,0x69,0x6E,0x73,0x74,0x72, - 0x75,0x6D,0x65,0x6E,0x74,0x29,0x2E,0x40,0x3E,0x53,0x65,0x76, - 0x65,0x72,0x61,0x6C,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, - 0x65,0x6E,0x74,0x73,0x20,0x63,0x61,0x6E,0x20,0x68,0x61,0x76, - 0x65,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x74, - 0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x63,0x68,0x61,0x6E, - 0x6E,0x65,0x6C,0x20,0x62,0x75,0x74,0x20,0x77,0x69,0x74,0x68, - 0x33,0x64,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74,0x20,0x70, - 0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x2E,0x20,0x46,0x54,0x32, - 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x73,0x20,0x74,0x68,0x65, - 0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x6F,0x6E, - 0x20,0x74,0x68,0x65,0x43,0x4D,0x49,0x44,0x49,0x2D,0x63,0x68, - 0x61,0x6E,0x6E,0x65,0x6C,0x73,0x20,0x69,0x6E,0x73,0x74,0x61, - 0x6E,0x74,0x6C,0x79,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20, - 0x70,0x6C,0x61,0x79,0x20,0x69,0x66,0x20,0x64,0x69,0x66,0x66, + 0x40,0x3E,0x41,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x27,0x73,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x64,0x20, + 0x62,0x79,0x20,0x69,0x74,0x73,0x20,0x65,0x6E,0x76,0x65,0x6C, + 0x6F,0x70,0x65,0x20,0x63,0x75,0x72,0x76,0x65,0x2E,0x20,0x49, + 0x66,0x20,0x74,0x68,0x65,0x3E,0x69,0x6E,0x73,0x74,0x72,0x75, + 0x6D,0x65,0x6E,0x74,0x20,0x68,0x61,0x73,0x20,0x61,0x20,0x73, + 0x75,0x73,0x74,0x61,0x69,0x6E,0x20,0x70,0x6F,0x69,0x6E,0x74, + 0x2C,0x20,0x74,0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F, + 0x70,0x65,0x20,0x77,0x69,0x6C,0x6C,0x20,0x73,0x74,0x6F,0x70, + 0x20,0x61,0x74,0x20,0x74,0x68,0x61,0x74,0x42,0x70,0x6F,0x69, + 0x6E,0x74,0x20,0x75,0x6E,0x74,0x69,0x6C,0x20,0x61,0x20,0x6B, + 0x65,0x79,0x2D,0x6F,0x66,0x66,0x20,0x6E,0x6F,0x74,0x65,0x20, + 0x68,0x61,0x73,0x20,0x62,0x65,0x65,0x6E,0x20,0x70,0x6C,0x61, + 0x79,0x65,0x64,0x2E,0x20,0x57,0x68,0x65,0x6E,0x20,0x61,0x20, + 0x6B,0x65,0x79,0x2D,0x6F,0x66,0x66,0x20,0x6E,0x6F,0x74,0x65, + 0x20,0x69,0x73,0x1D,0x70,0x6C,0x61,0x79,0x65,0x64,0x2C,0x20, + 0x74,0x68,0x65,0x20,0x22,0x66,0x61,0x64,0x65,0x6F,0x75,0x74, + 0x22,0x20,0x62,0x65,0x67,0x69,0x6E,0x73,0x2E,0x44,0x3E,0x4F, + 0x6E,0x65,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x69,0x6E,0x20, + 0x74,0x68,0x65,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65, + 0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x20,0x63,0x6F,0x72,0x72, + 0x65,0x73,0x70,0x6F,0x6E,0x64,0x73,0x20,0x74,0x6F,0x20,0x6F, + 0x6E,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x2D,0x74,0x69, + 0x63,0x6B,0x2E,0x20,0x49,0x66,0x3C,0x74,0x68,0x65,0x20,0x42, + 0x50,0x4D,0x20,0x69,0x73,0x20,0x31,0x32,0x35,0x2C,0x20,0x79, + 0x6F,0x75,0x27,0x6C,0x6C,0x20,0x63,0x6F,0x6E,0x73,0x75,0x6D, + 0x65,0x20,0x35,0x30,0x20,0x70,0x69,0x78,0x65,0x6C,0x2F,0x73, + 0x65,0x63,0x6F,0x6E,0x64,0x2E,0x20,0x54,0x68,0x65,0x20,0x77, + 0x69,0x6E,0x64,0x6F,0x77,0x27,0x73,0x1A,0x22,0x73,0x69,0x7A, + 0x65,0x22,0x20,0x69,0x73,0x20,0x61,0x62,0x6F,0x75,0x74,0x20, + 0x36,0x20,0x73,0x65,0x63,0x6F,0x6E,0x64,0x73,0x2E,0x3E,0x3E, + 0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x70,0x72,0x65,0x73,0x73, + 0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20,0x6D, + 0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x20, + 0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x65,0x64,0x65, + 0x66,0x69,0x6E,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x73, + 0x2C,0x3F,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x20,0x73,0x74,0x6F, + 0x72,0x65,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72,0x65, + 0x6E,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x20, + 0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x61,0x74,0x20,0x70,0x72, + 0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x63,0x65,0x6C,0x6C, + 0x2E,0x20,0x54,0x68,0x65,0x30,0x70,0x72,0x65,0x64,0x65,0x66, + 0x69,0x6E,0x65,0x73,0x20,0x61,0x72,0x65,0x20,0x73,0x74,0x6F, + 0x72,0x65,0x64,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x63, + 0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, + 0x20,0x66,0x69,0x6C,0x65,0x2E,0x43,0x3E,0x50,0x72,0x65,0x64, + 0x65,0x66,0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72, + 0x20,0x31,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x64,0x65, + 0x66,0x61,0x75,0x6C,0x74,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F, + 0x70,0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61, + 0x6E,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x66,0x20,0x79, + 0x6F,0x75,0x42,0x6C,0x6F,0x61,0x64,0x20,0x61,0x20,0x73,0x61, + 0x6D,0x70,0x6C,0x65,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C, + 0x6C,0x20,0x67,0x65,0x74,0x20,0x61,0x6C,0x6C,0x20,0x65,0x6E, + 0x76,0x65,0x6C,0x6F,0x70,0x65,0x20,0x69,0x6E,0x66,0x6F,0x72, + 0x6D,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x72,0x6F,0x6D,0x20, + 0x70,0x72,0x65,0x64,0x65,0x66,0x69,0x6E,0x65,0x20,0x6E,0x75, + 0x6D,0x62,0x65,0x72,0x20,0x31,0x2C,0x20,0x69,0x6E,0x63,0x6C, + 0x75,0x64,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20,0x76,0x69, + 0x62,0x72,0x61,0x74,0x6F,0x2E,0x42,0x3E,0x4E,0x6F,0x74,0x65, + 0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75, + 0x20,0x74,0x75,0x72,0x6E,0x20,0x74,0x68,0x65,0x20,0x76,0x6F, + 0x6C,0x75,0x6D,0x65,0x2D,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70, + 0x65,0x20,0x6F,0x66,0x66,0x2C,0x20,0x79,0x6F,0x75,0x20,0x64, + 0x6F,0x6E,0x27,0x74,0x20,0x74,0x75,0x72,0x6E,0x20,0x74,0x68, + 0x65,0x0C,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x6F,0x66, + 0x66,0x2E,0x00,0x20,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x54,0x68,0x65,0x20,0x70,0x61,0x6E,0x6E,0x69, + 0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F,0x70,0x65,0x3A, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x40,0x3E,0x53,0x61,0x6D,0x65,0x20,0x61,0x73,0x20,0x61,0x62, + 0x6F,0x76,0x65,0x2C,0x20,0x65,0x78,0x63,0x65,0x70,0x74,0x20, + 0x66,0x72,0x6F,0x6D,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68, + 0x65,0x20,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x69,0x73, + 0x20,0x6E,0x6F,0x74,0x20,0x63,0x6F,0x6E,0x6E,0x65,0x63,0x74, + 0x65,0x64,0x20,0x74,0x6F,0x15,0x74,0x68,0x65,0x20,0x70,0x61, + 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x65,0x6E,0x76,0x65,0x6C,0x6F, + 0x70,0x65,0x2E,0x00,0x1B,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x54,0x75,0x6E,0x65,0x20,0x28,0x66,0x69, + 0x6E,0x65,0x74,0x75,0x6E,0x65,0x29,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3E,0x3E,0x54,0x68, + 0x65,0x20,0x66,0x69,0x6E,0x65,0x74,0x75,0x6E,0x65,0x20,0x72, + 0x65,0x73,0x6F,0x6C,0x75,0x74,0x69,0x6F,0x6E,0x20,0x68,0x61, + 0x73,0x20,0x62,0x65,0x65,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67, + 0x65,0x64,0x20,0x66,0x72,0x6F,0x6D,0x20,0x61,0x20,0x73,0x69, + 0x67,0x6E,0x65,0x64,0x20,0x6E,0x69,0x62,0x62,0x6C,0x65,0x27, + 0x28,0x2D,0x38,0x2E,0x2E,0x2B,0x37,0x29,0x20,0x74,0x6F,0x20, + 0x61,0x20,0x73,0x69,0x67,0x6E,0x65,0x64,0x20,0x62,0x79,0x74, + 0x65,0x20,0x28,0x2D,0x31,0x32,0x38,0x2E,0x2E,0x2B,0x31,0x32, + 0x37,0x29,0x2E,0x46,0x3E,0x4E,0x4F,0x54,0x45,0x3A,0x20,0x54, + 0x68,0x65,0x20,0x6C,0x61,0x73,0x74,0x20,0x33,0x20,0x62,0x69, + 0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x64,0x69,0x73,0x63,0x61, + 0x72,0x64,0x65,0x64,0x20,0x64,0x75,0x72,0x69,0x6E,0x67,0x20, + 0x70,0x6C,0x61,0x79,0x62,0x61,0x63,0x6B,0x2C,0x20,0x73,0x6F, + 0x20,0x74,0x68,0x65,0x20,0x74,0x72,0x75,0x65,0x20,0x73,0x74, + 0x65,0x70,0x17,0x73,0x69,0x7A,0x65,0x20,0x69,0x73,0x20,0x38, + 0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x20,0x6F,0x66,0x20, + 0x31,0x2E,0x00,0x13,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x46,0x61,0x64,0x65,0x6F,0x75,0x74,0x3A,0x0B, + 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1B, + 0x3E,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65, + 0x20,0x66,0x61,0x64,0x65,0x6F,0x75,0x74,0x20,0x73,0x70,0x65, + 0x65,0x64,0x2E,0x00,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x56,0x69,0x62,0x72,0x61,0x74,0x6F,0x20, + 0x73,0x77,0x65,0x65,0x70,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x3E,0x3E,0x54,0x68,0x69,0x73, + 0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x74,0x69,0x6D,0x65, + 0x20,0x28,0x69,0x6E,0x20,0x70,0x6C,0x61,0x79,0x65,0x72,0x20, + 0x74,0x69,0x63,0x6B,0x73,0x29,0x20,0x74,0x68,0x61,0x74,0x20, + 0x77,0x69,0x6C,0x6C,0x20,0x62,0x79,0x70,0x61,0x73,0x73,0x20, + 0x75,0x6E,0x74,0x69,0x6C,0x20,0x74,0x68,0x65,0x2D,0x61,0x75, + 0x74,0x6F,0x2D,0x76,0x69,0x62,0x72,0x61,0x74,0x6F,0x20,0x77, + 0x69,0x6C,0x6C,0x20,0x72,0x65,0x61,0x63,0x68,0x20,0x69,0x74, + 0x27,0x73,0x20,0x66,0x69,0x6E,0x61,0x6C,0x20,0x61,0x6D,0x70, + 0x6C,0x69,0x74,0x75,0x64,0x65,0x2E,0x00,0x1E,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x54,0x68,0x65,0x20, + 0x70,0x69,0x61,0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61, + 0x72,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x3F,0x3E,0x54,0x68,0x65,0x20,0x70,0x69,0x61, + 0x6E,0x6F,0x20,0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x20, + 0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x74,0x68,0x65,0x20, + 0x6B,0x65,0x79,0x20,0x73,0x70,0x6C,0x69,0x74,0x20,0x66,0x6F, + 0x72,0x20,0x61,0x6E,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D, + 0x65,0x6E,0x74,0x2E,0x20,0x54,0x6F,0x3F,0x63,0x68,0x61,0x6E, + 0x67,0x65,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x20,0x73, + 0x70,0x6C,0x69,0x74,0x2C,0x20,0x63,0x68,0x6F,0x6F,0x73,0x65, + 0x20,0x61,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77,0x69, + 0x74,0x68,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x69,0x6E,0x73, + 0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x61,0x6E,0x64,0x1C, + 0x74,0x68,0x65,0x6E,0x20,0x22,0x64,0x72,0x61,0x77,0x22,0x20, + 0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x6F, + 0x61,0x72,0x64,0x2E,0x42,0x3E,0x54,0x68,0x65,0x20,0x6E,0x6F, + 0x74,0x65,0x73,0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x77, + 0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72, + 0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, + 0x6E,0x74,0x20,0x61,0x72,0x65,0x20,0x69,0x6E,0x64,0x69,0x63, + 0x61,0x74,0x65,0x64,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x09, + 0x6B,0x65,0x79,0x62,0x6F,0x61,0x72,0x64,0x2E,0x00,0x1A,0x3E, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x49,0x6D, + 0x70,0x6F,0x72,0x74,0x61,0x6E,0x74,0x20,0x6E,0x6F,0x74,0x65, + 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, + 0x32,0x44,0x3E,0x54,0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D, + 0x65,0x2C,0x20,0x70,0x61,0x6E,0x6E,0x69,0x6E,0x67,0x2C,0x20, + 0x66,0x69,0x6E,0x65,0x74,0x75,0x6E,0x65,0x20,0x61,0x6E,0x64, + 0x20,0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F, + 0x74,0x65,0x20,0x69,0x73,0x20,0x64,0x65,0x66,0x69,0x6E,0x65, + 0x64,0x20,0x66,0x6F,0x72,0x20,0x45,0x41,0x43,0x48,0x41,0x53, + 0x41,0x4D,0x50,0x4C,0x45,0x20,0x69,0x6E,0x20,0x61,0x6E,0x20, + 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x20, + 0x41,0x6C,0x6C,0x20,0x6F,0x74,0x68,0x65,0x72,0x20,0x69,0x6E, + 0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F,0x6E,0x20,0x69,0x73, + 0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x64,0x20,0x66,0x6F,0x72, + 0x20,0x74,0x68,0x65,0x12,0x65,0x6E,0x74,0x69,0x72,0x65,0x20, + 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x2E,0x00, + 0x31,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x49, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x45,0x64, + 0x69,0x74,0x6F,0x72,0x20,0x45,0x78,0x74,0x65,0x6E,0x73,0x69, + 0x6F,0x6E,0x3A,0x20,0x28,0x49,0x2E,0x45,0x2E,0x45,0x78,0x74, + 0x2E,0x29,0x01,0x3E,0x10,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x4D,0x49,0x44,0x49,0x3A,0x0B,0x3E,0x40, + 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x28,0x3E,0x27, + 0x70,0x2E,0x27,0x20,0x73,0x74,0x61,0x6E,0x64,0x73,0x20,0x66, + 0x6F,0x72,0x20,0x22,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x22, + 0x20,0x28,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74, + 0x29,0x2E,0x40,0x3E,0x53,0x65,0x76,0x65,0x72,0x61,0x6C,0x20, + 0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x73,0x20, + 0x63,0x61,0x6E,0x20,0x68,0x61,0x76,0x65,0x20,0x74,0x68,0x65, + 0x20,0x73,0x61,0x6D,0x65,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D, + 0x69,0x74,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x62, + 0x75,0x74,0x20,0x77,0x69,0x74,0x68,0x33,0x64,0x69,0x66,0x66, 0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61, - 0x6D,0x73,0x20,0x61,0x72,0x65,0x20,0x75,0x73,0x65,0x64,0x2E, - 0x3E,0x44,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74,0x20,0x70, - 0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x63,0x61,0x6E,0x6E, - 0x6F,0x74,0x20,0x62,0x65,0x20,0x70,0x6C,0x61,0x79,0x65,0x64, - 0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65, - 0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x61,0x74,0x20, - 0x74,0x68,0x65,0x11,0x73,0x61,0x6D,0x65,0x20,0x74,0x69,0x6D, - 0x65,0x20,0x74,0x68,0x6F,0x75,0x67,0x68,0x2E,0x44,0x3E,0x49, - 0x66,0x20,0x79,0x6F,0x75,0x20,0x63,0x68,0x61,0x6E,0x67,0x65, - 0x20,0x74,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x2C, - 0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D, - 0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x62,0x65,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74, - 0x74,0x65,0x64,0x20,0x74,0x6F,0x1C,0x74,0x68,0x65,0x20,0x73, - 0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x69, - 0x6D,0x6D,0x65,0x64,0x69,0x61,0x74,0x65,0x6C,0x79,0x2E,0x3E, - 0x3E,0x53,0x6F,0x6D,0x65,0x20,0x73,0x79,0x6E,0x74,0x68,0x65, - 0x73,0x69,0x7A,0x65,0x72,0x73,0x20,0x74,0x72,0x61,0x6E,0x73, - 0x6D,0x69,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20, - 0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x69,0x6E,0x66,0x6F,0x72, - 0x6D,0x61,0x74,0x69,0x6F,0x6E,0x2E,0x20,0x49,0x66,0x20,0x74, - 0x68,0x65,0x43,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x69, - 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E, - 0x20,0x46,0x54,0x32,0x20,0x69,0x73,0x20,0x61,0x20,0x4D,0x49, - 0x44,0x49,0x2D,0x69,0x6E,0x73,0x74,0x72,0x2E,0x20,0x77,0x69, - 0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20, - 0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x20,0x61,0x73,0x3F,0x74, - 0x68,0x65,0x20,0x72,0x65,0x63,0x65,0x69,0x76,0x65,0x64,0x20, + 0x6D,0x73,0x2E,0x20,0x46,0x54,0x32,0x20,0x63,0x68,0x61,0x6E, + 0x67,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67, + 0x72,0x61,0x6D,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x43, + 0x4D,0x49,0x44,0x49,0x2D,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C, + 0x73,0x20,0x69,0x6E,0x73,0x74,0x61,0x6E,0x74,0x6C,0x79,0x20, + 0x64,0x75,0x72,0x69,0x6E,0x67,0x20,0x70,0x6C,0x61,0x79,0x20, + 0x69,0x66,0x20,0x64,0x69,0x66,0x66,0x65,0x72,0x65,0x6E,0x74, + 0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x73,0x20,0x61,0x72, + 0x65,0x20,0x75,0x73,0x65,0x64,0x2E,0x3E,0x44,0x69,0x66,0x66, + 0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61, + 0x6D,0x73,0x20,0x63,0x61,0x6E,0x6E,0x6F,0x74,0x20,0x62,0x65, + 0x20,0x70,0x6C,0x61,0x79,0x65,0x64,0x20,0x61,0x74,0x20,0x74, + 0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E, + 0x6E,0x65,0x6C,0x20,0x61,0x74,0x20,0x74,0x68,0x65,0x11,0x73, + 0x61,0x6D,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x74,0x68,0x6F, + 0x75,0x67,0x68,0x2E,0x44,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x74,0x68,0x69,0x73, + 0x20,0x76,0x61,0x6C,0x75,0x65,0x2C,0x20,0x74,0x68,0x65,0x20, + 0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x6E,0x75,0x6D,0x62, + 0x65,0x72,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65,0x20,0x74, + 0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x74,0x65,0x64,0x20,0x74, + 0x6F,0x1C,0x74,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x68,0x65, + 0x73,0x69,0x7A,0x65,0x72,0x20,0x69,0x6D,0x6D,0x65,0x64,0x69, + 0x61,0x74,0x65,0x6C,0x79,0x2E,0x3E,0x3E,0x53,0x6F,0x6D,0x65, + 0x20,0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72, + 0x73,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20,0x70, + 0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x68,0x61,0x6E,0x67, + 0x65,0x20,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74,0x69,0x6F, + 0x6E,0x2E,0x20,0x49,0x66,0x20,0x74,0x68,0x65,0x43,0x63,0x75, + 0x72,0x72,0x65,0x6E,0x74,0x20,0x69,0x6E,0x73,0x74,0x72,0x75, + 0x6D,0x65,0x6E,0x74,0x20,0x69,0x6E,0x20,0x46,0x54,0x32,0x20, + 0x69,0x73,0x20,0x61,0x20,0x4D,0x49,0x44,0x49,0x2D,0x69,0x6E, + 0x73,0x74,0x72,0x2E,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68, + 0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x63,0x68,0x61,0x6E,0x6E, + 0x65,0x6C,0x20,0x61,0x73,0x3F,0x74,0x68,0x65,0x20,0x72,0x65, + 0x63,0x65,0x69,0x76,0x65,0x64,0x20,0x70,0x72,0x6F,0x67,0x72, + 0x61,0x6D,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2C,0x20,0x69, + 0x74,0x27,0x73,0x20,0x4D,0x49,0x44,0x49,0x2D,0x70,0x72,0x6F, + 0x67,0x72,0x61,0x6D,0x20,0x77,0x69,0x6C,0x6C,0x20,0x62,0x65, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x2E,0x40,0x3E,0x49, + 0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x73,0x79,0x6E,0x74,0x68, + 0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x64,0x6F,0x65,0x73,0x6E, + 0x27,0x74,0x20,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x20, 0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x68,0x61,0x6E, - 0x67,0x65,0x2C,0x20,0x69,0x74,0x27,0x73,0x20,0x4D,0x49,0x44, - 0x49,0x2D,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x77,0x69, - 0x6C,0x6C,0x20,0x62,0x65,0x20,0x63,0x68,0x61,0x6E,0x67,0x65, - 0x64,0x2E,0x40,0x3E,0x49,0x66,0x20,0x79,0x6F,0x75,0x72,0x20, - 0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20, - 0x64,0x6F,0x65,0x73,0x6E,0x27,0x74,0x20,0x74,0x72,0x61,0x6E, - 0x73,0x6D,0x69,0x74,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D, - 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2C,0x20,0x74,0x68,0x65, - 0x72,0x65,0x27,0x73,0x20,0x6E,0x6F,0x3E,0x70,0x6F,0x69,0x6E, - 0x74,0x20,0x69,0x6E,0x20,0x63,0x68,0x61,0x6E,0x67,0x69,0x6E, - 0x67,0x20,0x69,0x74,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20, - 0x73,0x79,0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x2C, - 0x20,0x64,0x6F,0x20,0x69,0x74,0x20,0x69,0x6E,0x20,0x46,0x54, - 0x32,0x20,0x69,0x6E,0x73,0x74,0x65,0x61,0x64,0x2E,0x00,0x18, - 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x42, - 0x65,0x6E,0x64,0x65,0x72,0x20,0x72,0x61,0x6E,0x67,0x65,0x3A, - 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x38,0x3E,0x54,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65, - 0x20,0x64,0x65,0x66,0x69,0x6E,0x65,0x73,0x20,0x68,0x6F,0x77, - 0x20,0x6D,0x61,0x6E,0x79,0x20,0x6E,0x6F,0x74,0x65,0x73,0x20, - 0x74,0x68,0x65,0x20,0x69,0x6E,0x73,0x74,0x72,0x75,0x6D,0x65, - 0x6E,0x74,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x37,0x73,0x79, - 0x6E,0x74,0x68,0x65,0x73,0x69,0x7A,0x65,0x72,0x20,0x63,0x61, - 0x6E,0x20,0x62,0x65,0x20,0x70,0x69,0x74,0x63,0x68,0x62,0x65, - 0x6E,0x64,0x65,0x64,0x2E,0x20,0x46,0x54,0x32,0x20,0x75,0x73, - 0x65,0x73,0x20,0x74,0x68,0x69,0x73,0x20,0x76,0x61,0x6C,0x75, - 0x65,0x20,0x66,0x6F,0x72,0x37,0x74,0x72,0x61,0x6E,0x73,0x6D, - 0x69,0x74,0x74,0x69,0x6E,0x67,0x20,0x74,0x68,0x65,0x20,0x70, - 0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x20,0x75,0x70, - 0x2F,0x64,0x6F,0x77,0x6E,0x20,0x61,0x6E,0x64,0x20,0x74,0x6F, - 0x6E,0x65,0x2D,0x70,0x6F,0x72,0x74,0x61,0x6D,0x65,0x6E,0x74, - 0x6F,0x13,0x63,0x6F,0x6D,0x6D,0x61,0x6E,0x64,0x73,0x20,0x63, - 0x6F,0x72,0x72,0x65,0x63,0x74,0x6C,0x79,0x2E,0x45,0x3E,0x54, - 0x68,0x65,0x20,0x4D,0x49,0x44,0x49,0x2D,0x70,0x69,0x74,0x63, - 0x68,0x62,0x65,0x6E,0x64,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20, - 0x63,0x6F,0x72,0x72,0x65,0x63,0x74,0x6C,0x79,0x20,0x6F,0x6E, - 0x6C,0x79,0x20,0x77,0x69,0x74,0x68,0x20,0x6C,0x69,0x6E,0x65, - 0x61,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79, - 0x20,0x74,0x61,0x62,0x6C,0x65,0x2E,0x00,0x18,0x40,0x58,0x30, - 0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x61,0x6D,0x70,0x6C, - 0x65,0x20,0x45,0x64,0x69,0x74,0x6F,0x72,0x3A,0x01,0x3E,0x2C, - 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50, - 0x6C,0x61,0x79,0x20,0x28,0x57,0x61,0x76,0x65,0x20,0x66,0x6F, - 0x72,0x6D,0x2C,0x20,0x72,0x61,0x6E,0x67,0x65,0x2C,0x20,0x64, - 0x69,0x73,0x70,0x6C,0x61,0x79,0x29,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x3E,0x50,0x6C, - 0x61,0x79,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x72,0x72, - 0x65,0x6E,0x74,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x77, - 0x69,0x74,0x68,0x20,0x74,0x6F,0x6E,0x65,0x20,0x64,0x69,0x73, - 0x70,0x6C,0x61,0x79,0x20,0x61,0x62,0x6F,0x76,0x65,0x20,0x74, + 0x67,0x65,0x2C,0x20,0x74,0x68,0x65,0x72,0x65,0x27,0x73,0x20, + 0x6E,0x6F,0x3E,0x70,0x6F,0x69,0x6E,0x74,0x20,0x69,0x6E,0x20, + 0x63,0x68,0x61,0x6E,0x67,0x69,0x6E,0x67,0x20,0x69,0x74,0x20, + 0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x79,0x6E,0x74,0x68, + 0x65,0x73,0x69,0x7A,0x65,0x72,0x2C,0x20,0x64,0x6F,0x20,0x69, + 0x74,0x20,0x69,0x6E,0x20,0x46,0x54,0x32,0x20,0x69,0x6E,0x73, + 0x74,0x65,0x61,0x64,0x2E,0x00,0x18,0x3E,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x42,0x65,0x6E,0x64,0x65,0x72, + 0x20,0x72,0x61,0x6E,0x67,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30, + 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x38,0x3E,0x54,0x68,0x69, + 0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x64,0x65,0x66,0x69, + 0x6E,0x65,0x73,0x20,0x68,0x6F,0x77,0x20,0x6D,0x61,0x6E,0x79, + 0x20,0x6E,0x6F,0x74,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x69, + 0x6E,0x73,0x74,0x72,0x75,0x6D,0x65,0x6E,0x74,0x20,0x6F,0x6E, + 0x20,0x74,0x68,0x65,0x37,0x73,0x79,0x6E,0x74,0x68,0x65,0x73, + 0x69,0x7A,0x65,0x72,0x20,0x63,0x61,0x6E,0x20,0x62,0x65,0x20, + 0x70,0x69,0x74,0x63,0x68,0x62,0x65,0x6E,0x64,0x65,0x64,0x2E, + 0x20,0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x20,0x74,0x68, + 0x69,0x73,0x20,0x76,0x61,0x6C,0x75,0x65,0x20,0x66,0x6F,0x72, + 0x37,0x74,0x72,0x61,0x6E,0x73,0x6D,0x69,0x74,0x74,0x69,0x6E, + 0x67,0x20,0x74,0x68,0x65,0x20,0x70,0x6F,0x72,0x74,0x61,0x6D, + 0x65,0x6E,0x74,0x6F,0x20,0x75,0x70,0x2F,0x64,0x6F,0x77,0x6E, + 0x20,0x61,0x6E,0x64,0x20,0x74,0x6F,0x6E,0x65,0x2D,0x70,0x6F, + 0x72,0x74,0x61,0x6D,0x65,0x6E,0x74,0x6F,0x13,0x63,0x6F,0x6D, + 0x6D,0x61,0x6E,0x64,0x73,0x20,0x63,0x6F,0x72,0x72,0x65,0x63, + 0x74,0x6C,0x79,0x2E,0x45,0x3E,0x54,0x68,0x65,0x20,0x4D,0x49, + 0x44,0x49,0x2D,0x70,0x69,0x74,0x63,0x68,0x62,0x65,0x6E,0x64, + 0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x63,0x6F,0x72,0x72,0x65, + 0x63,0x74,0x6C,0x79,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x77,0x69, + 0x74,0x68,0x20,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x66,0x72, + 0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,0x6C, + 0x65,0x2E,0x00,0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30, + 0x30,0x31,0x53,0x61,0x6D,0x70,0x6C,0x65,0x20,0x45,0x64,0x69, + 0x74,0x6F,0x72,0x3A,0x01,0x3E,0x2B,0x3E,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x6C,0x61,0x79,0x20,0x28, + 0x57,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x2C,0x20,0x72,0x61, + 0x6E,0x67,0x65,0x2C,0x20,0x64,0x69,0x73,0x70,0x6C,0x61,0x79, + 0x29,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x42,0x3E,0x50,0x6C,0x61,0x79,0x73,0x20,0x74,0x68, + 0x65,0x20,0x63,0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x73,0x61, + 0x6D,0x70,0x6C,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68, + 0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x64,0x69,0x73,0x70,0x6C, + 0x61,0x79,0x65,0x64,0x20,0x61,0x62,0x6F,0x76,0x65,0x20,0x74, 0x68,0x65,0x20,0x22,0x73,0x74,0x6F,0x70,0x22,0x3D,0x62,0x75, 0x74,0x74,0x6F,0x6E,0x2E,0x20,0x4E,0x6F,0x74,0x65,0x20,0x74, 0x68,0x61,0x74,0x20,0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20, 0x69,0x73,0x20,0x74,0x61,0x6B,0x65,0x6E,0x20,0x74,0x6F,0x20, 0x74,0x68,0x65,0x20,0x70,0x61,0x72,0x74,0x69,0x63,0x75,0x6C, 0x61,0x72,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x27,0x73,0x0E, - 0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x74,0x6F,0x6E, + 0x72,0x65,0x6C,0x61,0x74,0x69,0x76,0x65,0x20,0x6E,0x6F,0x74, 0x65,0x2E,0x00,0x16,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, 0x30,0x30,0x31,0x53,0x61,0x76,0x65,0x20,0x72,0x61,0x6E,0x67, 0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, @@ -1689,360 +1710,542 @@ const uint8_t helpData[27385] = 0x61,0x6E,0x67,0x65,0x20,0x28,0x6F,0x72,0x20,0x74,0x68,0x65, 0x20,0x77,0x68,0x6F,0x6C,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C, 0x65,0x20,0x69,0x66,0x20,0x6E,0x6F,0x20,0x72,0x61,0x6E,0x67, - 0x65,0x20,0x69,0x73,0x20,0x73,0x65,0x74,0x29,0x2E,0x00,0x13, - 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43, - 0x6F,0x6E,0x76,0x65,0x72,0x74,0x3A,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x34,0x3E,0x43,0x6F,0x6E, - 0x76,0x65,0x72,0x74,0x73,0x20,0x74,0x68,0x65,0x20,0x65,0x6E, - 0x74,0x69,0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20, - 0x66,0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x73,0x69,0x67,0x6E, - 0x65,0x64,0x2F,0x75,0x6E,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E, - 0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, - 0x31,0x43,0x6F,0x6E,0x76,0x65,0x72,0x74,0x20,0x57,0x3A,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3F, - 0x53,0x77,0x61,0x70,0x73,0x20,0x74,0x68,0x65,0x20,0x62,0x79, - 0x74,0x65,0x20,0x6F,0x72,0x64,0x65,0x72,0x20,0x74,0x6F,0x2F, - 0x66,0x72,0x6F,0x6D,0x20,0x49,0x6E,0x74,0x65,0x6C,0x20,0x66, - 0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x4D,0x6F,0x74,0x6F,0x72, - 0x6F,0x6C,0x61,0x20,0x73,0x74,0x61,0x6E,0x64,0x61,0x72,0x64, - 0x20,0x6F,0x6E,0x12,0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69, - 0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x44,0x59, - 0x6F,0x75,0x27,0x6C,0x6C,0x20,0x6E,0x65,0x65,0x64,0x20,0x74, - 0x68,0x69,0x73,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E, - 0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x69,0x6D,0x70,0x6F, - 0x72,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x20,0x73,0x61, - 0x6D,0x70,0x6C,0x65,0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x4D, - 0x6F,0x74,0x6F,0x72,0x6F,0x6C,0x61,0x2D,0x62,0x79,0x74,0x65, - 0x2D,0x6F,0x72,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x28,0x66, - 0x2E,0x65,0x78,0x2E,0x20,0x4B,0x75,0x72,0x7A,0x77,0x65,0x69, - 0x6C,0x20,0x4B,0x32,0x30,0x30,0x30,0x20,0x73,0x61,0x6D,0x70, - 0x6C,0x65,0x73,0x2E,0x29,0x00,0x10,0x3E,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x63,0x68,0x6F,0x3A,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x1E, - 0x4F,0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20, - 0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73, - 0x61,0x6D,0x70,0x6C,0x65,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x78,0x20,0x44, - 0x43,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x3D,0x41,0x74,0x74,0x65,0x6D,0x70,0x74,0x73,0x20, - 0x74,0x6F,0x20,0x63,0x65,0x6E,0x74,0x65,0x72,0x20,0x61,0x20, - 0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x74,0x68,0x61,0x74,0x20, - 0x68,0x61,0x73,0x20,0x75,0x6E,0x77,0x61,0x6E,0x74,0x65,0x64, - 0x20,0x44,0x43,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x2F,0x62, - 0x69,0x61,0x73,0x2E,0x43,0x50,0x6C,0x65,0x61,0x73,0x65,0x20, - 0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x74, - 0x20,0x69,0x73,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61,0x20, - 0x63,0x72,0x75,0x64,0x65,0x20,0x61,0x6C,0x67,0x6F,0x72,0x69, - 0x74,0x68,0x6D,0x2C,0x20,0x73,0x6F,0x20,0x69,0x74,0x20,0x63, - 0x61,0x6E,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D,0x65,0x73, - 0x22,0x66,0x61,0x69,0x6C,0x20,0x64,0x65,0x70,0x65,0x6E,0x64, - 0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x73, - 0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x2E,0x00, - 0x14,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x52,0x65,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x4F,0x70, - 0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20,0x74,0x68, - 0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73,0x61,0x6D, - 0x70,0x6C,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61,0x6D, - 0x70,0x6C,0x65,0x27,0x73,0x20,0x72,0x65,0x6C,0x61,0x74,0x69, - 0x76,0x65,0x20,0x74,0x6F,0x6E,0x65,0x20,0x69,0x73,0x2C,0x63, - 0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20, - 0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20,0x74,0x6F,0x20,0x74, - 0x68,0x65,0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E, - 0x67,0x20,0x72,0x61,0x74,0x65,0x2E,0x00,0x16,0x3E,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x78,0x20, - 0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30, - 0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x35,0x3E,0x4D,0x69,0x78, - 0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63, - 0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20,0x64, - 0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74, - 0x6F,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63,0x65, - 0x2E,0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30, - 0x30,0x31,0x44,0x72,0x61,0x77,0x20,0x6D,0x6F,0x64,0x65,0x3A, + 0x65,0x20,0x69,0x73,0x20,0x73,0x65,0x74,0x29,0x2E,0x00,0x10, + 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53, + 0x69,0x67,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40, + 0x43,0x30,0x30,0x32,0x22,0x3E,0x43,0x6F,0x6E,0x76,0x65,0x72, + 0x74,0x73,0x20,0x62,0x65,0x74,0x77,0x65,0x65,0x6E,0x20,0x73, + 0x69,0x67,0x6E,0x65,0x64,0x2F,0x75,0x6E,0x73,0x69,0x67,0x6E, + 0x65,0x64,0x2E,0x00,0x1F,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x42,0x2E,0x20,0x73,0x77,0x61,0x70,0x20, + 0x28,0x62,0x79,0x74,0x65,0x20,0x73,0x77,0x61,0x70,0x29,0x3A, 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, - 0x40,0x42,0x79,0x20,0x70,0x72,0x65,0x73,0x73,0x69,0x6E,0x67, - 0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20,0x6D, - 0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x20, - 0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C, - 0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x2C,0x20,0x79,0x6F, - 0x75,0x20,0x63,0x61,0x6E,0x1D,0x64,0x72,0x61,0x77,0x20,0x79, - 0x6F,0x75,0x72,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D, - 0x73,0x20,0x6D,0x61,0x6E,0x75,0x61,0x6C,0x6C,0x79,0x2E,0x00, - 0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43, - 0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, - 0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, - 0x30,0x30,0x31,0x41,0x75,0x74,0x6F,0x20,0x73,0x61,0x76,0x65, + 0x3F,0x53,0x77,0x61,0x70,0x73,0x20,0x74,0x68,0x65,0x20,0x62, + 0x79,0x74,0x65,0x20,0x6F,0x72,0x64,0x65,0x72,0x20,0x74,0x6F, + 0x2F,0x66,0x72,0x6F,0x6D,0x20,0x49,0x6E,0x74,0x65,0x6C,0x20, + 0x66,0x72,0x6F,0x6D,0x2F,0x74,0x6F,0x20,0x4D,0x6F,0x74,0x6F, + 0x72,0x6F,0x6C,0x61,0x20,0x73,0x74,0x61,0x6E,0x64,0x61,0x72, + 0x64,0x20,0x6F,0x6E,0x12,0x74,0x68,0x65,0x20,0x65,0x6E,0x74, + 0x69,0x72,0x65,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x44, + 0x59,0x6F,0x75,0x27,0x6C,0x6C,0x20,0x6E,0x65,0x65,0x64,0x20, + 0x74,0x68,0x69,0x73,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F, + 0x6E,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x20,0x69,0x6D,0x70, + 0x6F,0x72,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x20,0x73, + 0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x77,0x69,0x74,0x68,0x20, + 0x4D,0x6F,0x74,0x6F,0x72,0x6F,0x6C,0x61,0x2D,0x62,0x79,0x74, + 0x65,0x2D,0x6F,0x72,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x28, + 0x66,0x2E,0x65,0x78,0x2E,0x20,0x4B,0x75,0x72,0x7A,0x77,0x65, + 0x69,0x6C,0x20,0x4B,0x32,0x30,0x30,0x30,0x20,0x73,0x61,0x6D, + 0x70,0x6C,0x65,0x73,0x2E,0x29,0x00,0x10,0x3E,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x45,0x63,0x68,0x6F,0x3A, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x1E,0x4F,0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E, + 0x20,0x74,0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20, + 0x73,0x61,0x6D,0x70,0x6C,0x65,0x2E,0x00,0x12,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x69,0x78,0x20, + 0x44,0x43,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x3D,0x41,0x74,0x74,0x65,0x6D,0x70,0x74,0x73, + 0x20,0x74,0x6F,0x20,0x63,0x65,0x6E,0x74,0x65,0x72,0x20,0x61, + 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x74,0x68,0x61,0x74, + 0x20,0x68,0x61,0x73,0x20,0x75,0x6E,0x77,0x61,0x6E,0x74,0x65, + 0x64,0x20,0x44,0x43,0x20,0x6F,0x66,0x66,0x73,0x65,0x74,0x2F, + 0x62,0x69,0x61,0x73,0x2E,0x43,0x50,0x6C,0x65,0x61,0x73,0x65, + 0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x69, + 0x74,0x20,0x69,0x73,0x20,0x75,0x73,0x69,0x6E,0x67,0x20,0x61, + 0x20,0x63,0x72,0x75,0x64,0x65,0x20,0x61,0x6C,0x67,0x6F,0x72, + 0x69,0x74,0x68,0x6D,0x2C,0x20,0x73,0x6F,0x20,0x69,0x74,0x20, + 0x63,0x61,0x6E,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D,0x65, + 0x73,0x22,0x66,0x61,0x69,0x6C,0x20,0x64,0x65,0x70,0x65,0x6E, + 0x64,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20, + 0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x64,0x61,0x74,0x61,0x2E, + 0x00,0x14,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, + 0x31,0x52,0x65,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E, + 0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3C,0x4F, + 0x70,0x65,0x72,0x61,0x74,0x65,0x73,0x20,0x6F,0x6E,0x20,0x74, + 0x68,0x65,0x20,0x65,0x6E,0x74,0x69,0x72,0x65,0x20,0x73,0x61, + 0x6D,0x70,0x6C,0x65,0x2E,0x20,0x54,0x68,0x65,0x20,0x73,0x61, + 0x6D,0x70,0x6C,0x65,0x27,0x73,0x20,0x72,0x65,0x6C,0x61,0x74, + 0x69,0x76,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x69,0x73,0x2C, + 0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x20,0x77,0x69,0x74,0x68, + 0x20,0x72,0x65,0x73,0x70,0x65,0x63,0x74,0x20,0x74,0x6F,0x20, + 0x74,0x68,0x65,0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69, + 0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x2E,0x00,0x16,0x3E,0x40, + 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x4D,0x69,0x78, + 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58, + 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x35,0x3E,0x4D,0x69, + 0x78,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72, + 0x63,0x65,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68,0x65,0x20, + 0x64,0x65,0x73,0x74,0x69,0x6E,0x61,0x74,0x69,0x6F,0x6E,0x20, + 0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x72,0x63, + 0x65,0x2E,0x00,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43, + 0x30,0x30,0x31,0x44,0x72,0x61,0x77,0x20,0x6D,0x6F,0x64,0x65, 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x43,0x49,0x66,0x20,0x74,0x68,0x65,0x20,0x61,0x75,0x74, - 0x6F,0x20,0x73,0x61,0x76,0x65,0x20,0x69,0x73,0x20,0x6F,0x6E, - 0x2C,0x20,0x46,0x54,0x32,0x20,0x77,0x69,0x6C,0x6C,0x20,0x75, - 0x70,0x64,0x61,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x63,0x6F, - 0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20, - 0x66,0x69,0x6C,0x65,0x20,0x77,0x68,0x65,0x6E,0x15,0x79,0x6F, - 0x75,0x20,0x65,0x78,0x69,0x74,0x20,0x74,0x68,0x65,0x20,0x70, - 0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x25,0x40,0x58,0x30, - 0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69, - 0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x49,0x2F, - 0x4F,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x3A,0x01,0x3E, - 0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31, - 0x49,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F, - 0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x35,0x53,0x65,0x6C,0x65,0x63,0x74,0x73,0x20,0x77, - 0x68,0x61,0x74,0x20,0x74,0x79,0x70,0x65,0x20,0x6F,0x66,0x20, - 0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E,0x67,0x20,0x69, - 0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E, - 0x20,0x74,0x6F,0x20,0x75,0x73,0x65,0x2E,0x45,0x22,0x4E,0x6F, - 0x6E,0x65,0x22,0x20,0x75,0x73,0x65,0x73,0x20,0x6E,0x6F,0x20, - 0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F, - 0x6E,0x20,0x28,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20,0x6E, - 0x65,0x69,0x67,0x68,0x62,0x6F,0x72,0x29,0x2C,0x20,0x77,0x68, - 0x69,0x63,0x68,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73, - 0x75,0x6C,0x74,0x20,0x69,0x6E,0x49,0x61,0x6C,0x69,0x61,0x73, - 0x69,0x6E,0x67,0x20,0x28,0x6E,0x6F,0x69,0x73,0x65,0x29,0x20, - 0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64, - 0x2E,0x20,0x22,0x4C,0x69,0x6E,0x65,0x61,0x72,0x22,0x20,0x69, - 0x73,0x20,0x77,0x68,0x61,0x74,0x20,0x72,0x65,0x61,0x6C,0x20, - 0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x2C,0x20,0x77,0x68, - 0x69,0x63,0x68,0x20,0x69,0x73,0x20,0x61,0x47,0x6D,0x65,0x64, - 0x69,0x6F,0x63,0x72,0x65,0x20,0x69,0x6E,0x74,0x65,0x72,0x70, - 0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x79,0x70,0x65, - 0x2E,0x20,0x22,0x57,0x69,0x6E,0x64,0x6F,0x77,0x65,0x64,0x2D, - 0x73,0x69,0x6E,0x63,0x22,0x20,0x69,0x73,0x20,0x74,0x68,0x65, - 0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x65,0x64, - 0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x48,0x66,0x6F,0x72, - 0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x73,0x74,0x20,0x61,0x75, - 0x64,0x69,0x6F,0x20,0x71,0x75,0x61,0x6C,0x69,0x74,0x79,0x2C, - 0x20,0x61,0x6C,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x69,0x74, - 0x20,0x6D,0x61,0x79,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69,0x6D, - 0x65,0x73,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x74,0x6F,0x6F, - 0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x65,0x64,0x2A,0x6F,0x6E, - 0x20,0x6C,0x6F,0x77,0x2D,0x71,0x75,0x61,0x6C,0x69,0x74,0x79, - 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x28,0x66,0x2E, - 0x65,0x78,0x2E,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x4D,0x4F, - 0x44,0x73,0x29,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30,0x34,0x30, - 0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x20, - 0x72,0x61,0x6D,0x70,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3B,0x45,0x6E,0x61, - 0x62,0x6C,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x61,0x6E,0x74, - 0x69,0x2D,0x63,0x6C,0x69,0x63,0x6B,0x20,0x73,0x79,0x73,0x74, - 0x65,0x6D,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x61,0x75, - 0x64,0x69,0x6F,0x20,0x6D,0x69,0x78,0x65,0x72,0x20,0x28,0x46, - 0x54,0x32,0x2E,0x30,0x38,0x2B,0x29,0x2E,0x3B,0x50,0x6C,0x65, - 0x61,0x73,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68,0x61, - 0x74,0x20,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20,0x46, - 0x54,0x32,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6C,0x6F,0x61, - 0x64,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6F,0x6E,0x66,0x69, - 0x67,0x20,0x65,0x6E,0x74,0x72,0x79,0x2C,0x0B,0x63,0x6C,0x6F, - 0x6E,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x2E,0x00,0x19,0x3E,0x40, - 0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x6D,0x70, - 0x6C,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3A,0x0B, - 0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x46, - 0x41,0x6D,0x70,0x6C,0x69,0x66,0x69,0x65,0x73,0x20,0x74,0x68, - 0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x77,0x68,0x65, - 0x6E,0x20,0x6D,0x69,0x78,0x69,0x6E,0x67,0x2E,0x20,0x49,0x66, - 0x20,0x79,0x6F,0x75,0x20,0x73,0x65,0x74,0x20,0x74,0x68,0x69, - 0x73,0x20,0x6F,0x6E,0x65,0x20,0x74,0x6F,0x6F,0x20,0x68,0x69, - 0x67,0x68,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x3A,0x67, - 0x65,0x74,0x20,0x64,0x69,0x73,0x74,0x6F,0x72,0x74,0x69,0x6F, - 0x6E,0x2E,0x20,0x33,0x32,0x58,0x20,0x65,0x71,0x75,0x61,0x6C, - 0x73,0x20,0x66,0x75,0x6C,0x6C,0x20,0x61,0x6D,0x70,0x6C,0x69, - 0x74,0x75,0x64,0x65,0x20,0x66,0x6F,0x72,0x20,0x6F,0x6E,0x65, - 0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x00,0x1B,0x3E, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46,0x72, - 0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62,0x6C, + 0x32,0x40,0x42,0x79,0x20,0x70,0x72,0x65,0x73,0x73,0x69,0x6E, + 0x67,0x20,0x74,0x68,0x65,0x20,0x72,0x69,0x67,0x68,0x74,0x20, + 0x6D,0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E, + 0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x70, + 0x6C,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x2C,0x20,0x79, + 0x6F,0x75,0x20,0x63,0x61,0x6E,0x1D,0x64,0x72,0x61,0x77,0x20, + 0x79,0x6F,0x75,0x72,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72, + 0x6D,0x73,0x20,0x6D,0x61,0x6E,0x75,0x61,0x6C,0x6C,0x79,0x2E, + 0x00,0x18,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31, + 0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F, + 0x6E,0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30,0x34,0x30,0x40, + 0x43,0x30,0x30,0x31,0x41,0x75,0x74,0x6F,0x20,0x73,0x61,0x76, 0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, - 0x30,0x32,0x40,0x54,0x68,0x65,0x20,0x6C,0x69,0x6E,0x65,0x61, - 0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20, - 0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x61,0x6B,0x65,0x73,0x20, - 0x61,0x6C,0x6C,0x20,0x70,0x69,0x74,0x63,0x68,0x20,0x62,0x65, - 0x6E,0x64,0x73,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20,0x63, - 0x6F,0x6E,0x73,0x74,0x61,0x6E,0x74,0x3F,0x73,0x70,0x65,0x65, - 0x64,0x2C,0x20,0x69,0x6E,0x64,0x65,0x70,0x65,0x6E,0x64,0x65, - 0x6E,0x74,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x75, - 0x72,0x72,0x65,0x6E,0x74,0x20,0x66,0x72,0x65,0x71,0x75,0x65, - 0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x20, - 0x73,0x77,0x69,0x74,0x63,0x68,0x20,0x74,0x68,0x69,0x73,0x41, - 0x6F,0x6E,0x65,0x2C,0x20,0x6F,0x6E,0x20,0x61,0x20,0x66,0x69, - 0x6E,0x69,0x73,0x68,0x65,0x64,0x20,0x73,0x6F,0x6E,0x67,0x2C, - 0x20,0x69,0x74,0x20,0x6D,0x69,0x67,0x68,0x74,0x20,0x73,0x6F, - 0x75,0x6E,0x64,0x20,0x73,0x74,0x72,0x61,0x6E,0x67,0x65,0x20, - 0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E,0x64, - 0x20,0x75,0x73,0x65,0x73,0x0D,0x70,0x6F,0x72,0x74,0x61,0x6D, - 0x65,0x6E,0x74,0x6F,0x65,0x73,0x2E,0x00,0x20,0x40,0x58,0x30, - 0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69, - 0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4C,0x61, - 0x79,0x6F,0x75,0x74,0x3A,0x01,0x3E,0x29,0x3E,0x40,0x58,0x30, - 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74,0x65, - 0x72,0x6E,0x20,0x6C,0x61,0x79,0x6F,0x75,0x74,0x2C,0x20,0x68, - 0x65,0x78,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x69,0x6E,0x67, - 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x41,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x75,0x73,0x65, - 0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x73,0x20,0x74,0x68, - 0x61,0x74,0x20,0x61,0x72,0x65,0x20,0x6C,0x6F,0x6E,0x67,0x65, - 0x72,0x20,0x74,0x68,0x61,0x6E,0x20,0x39,0x39,0x20,0x6C,0x69, - 0x6E,0x65,0x73,0x2C,0x20,0x79,0x6F,0x75,0x20,0x73,0x68,0x6F, - 0x75,0x6C,0x64,0x20,0x75,0x73,0x65,0x45,0x68,0x65,0x78,0x20, - 0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x20,0x73,0x69,0x6E, - 0x63,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72,0x65, - 0x20,0x6F,0x6E,0x6C,0x79,0x20,0x32,0x20,0x64,0x69,0x67,0x69, - 0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6C,0x69, - 0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x63,0x6F, - 0x6C,0x75,0x6D,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30,0x34, - 0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65,0x73, - 0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30, - 0x32,0x43,0x22,0x53,0x74,0x64,0x2E,0x22,0x20,0x28,0x73,0x74, - 0x61,0x6E,0x64,0x61,0x72,0x64,0x29,0x20,0x77,0x69,0x6C,0x6C, - 0x20,0x73,0x68,0x6F,0x77,0x20,0x74,0x68,0x65,0x20,0x73,0x61, - 0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,0x74,0x73,0x20, - 0x61,0x73,0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x28,0x6C, - 0x69,0x6B,0x65,0x20,0x46,0x54,0x32,0x29,0x2E,0x41,0x22,0x4C, - 0x69,0x6E,0x65,0x64,0x22,0x20,0x77,0x69,0x6C,0x6C,0x20,0x64, - 0x72,0x61,0x77,0x20,0x6C,0x69,0x6E,0x65,0x73,0x20,0x62,0x65, - 0x74,0x77,0x65,0x65,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x6F, - 0x69,0x6E,0x74,0x73,0x2C,0x20,0x6C,0x69,0x6B,0x65,0x20,0x61, - 0x6E,0x20,0x6F,0x73,0x63,0x69,0x6C,0x6C,0x6F,0x73,0x63,0x6F, - 0x70,0x65,0x2E,0x00,0x27,0x40,0x58,0x30,0x32,0x30,0x40,0x43, - 0x30,0x30,0x31,0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61, - 0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C, - 0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x3A,0x01,0x3E,0x15,0x3E, - 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x53, - 0x79,0x6E,0x63,0x20,0x6F,0x66,0x66,0x3A,0x0B,0x3E,0x40,0x58, - 0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3F,0x54,0x65,0x6C, - 0x6C,0x73,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72, - 0x61,0x6D,0x20,0x74,0x6F,0x20,0x6E,0x6F,0x74,0x20,0x75,0x73, - 0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x66,0x6F,0x72,0x20, - 0x76,0x69,0x64,0x65,0x6F,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F, - 0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73, - 0x40,0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74, - 0x65,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x36,0x30,0x48, - 0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x2C, - 0x20,0x74,0x68,0x65,0x6E,0x20,0x56,0x53,0x79,0x6E,0x63,0x20, - 0x69,0x73,0x20,0x61,0x6C,0x77,0x61,0x79,0x73,0x20,0x6F,0x66, - 0x66,0x20,0x66,0x6F,0x72,0x45,0x74,0x68,0x69,0x73,0x20,0x70, - 0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x20,0x4E,0x6F,0x74,0x20, - 0x68,0x61,0x76,0x69,0x6E,0x67,0x20,0x56,0x53,0x79,0x6E,0x63, - 0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74, - 0x20,0x69,0x6E,0x20,0x6C,0x65,0x73,0x73,0x20,0x69,0x6E,0x70, - 0x75,0x74,0x2F,0x76,0x69,0x64,0x65,0x6F,0x20,0x64,0x65,0x6C, - 0x61,0x79,0x2C,0x1E,0x62,0x75,0x74,0x20,0x61,0x6C,0x73,0x6F, - 0x20,0x70,0x6F,0x74,0x65,0x6E,0x74,0x69,0x61,0x6C,0x20,0x73, - 0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x2E,0x00,0x15, - 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53, - 0x74,0x72,0x65,0x74,0x63,0x68,0x65,0x64,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x4D,0x61, - 0x6B,0x65,0x73,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65, - 0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x20,0x63,0x6F,0x6D,0x70, - 0x6C,0x65,0x74,0x65,0x6C,0x79,0x20,0x73,0x74,0x72,0x65,0x74, - 0x63,0x68,0x20,0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x69, - 0x6D,0x61,0x67,0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x63, - 0x61,0x6E,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E, - 0x4E,0x61,0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x28,0x75, - 0x6E,0x65,0x76,0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20, - 0x77,0x69,0x64,0x74,0x68,0x73,0x29,0x20,0x69,0x66,0x20,0x74, - 0x68,0x65,0x20,0x61,0x73,0x70,0x65,0x63,0x74,0x20,0x72,0x61, - 0x74,0x69,0x6F,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73, - 0x63,0x72,0x65,0x65,0x6E,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74, - 0x20,0x31,0x36,0x3A,0x31,0x30,0x2E,0x52,0x54,0x68,0x65,0x20, + 0x30,0x32,0x43,0x49,0x66,0x20,0x74,0x68,0x65,0x20,0x61,0x75, + 0x74,0x6F,0x20,0x73,0x61,0x76,0x65,0x20,0x69,0x73,0x20,0x6F, + 0x6E,0x2C,0x20,0x46,0x54,0x32,0x20,0x77,0x69,0x6C,0x6C,0x20, + 0x75,0x70,0x64,0x61,0x74,0x65,0x20,0x74,0x68,0x65,0x20,0x63, + 0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, + 0x20,0x66,0x69,0x6C,0x65,0x20,0x77,0x68,0x65,0x6E,0x15,0x79, + 0x6F,0x75,0x20,0x65,0x78,0x69,0x74,0x20,0x74,0x68,0x65,0x20, + 0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x25,0x40,0x58, + 0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66, + 0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x49, + 0x2F,0x4F,0x20,0x64,0x65,0x76,0x69,0x63,0x65,0x73,0x3A,0x01, + 0x3E,0x19,0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30, + 0x31,0x49,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69, + 0x6F,0x6E,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x35,0x53,0x65,0x6C,0x65,0x63,0x74,0x73,0x20, + 0x77,0x68,0x61,0x74,0x20,0x74,0x79,0x70,0x65,0x20,0x6F,0x66, + 0x20,0x72,0x65,0x73,0x61,0x6D,0x70,0x6C,0x69,0x6E,0x67,0x20, + 0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F, + 0x6E,0x20,0x74,0x6F,0x20,0x75,0x73,0x65,0x2E,0x45,0x22,0x4E, + 0x6F,0x6E,0x65,0x22,0x20,0x75,0x73,0x65,0x73,0x20,0x6E,0x6F, + 0x20,0x69,0x6E,0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69, + 0x6F,0x6E,0x20,0x28,0x6E,0x65,0x61,0x72,0x65,0x73,0x74,0x20, + 0x6E,0x65,0x69,0x67,0x68,0x62,0x6F,0x72,0x29,0x2C,0x20,0x77, + 0x68,0x69,0x63,0x68,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65, + 0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x49,0x61,0x6C,0x69,0x61, + 0x73,0x69,0x6E,0x67,0x20,0x28,0x6E,0x6F,0x69,0x73,0x65,0x29, + 0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E, + 0x64,0x2E,0x20,0x22,0x4C,0x69,0x6E,0x65,0x61,0x72,0x22,0x20, + 0x69,0x73,0x20,0x77,0x68,0x61,0x74,0x20,0x72,0x65,0x61,0x6C, + 0x20,0x46,0x54,0x32,0x20,0x75,0x73,0x65,0x73,0x2C,0x20,0x77, + 0x68,0x69,0x63,0x68,0x20,0x69,0x73,0x20,0x61,0x47,0x6D,0x65, + 0x64,0x69,0x6F,0x63,0x72,0x65,0x20,0x69,0x6E,0x74,0x65,0x72, + 0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x20,0x74,0x79,0x70, + 0x65,0x2E,0x20,0x22,0x57,0x69,0x6E,0x64,0x6F,0x77,0x65,0x64, + 0x2D,0x73,0x69,0x6E,0x63,0x22,0x20,0x69,0x73,0x20,0x74,0x68, + 0x65,0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x65, + 0x64,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x48,0x66,0x6F, + 0x72,0x20,0x74,0x68,0x65,0x20,0x62,0x65,0x73,0x74,0x20,0x61, + 0x75,0x64,0x69,0x6F,0x20,0x71,0x75,0x61,0x6C,0x69,0x74,0x79, + 0x2C,0x20,0x61,0x6C,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x69, + 0x74,0x20,0x6D,0x61,0x79,0x20,0x73,0x6F,0x6D,0x65,0x74,0x69, + 0x6D,0x65,0x73,0x20,0x73,0x6F,0x75,0x6E,0x64,0x20,0x74,0x6F, + 0x6F,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x65,0x64,0x2A,0x6F, + 0x6E,0x20,0x6C,0x6F,0x77,0x2D,0x71,0x75,0x61,0x6C,0x69,0x74, + 0x79,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x73,0x20,0x28,0x66, + 0x2E,0x65,0x78,0x2E,0x20,0x41,0x6D,0x69,0x67,0x61,0x20,0x4D, + 0x4F,0x44,0x73,0x29,0x2E,0x00,0x1A,0x3E,0x40,0x58,0x30,0x34, + 0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x6F,0x6C,0x75,0x6D,0x65, + 0x20,0x72,0x61,0x6D,0x70,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40, + 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x3B,0x45,0x6E, + 0x61,0x62,0x6C,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x61,0x6E, + 0x74,0x69,0x2D,0x63,0x6C,0x69,0x63,0x6B,0x20,0x73,0x79,0x73, + 0x74,0x65,0x6D,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x61, + 0x75,0x64,0x69,0x6F,0x20,0x6D,0x69,0x78,0x65,0x72,0x20,0x28, + 0x46,0x54,0x32,0x2E,0x30,0x38,0x2B,0x29,0x2E,0x3B,0x50,0x6C, + 0x65,0x61,0x73,0x65,0x20,0x6E,0x6F,0x74,0x65,0x20,0x74,0x68, + 0x61,0x74,0x20,0x6F,0x72,0x69,0x67,0x69,0x6E,0x61,0x6C,0x20, + 0x46,0x54,0x32,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6C,0x6F, + 0x61,0x64,0x20,0x74,0x68,0x69,0x73,0x20,0x63,0x6F,0x6E,0x66, + 0x69,0x67,0x20,0x65,0x6E,0x74,0x72,0x79,0x2C,0x0B,0x63,0x6C, + 0x6F,0x6E,0x65,0x20,0x6F,0x6E,0x6C,0x79,0x2E,0x00,0x19,0x3E, + 0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x6D, + 0x70,0x6C,0x69,0x66,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x3A, + 0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32, + 0x46,0x41,0x6D,0x70,0x6C,0x69,0x66,0x69,0x65,0x73,0x20,0x74, + 0x68,0x65,0x20,0x76,0x6F,0x6C,0x75,0x6D,0x65,0x20,0x77,0x68, + 0x65,0x6E,0x20,0x6D,0x69,0x78,0x69,0x6E,0x67,0x2E,0x20,0x49, + 0x66,0x20,0x79,0x6F,0x75,0x20,0x73,0x65,0x74,0x20,0x74,0x68, + 0x69,0x73,0x20,0x6F,0x6E,0x65,0x20,0x74,0x6F,0x6F,0x20,0x68, + 0x69,0x67,0x68,0x2C,0x20,0x79,0x6F,0x75,0x27,0x6C,0x6C,0x3A, + 0x67,0x65,0x74,0x20,0x64,0x69,0x73,0x74,0x6F,0x72,0x74,0x69, + 0x6F,0x6E,0x2E,0x20,0x33,0x32,0x58,0x20,0x65,0x71,0x75,0x61, + 0x6C,0x73,0x20,0x66,0x75,0x6C,0x6C,0x20,0x61,0x6D,0x70,0x6C, + 0x69,0x74,0x75,0x64,0x65,0x20,0x66,0x6F,0x72,0x20,0x6F,0x6E, + 0x65,0x20,0x63,0x68,0x61,0x6E,0x6E,0x65,0x6C,0x2E,0x00,0x1B, + 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x46, + 0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x74,0x61,0x62, + 0x6C,0x65,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x40,0x54,0x68,0x65,0x20,0x6C,0x69,0x6E,0x65, + 0x61,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79, + 0x20,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x61,0x6B,0x65,0x73, + 0x20,0x61,0x6C,0x6C,0x20,0x70,0x69,0x74,0x63,0x68,0x20,0x62, + 0x65,0x6E,0x64,0x73,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20, + 0x63,0x6F,0x6E,0x73,0x74,0x61,0x6E,0x74,0x3F,0x73,0x70,0x65, + 0x65,0x64,0x2C,0x20,0x69,0x6E,0x64,0x65,0x70,0x65,0x6E,0x64, + 0x65,0x6E,0x74,0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x63, + 0x75,0x72,0x72,0x65,0x6E,0x74,0x20,0x66,0x72,0x65,0x71,0x75, + 0x65,0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75, + 0x20,0x73,0x77,0x69,0x74,0x63,0x68,0x20,0x74,0x68,0x69,0x73, + 0x41,0x6F,0x6E,0x65,0x2C,0x20,0x6F,0x6E,0x20,0x61,0x20,0x66, + 0x69,0x6E,0x69,0x73,0x68,0x65,0x64,0x20,0x73,0x6F,0x6E,0x67, + 0x2C,0x20,0x69,0x74,0x20,0x6D,0x69,0x67,0x68,0x74,0x20,0x73, + 0x6F,0x75,0x6E,0x64,0x20,0x73,0x74,0x72,0x61,0x6E,0x67,0x65, + 0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x6F,0x75,0x6E, + 0x64,0x20,0x75,0x73,0x65,0x73,0x0D,0x70,0x6F,0x72,0x74,0x61, + 0x6D,0x65,0x6E,0x74,0x6F,0x65,0x73,0x2E,0x00,0x20,0x40,0x58, + 0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x6E,0x66, + 0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x4C, + 0x61,0x79,0x6F,0x75,0x74,0x3A,0x01,0x3E,0x29,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x20,0x6C,0x61,0x79,0x6F,0x75,0x74,0x2C,0x20, + 0x68,0x65,0x78,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x69,0x6E, + 0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x41,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x75,0x73, + 0x65,0x20,0x70,0x61,0x74,0x74,0x65,0x72,0x6E,0x73,0x20,0x74, + 0x68,0x61,0x74,0x20,0x61,0x72,0x65,0x20,0x6C,0x6F,0x6E,0x67, + 0x65,0x72,0x20,0x74,0x68,0x61,0x6E,0x20,0x39,0x39,0x20,0x6C, + 0x69,0x6E,0x65,0x73,0x2C,0x20,0x79,0x6F,0x75,0x20,0x73,0x68, + 0x6F,0x75,0x6C,0x64,0x20,0x75,0x73,0x65,0x45,0x68,0x65,0x78, + 0x20,0x63,0x6F,0x75,0x6E,0x74,0x69,0x6E,0x67,0x20,0x73,0x69, + 0x6E,0x63,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72, + 0x65,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x32,0x20,0x64,0x69,0x67, + 0x69,0x74,0x73,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6C, + 0x69,0x6E,0x65,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x63, + 0x6F,0x6C,0x75,0x6D,0x6E,0x2E,0x00,0x12,0x3E,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x63,0x6F,0x70,0x65, + 0x73,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43,0x30, + 0x30,0x32,0x43,0x22,0x53,0x74,0x64,0x2E,0x22,0x20,0x28,0x73, + 0x74,0x61,0x6E,0x64,0x61,0x72,0x64,0x29,0x20,0x77,0x69,0x6C, + 0x6C,0x20,0x73,0x68,0x6F,0x77,0x20,0x74,0x68,0x65,0x20,0x73, + 0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E,0x74,0x73, + 0x20,0x61,0x73,0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x28, + 0x6C,0x69,0x6B,0x65,0x20,0x46,0x54,0x32,0x29,0x2E,0x3D,0x22, + 0x4C,0x69,0x6E,0x65,0x64,0x22,0x20,0x77,0x69,0x6C,0x6C,0x20, + 0x64,0x72,0x61,0x77,0x20,0x69,0x6E,0x74,0x65,0x72,0x70,0x6F, + 0x6C,0x61,0x74,0x65,0x64,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65, + 0x73,0x20,0x28,0x6C,0x69,0x6E,0x65,0x61,0x72,0x20,0x69,0x6E, + 0x74,0x65,0x72,0x70,0x6F,0x6C,0x61,0x74,0x69,0x6F,0x6E,0x2E, + 0x00,0x27,0x40,0x58,0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31, + 0x43,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F, + 0x6E,0x2C,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E, + 0x65,0x6F,0x75,0x73,0x3A,0x01,0x3E,0x15,0x3E,0x40,0x58,0x30, + 0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x56,0x53,0x79,0x6E,0x63, + 0x20,0x6F,0x66,0x66,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30, + 0x40,0x43,0x30,0x30,0x32,0x3F,0x54,0x65,0x6C,0x6C,0x73,0x20, + 0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20, + 0x74,0x6F,0x20,0x6E,0x6F,0x74,0x20,0x75,0x73,0x65,0x20,0x56, + 0x53,0x79,0x6E,0x63,0x20,0x66,0x6F,0x72,0x20,0x76,0x69,0x64, + 0x65,0x6F,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x72,0x20, + 0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x40,0x72,0x65, + 0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69, + 0x73,0x20,0x6E,0x6F,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x28, + 0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x2C,0x20,0x74,0x68, + 0x65,0x6E,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x69,0x73,0x20, + 0x61,0x6C,0x77,0x61,0x79,0x73,0x20,0x6F,0x66,0x66,0x20,0x66, + 0x6F,0x72,0x45,0x74,0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67, + 0x72,0x61,0x6D,0x2E,0x20,0x4E,0x6F,0x74,0x20,0x68,0x61,0x76, + 0x69,0x6E,0x67,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x77,0x69, + 0x6C,0x6C,0x20,0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E, + 0x20,0x6C,0x65,0x73,0x73,0x20,0x69,0x6E,0x70,0x75,0x74,0x2F, + 0x76,0x69,0x64,0x65,0x6F,0x20,0x64,0x65,0x6C,0x61,0x79,0x2C, + 0x1E,0x62,0x75,0x74,0x20,0x61,0x6C,0x73,0x6F,0x20,0x70,0x6F, + 0x74,0x65,0x6E,0x74,0x69,0x61,0x6C,0x20,0x73,0x74,0x75,0x74, + 0x74,0x65,0x72,0x69,0x6E,0x67,0x2E,0x00,0x15,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x53,0x74,0x72,0x65, + 0x74,0x63,0x68,0x65,0x64,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x4A,0x4D,0x61,0x6B,0x65,0x73, + 0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20, + 0x6D,0x6F,0x64,0x65,0x20,0x63,0x6F,0x6D,0x70,0x6C,0x65,0x74, + 0x65,0x6C,0x79,0x20,0x73,0x74,0x72,0x65,0x74,0x63,0x68,0x20, + 0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,0x67, + 0x65,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x63,0x61,0x6E,0x20, + 0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x4E,0x61,0x6C, + 0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x28,0x75,0x6E,0x65,0x76, + 0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77,0x69,0x64, + 0x74,0x68,0x73,0x29,0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20, + 0x61,0x73,0x70,0x65,0x63,0x74,0x20,0x72,0x61,0x74,0x69,0x6F, + 0x20,0x6F,0x66,0x20,0x74,0x68,0x65,0x20,0x73,0x63,0x72,0x65, + 0x65,0x6E,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x31,0x36, + 0x3A,0x31,0x30,0x2E,0x52,0x54,0x68,0x65,0x20,0x22,0x50,0x69, + 0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x22,0x20, + 0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x20,0x63,0x61,0x6E,0x20, + 0x68,0x65,0x6C,0x70,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x68, + 0x69,0x73,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x74,0x20,0x6D, + 0x61,0x6B,0x65,0x73,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61, + 0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72, + 0x72,0x79,0x2E,0x01,0x20,0x18,0x3E,0x40,0x58,0x30,0x34,0x30, + 0x40,0x43,0x30,0x30,0x31,0x50,0x69,0x78,0x65,0x6C,0x20,0x66, + 0x69,0x6C,0x74,0x65,0x72,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, + 0x30,0x40,0x43,0x30,0x30,0x32,0x52,0x41,0x70,0x70,0x6C,0x69, + 0x65,0x73,0x20,0x61,0x6E,0x20,0x61,0x6E,0x74,0x69,0x2D,0x61, + 0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x73,0x75,0x62,0x70, + 0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x20, + 0x74,0x68,0x61,0x74,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64, + 0x20,0x77,0x68,0x65,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69, + 0x6E,0x64,0x6F,0x77,0x20,0x69,0x73,0x20,0x75,0x70,0x73,0x63, + 0x61,0x6C,0x65,0x64,0x2E,0x3B,0x50,0x6C,0x65,0x61,0x73,0x65, + 0x20,0x6B,0x65,0x65,0x70,0x20,0x69,0x6E,0x20,0x6D,0x69,0x6E, + 0x64,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68,0x69,0x73,0x20, + 0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B,0x65,0x20,0x70,0x69, + 0x78,0x65,0x6C,0x73,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C, + 0x75,0x72,0x72,0x79,0x2E,0x00,0x23,0x40,0x58,0x30,0x32,0x30, + 0x40,0x43,0x30,0x30,0x31,0x41,0x64,0x76,0x61,0x6E,0x63,0x65, + 0x64,0x20,0x65,0x64,0x69,0x74,0x20,0x66,0x75,0x6E,0x63,0x74, + 0x69,0x6F,0x6E,0x73,0x3A,0x20,0x01,0x3E,0x1E,0x3E,0x40,0x58, + 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43,0x6F,0x70,0x79, + 0x2F,0x50,0x61,0x73,0x74,0x65,0x20,0x6D,0x61,0x73,0x6B,0x69, + 0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36,0x30,0x40,0x43, + 0x30,0x30,0x32,0x37,0x54,0x68,0x65,0x20,0x6D,0x61,0x73,0x6B, + 0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x75,0x73,0x65,0x64,0x20, + 0x66,0x6F,0x72,0x20,0x63,0x6F,0x70,0x79,0x69,0x6E,0x67,0x2F, + 0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x6C,0x79, + 0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66,0x20,0x61,0x46, + 0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C,0x6C,0x22,0x2E, + 0x20,0x54,0x68,0x65,0x20,0x64,0x69,0x66,0x66,0x65,0x72,0x65, + 0x6E,0x74,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66,0x20, + 0x61,0x20,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C,0x6C, + 0x22,0x20,0x69,0x73,0x20,0x4E,0x6F,0x74,0x65,0x2C,0x20,0x49, + 0x6E,0x73,0x74,0x72,0x2E,0x20,0x6E,0x72,0x2E,0x2C,0x20,0x56, + 0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20,0x45,0x66,0x66,0x65,0x63, + 0x74,0x20,0x6E,0x72,0x20,0x26,0x20,0x45,0x66,0x66,0x65,0x63, + 0x74,0x20,0x64,0x61,0x74,0x61,0x2E,0x34,0x3E,0x41,0x73,0x20, + 0x79,0x6F,0x75,0x20,0x63,0x61,0x6E,0x20,0x73,0x65,0x65,0x20, + 0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F, + 0x77,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61,0x72,0x65,0x20, + 0x33,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x73,0x20,0x6F,0x66, + 0x3D,0x22,0x65,0x6E,0x61,0x62,0x6C,0x65,0x2F,0x64,0x69,0x73, + 0x61,0x62,0x6C,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E,0x73, + 0x22,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x68,0x61,0x73,0x20, + 0x74,0x68,0x65,0x20,0x6C,0x65,0x74,0x74,0x65,0x72,0x73,0x20, + 0x43,0x2C,0x50,0x20,0x26,0x20,0x54,0x20,0x61,0x62,0x6F,0x76, + 0x65,0x2E,0x45,0x3E,0x43,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20, + 0x63,0x6F,0x70,0x79,0x2C,0x20,0x69,0x74,0x20,0x63,0x6F,0x6E, + 0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63,0x68,0x20, + 0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x67, + 0x6F,0x65,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x65, + 0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72,0x2E, + 0x3E,0x3E,0x50,0x20,0x6D,0x65,0x61,0x6E,0x73,0x20,0x70,0x61, + 0x73,0x74,0x65,0x20,0x61,0x6E,0x64,0x20,0x63,0x6F,0x6E,0x74, + 0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x70, + 0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x67,0x6F, + 0x65,0x73,0x20,0x6F,0x75,0x74,0x20,0x66,0x72,0x6F,0x6D,0x20, + 0x74,0x68,0x65,0x0B,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66, + 0x65,0x72,0x2E,0x45,0x3E,0x54,0x20,0x6D,0x65,0x61,0x6E,0x73, + 0x20,0x74,0x72,0x61,0x6E,0x73,0x70,0x61,0x72,0x65,0x6E,0x63, + 0x79,0x2E,0x20,0x49,0x66,0x20,0x69,0x74,0x27,0x73,0x20,0x65, + 0x6E,0x61,0x62,0x6C,0x65,0x64,0x2C,0x20,0x74,0x68,0x65,0x20, + 0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x64,0x6F,0x65,0x73, + 0x6E,0x27,0x74,0x20,0x6F,0x76,0x65,0x72,0x77,0x72,0x69,0x74, + 0x65,0x3D,0x64,0x61,0x74,0x61,0x20,0x77,0x69,0x74,0x68,0x20, + 0x6E,0x69,0x6C,0x2D,0x69,0x6E,0x66,0x6F,0x72,0x6D,0x61,0x74, + 0x69,0x6F,0x6E,0x2C,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x77,0x69, + 0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65,0x20,0x6F,0x72, + 0x20,0x61,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72,0x20,0x3C,0x3E, + 0x20,0x30,0x2E,0x01,0x3E,0x40,0x3E,0x54,0x68,0x65,0x20,0x63, + 0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73, + 0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69,0x6B,0x65,0x20, + 0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x77,0x69,0x74,0x68, + 0x20,0x7A,0x65,0x72,0x6F,0x2D,0x64,0x61,0x74,0x61,0x2E,0x20, + 0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61,0x6E,0x73,0x3B,0x74, + 0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x63,0x75,0x74,0x74, + 0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x63,0x6F,0x6E,0x74,0x72, + 0x6F,0x6C,0x6C,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x50, + 0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20,0x28,0x6F,0x72,0x20, + 0x54,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x29,0x2E,0x3C,0x3E, + 0x57,0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x20,0x63,0x6F,0x70, + 0x79,0x20,0x64,0x61,0x74,0x61,0x20,0x77,0x69,0x74,0x68,0x20, + 0x6D,0x61,0x73,0x6B,0x69,0x6E,0x67,0x2C,0x20,0x74,0x68,0x65, + 0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64,0x20,0x70,0x61, + 0x72,0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x6E,0x6F,0x74,0x43, + 0x63,0x6C,0x65,0x61,0x72,0x65,0x64,0x20,0x69,0x6E,0x20,0x74, + 0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65, + 0x72,0x2E,0x20,0x28,0x4D,0x61,0x6B,0x69,0x6E,0x67,0x20,0x69, + 0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C,0x65,0x20,0x74, + 0x6F,0x20,0x63,0x6F,0x6C,0x6C,0x65,0x63,0x74,0x20,0x64,0x61, + 0x74,0x61,0x20,0x66,0x72,0x6F,0x6D,0x27,0x73,0x65,0x76,0x65, + 0x72,0x61,0x6C,0x20,0x6C,0x6F,0x63,0x61,0x74,0x69,0x6F,0x6E, + 0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68,0x65,0x20,0x63, + 0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72,0x2E,0x29,0x00, + 0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x4C,0x3B,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, + 0x2A,0x2A,0x0E,0x40,0x4C,0x50,0x72,0x6F,0x62,0x6C,0x65,0x6D, + 0x73,0x2F,0x46,0x41,0x51,0x06,0x3E,0x40,0x58,0x30,0x32,0x30, + 0x2A,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48,0x6F, + 0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x74,0x6F,0x67,0x67, + 0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65, + 0x6E,0x20,0x6D,0x6F,0x64,0x65,0x3F,0x37,0x3E,0x40,0x43,0x30, + 0x30,0x32,0x41,0x3A,0x20,0x50,0x72,0x65,0x73,0x73,0x20,0x41, + 0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20,0x28,0x43,0x74, + 0x72,0x6C,0x2B,0x43,0x6D,0x64,0x2B,0x46,0x20,0x61,0x6C,0x73, + 0x6F,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6F,0x6E,0x20,0x4D, + 0x61,0x63,0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x45,0x3E, + 0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48,0x6F,0x77,0x20, + 0x63,0x61,0x6E,0x20,0x49,0x20,0x6D,0x61,0x6B,0x65,0x20,0x66, + 0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F, + 0x64,0x65,0x20,0x73,0x74,0x72,0x65,0x74,0x63,0x68,0x20,0x6F, + 0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x77,0x68,0x6F,0x6C,0x65, + 0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3F,0x37,0x3E,0x40,0x43, + 0x30,0x30,0x32,0x41,0x3A,0x20,0x45,0x6E,0x61,0x62,0x6C,0x65, + 0x20,0x22,0x53,0x74,0x72,0x65,0x74,0x63,0x68,0x65,0x64,0x22, + 0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D, + 0x3E,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65, + 0x6F,0x75,0x73,0x2E,0x4E,0x3E,0x40,0x58,0x30,0x33,0x35,0x54, + 0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x73, + 0x75,0x6C,0x74,0x20,0x69,0x6E,0x20,0x75,0x6E,0x65,0x76,0x65, + 0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77,0x69,0x64,0x74, + 0x68,0x73,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F,0x75,0x20,0x77, + 0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x66,0x69,0x78,0x20,0x74, + 0x68,0x69,0x73,0x2C,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x3D, 0x22,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74,0x65, - 0x72,0x22,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x20,0x63, - 0x61,0x6E,0x20,0x68,0x65,0x6C,0x70,0x20,0x77,0x69,0x74,0x68, - 0x20,0x74,0x68,0x69,0x73,0x2C,0x20,0x62,0x75,0x74,0x20,0x69, - 0x74,0x20,0x6D,0x61,0x6B,0x65,0x73,0x20,0x74,0x68,0x65,0x20, - 0x69,0x6D,0x61,0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62, - 0x6C,0x75,0x72,0x72,0x79,0x2E,0x01,0x20,0x18,0x3E,0x40,0x58, - 0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x50,0x69,0x78,0x65, - 0x6C,0x20,0x66,0x69,0x6C,0x74,0x65,0x72,0x3A,0x0B,0x3E,0x40, - 0x58,0x30,0x36,0x30,0x40,0x43,0x30,0x30,0x32,0x52,0x41,0x70, - 0x70,0x6C,0x69,0x65,0x73,0x20,0x61,0x6E,0x20,0x61,0x6E,0x74, - 0x69,0x2D,0x61,0x6C,0x69,0x61,0x73,0x69,0x6E,0x67,0x20,0x73, - 0x75,0x62,0x70,0x69,0x78,0x65,0x6C,0x20,0x66,0x69,0x6C,0x74, - 0x65,0x72,0x20,0x74,0x68,0x61,0x74,0x20,0x69,0x73,0x20,0x75, - 0x73,0x65,0x64,0x20,0x77,0x68,0x65,0x6E,0x20,0x74,0x68,0x65, - 0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x20,0x69,0x73,0x20,0x75, - 0x70,0x73,0x63,0x61,0x6C,0x65,0x64,0x2E,0x3B,0x50,0x6C,0x65, - 0x61,0x73,0x65,0x20,0x6B,0x65,0x65,0x70,0x20,0x69,0x6E,0x20, - 0x6D,0x69,0x6E,0x64,0x20,0x74,0x68,0x61,0x74,0x20,0x74,0x68, - 0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B,0x65, - 0x20,0x70,0x69,0x78,0x65,0x6C,0x73,0x20,0x6C,0x6F,0x6F,0x6B, - 0x20,0x62,0x6C,0x75,0x72,0x72,0x79,0x2E,0x00,0x23,0x40,0x58, - 0x30,0x32,0x30,0x40,0x43,0x30,0x30,0x31,0x41,0x64,0x76,0x61, - 0x6E,0x63,0x65,0x64,0x20,0x65,0x64,0x69,0x74,0x20,0x66,0x75, - 0x6E,0x63,0x74,0x69,0x6F,0x6E,0x73,0x3A,0x20,0x01,0x3E,0x1E, - 0x3E,0x40,0x58,0x30,0x34,0x30,0x40,0x43,0x30,0x30,0x31,0x43, - 0x6F,0x70,0x79,0x2F,0x50,0x61,0x73,0x74,0x65,0x20,0x6D,0x61, - 0x73,0x6B,0x69,0x6E,0x67,0x3A,0x0B,0x3E,0x40,0x58,0x30,0x36, - 0x30,0x40,0x43,0x30,0x30,0x32,0x37,0x54,0x68,0x65,0x20,0x6D, - 0x61,0x73,0x6B,0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x75,0x73, - 0x65,0x64,0x20,0x66,0x6F,0x72,0x20,0x63,0x6F,0x70,0x79,0x69, - 0x6E,0x67,0x2F,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x6F, - 0x6E,0x6C,0x79,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x6F,0x66, - 0x20,0x61,0x46,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63,0x65,0x6C, - 0x6C,0x22,0x2E,0x20,0x54,0x68,0x65,0x20,0x64,0x69,0x66,0x66, - 0x65,0x72,0x65,0x6E,0x74,0x20,0x70,0x61,0x72,0x74,0x73,0x20, - 0x6F,0x66,0x20,0x61,0x20,0x22,0x6E,0x6F,0x74,0x65,0x2D,0x63, - 0x65,0x6C,0x6C,0x22,0x20,0x69,0x73,0x20,0x4E,0x6F,0x74,0x65, - 0x2C,0x20,0x49,0x6E,0x73,0x74,0x72,0x2E,0x20,0x6E,0x72,0x2E, - 0x2C,0x20,0x56,0x6F,0x6C,0x75,0x6D,0x65,0x2C,0x20,0x45,0x66, - 0x66,0x65,0x63,0x74,0x20,0x6E,0x72,0x20,0x26,0x20,0x45,0x66, - 0x66,0x65,0x63,0x74,0x20,0x64,0x61,0x74,0x61,0x2E,0x34,0x3E, - 0x41,0x73,0x20,0x79,0x6F,0x75,0x20,0x63,0x61,0x6E,0x20,0x73, - 0x65,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x69, - 0x6E,0x64,0x6F,0x77,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x61, - 0x72,0x65,0x20,0x33,0x20,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x73, - 0x20,0x6F,0x66,0x3D,0x22,0x65,0x6E,0x61,0x62,0x6C,0x65,0x2F, - 0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x20,0x62,0x75,0x74,0x74, - 0x6F,0x6E,0x73,0x22,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x68, - 0x61,0x73,0x20,0x74,0x68,0x65,0x20,0x6C,0x65,0x74,0x74,0x65, - 0x72,0x73,0x20,0x43,0x2C,0x50,0x20,0x26,0x20,0x54,0x20,0x61, - 0x62,0x6F,0x76,0x65,0x2E,0x45,0x3E,0x43,0x20,0x6D,0x65,0x61, - 0x6E,0x73,0x20,0x63,0x6F,0x70,0x79,0x2C,0x20,0x69,0x74,0x20, - 0x63,0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69, - 0x63,0x68,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61, - 0x74,0x20,0x67,0x6F,0x65,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20, - 0x74,0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66, - 0x65,0x72,0x2E,0x3E,0x3E,0x50,0x20,0x6D,0x65,0x61,0x6E,0x73, - 0x20,0x70,0x61,0x73,0x74,0x65,0x20,0x61,0x6E,0x64,0x20,0x63, - 0x6F,0x6E,0x74,0x72,0x6F,0x6C,0x73,0x20,0x77,0x68,0x69,0x63, - 0x68,0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x74,0x68,0x61,0x74, - 0x20,0x67,0x6F,0x65,0x73,0x20,0x6F,0x75,0x74,0x20,0x66,0x72, - 0x6F,0x6D,0x20,0x74,0x68,0x65,0x0B,0x63,0x6F,0x70,0x79,0x62, - 0x75,0x66,0x66,0x65,0x72,0x2E,0x45,0x3E,0x54,0x20,0x6D,0x65, - 0x61,0x6E,0x73,0x20,0x74,0x72,0x61,0x6E,0x73,0x70,0x61,0x72, - 0x65,0x6E,0x63,0x79,0x2E,0x20,0x49,0x66,0x20,0x69,0x74,0x27, - 0x73,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x64,0x2C,0x20,0x74, - 0x68,0x65,0x20,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x64, - 0x6F,0x65,0x73,0x6E,0x27,0x74,0x20,0x6F,0x76,0x65,0x72,0x77, - 0x72,0x69,0x74,0x65,0x3D,0x64,0x61,0x74,0x61,0x20,0x77,0x69, - 0x74,0x68,0x20,0x6E,0x69,0x6C,0x2D,0x69,0x6E,0x66,0x6F,0x72, - 0x6D,0x61,0x74,0x69,0x6F,0x6E,0x2C,0x20,0x6F,0x6E,0x6C,0x79, - 0x20,0x77,0x69,0x74,0x68,0x20,0x61,0x20,0x6E,0x6F,0x74,0x65, - 0x20,0x6F,0x72,0x20,0x61,0x20,0x6E,0x75,0x6D,0x62,0x65,0x72, - 0x20,0x3C,0x3E,0x20,0x30,0x2E,0x01,0x3E,0x40,0x3E,0x54,0x68, - 0x65,0x20,0x63,0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69, - 0x6F,0x6E,0x73,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6C,0x69, - 0x6B,0x65,0x20,0x70,0x61,0x73,0x74,0x69,0x6E,0x67,0x20,0x77, - 0x69,0x74,0x68,0x20,0x7A,0x65,0x72,0x6F,0x2D,0x64,0x61,0x74, - 0x61,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x6D,0x65,0x61,0x6E, - 0x73,0x3B,0x74,0x68,0x61,0x74,0x20,0x74,0x68,0x65,0x20,0x63, - 0x75,0x74,0x74,0x69,0x6E,0x67,0x20,0x69,0x73,0x20,0x63,0x6F, - 0x6E,0x74,0x72,0x6F,0x6C,0x6C,0x65,0x64,0x20,0x77,0x69,0x74, - 0x68,0x20,0x50,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x20,0x28, - 0x6F,0x72,0x20,0x54,0x2D,0x63,0x6F,0x6C,0x75,0x6D,0x6E,0x29, - 0x2E,0x3C,0x3E,0x57,0x68,0x65,0x6E,0x20,0x79,0x6F,0x75,0x20, - 0x63,0x6F,0x70,0x79,0x20,0x64,0x61,0x74,0x61,0x20,0x77,0x69, - 0x74,0x68,0x20,0x6D,0x61,0x73,0x6B,0x69,0x6E,0x67,0x2C,0x20, - 0x74,0x68,0x65,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64, - 0x20,0x70,0x61,0x72,0x74,0x73,0x20,0x61,0x72,0x65,0x20,0x6E, - 0x6F,0x74,0x43,0x63,0x6C,0x65,0x61,0x72,0x65,0x64,0x20,0x69, - 0x6E,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75, - 0x66,0x66,0x65,0x72,0x2E,0x20,0x28,0x4D,0x61,0x6B,0x69,0x6E, - 0x67,0x20,0x69,0x74,0x20,0x70,0x6F,0x73,0x73,0x69,0x62,0x6C, - 0x65,0x20,0x74,0x6F,0x20,0x63,0x6F,0x6C,0x6C,0x65,0x63,0x74, - 0x20,0x64,0x61,0x74,0x61,0x20,0x66,0x72,0x6F,0x6D,0x27,0x73, - 0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x6C,0x6F,0x63,0x61,0x74, - 0x69,0x6F,0x6E,0x73,0x20,0x69,0x6E,0x74,0x6F,0x20,0x74,0x68, - 0x65,0x20,0x63,0x6F,0x70,0x79,0x62,0x75,0x66,0x66,0x65,0x72, - 0x2E,0x29,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A, + 0x72,0x22,0x20,0x28,0x74,0x68,0x6F,0x75,0x67,0x68,0x20,0x74, + 0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6D,0x61,0x6B, + 0x65,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61,0x67,0x65,0x20, + 0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72,0x72,0x79,0x29, + 0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x27,0x3E,0x40,0x43, + 0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x63,0x61,0x6E,0x27, + 0x74,0x20,0x75,0x73,0x65,0x20,0x41,0x6C,0x74,0x2B,0x46,0x34, + 0x20,0x61,0x6E,0x64,0x20,0x41,0x6C,0x74,0x2B,0x46,0x35,0x21, + 0x4E,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x57,0x69, + 0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x49,0x66,0x20,0x79,0x6F, + 0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x47,0x65,0x46,0x6F,0x72, + 0x63,0x65,0x20,0x45,0x78,0x70,0x65,0x72,0x69,0x65,0x6E,0x63, + 0x65,0x20,0x69,0x6E,0x73,0x74,0x61,0x6C,0x6C,0x65,0x64,0x2C, + 0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64,0x20,0x74,0x6F, + 0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2B,0x3E,0x40,0x58,0x30, + 0x33,0x35,0x74,0x68,0x65,0x20,0x6B,0x65,0x79,0x62,0x69,0x6E, + 0x64,0x69,0x6E,0x67,0x73,0x20,0x69,0x6E,0x20,0x69,0x74,0x73, + 0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x20,0x70,0x61, + 0x67,0x65,0x2E,0x57,0x3E,0x6D,0x61,0x63,0x4F,0x53,0x2F,0x4F, + 0x53,0x20,0x58,0x3A,0x20,0x43,0x68,0x61,0x6E,0x67,0x65,0x20, + 0x41,0x6C,0x74,0x2B,0x46,0x34,0x2F,0x41,0x6C,0x74,0x2B,0x46, + 0x35,0x20,0x6B,0x65,0x79,0x73,0x20,0x69,0x6E,0x20,0x74,0x68, + 0x65,0x20,0x4F,0x53,0x20,0x74,0x6F,0x20,0x73,0x6F,0x6D,0x65, + 0x74,0x68,0x69,0x6E,0x67,0x20,0x65,0x6C,0x73,0x65,0x2E,0x20, + 0x41,0x6C,0x73,0x6F,0x20,0x66,0x6F,0x72,0x20,0x47,0x4E,0x55, + 0x2F,0x4C,0x69,0x6E,0x75,0x78,0x2E,0x06,0x3E,0x40,0x58,0x30, + 0x32,0x30,0x2B,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20, + 0x54,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x63,0x75, + 0x72,0x73,0x6F,0x72,0x20,0x69,0x73,0x20,0x64,0x65,0x6C,0x61, + 0x79,0x65,0x64,0x2F,0x6C,0x61,0x67,0x67,0x79,0x21,0x44,0x3E, + 0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4D,0x61,0x6B,0x65, + 0x20,0x73,0x75,0x72,0x65,0x20,0x22,0x53,0x6F,0x66,0x74,0x77, + 0x61,0x72,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x22,0x20,0x69, + 0x73,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65,0x64,0x20,0x69, + 0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20, + 0x4C,0x61,0x79,0x6F,0x75,0x74,0x2E,0x4B,0x3E,0x40,0x58,0x30, + 0x33,0x35,0x41,0x6C,0x74,0x65,0x72,0x6E,0x61,0x74,0x69,0x76, + 0x65,0x6C,0x79,0x2C,0x20,0x79,0x6F,0x75,0x20,0x63,0x61,0x6E, + 0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x20,0x22,0x56,0x53,0x79, + 0x6E,0x63,0x20,0x6F,0x66,0x66,0x22,0x20,0x69,0x6E,0x20,0x43, + 0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20,0x4D,0x69,0x73, + 0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x2E,0x46, + 0x3E,0x54,0x68,0x69,0x73,0x20,0x68,0x6F,0x77,0x65,0x76,0x65, + 0x72,0x2C,0x20,0x77,0x69,0x6C,0x6C,0x20,0x69,0x6E,0x74,0x72, + 0x6F,0x64,0x75,0x63,0x65,0x20,0x73,0x74,0x75,0x74,0x74,0x65, + 0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65, + 0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x69, + 0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73,0x22,0x3E, + 0x6E,0x6F,0x74,0x20,0x65,0x78,0x61,0x63,0x74,0x20,0x74,0x6F, + 0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F, + 0x72,0x27,0x73,0x20,0x72,0x61,0x74,0x65,0x2E,0x06,0x3E,0x40, + 0x58,0x30,0x32,0x30,0x33,0x3E,0x40,0x43,0x30,0x30,0x31,0x51, + 0x3A,0x20,0x57,0x69,0x6C,0x6C,0x20,0x79,0x6F,0x75,0x20,0x69, + 0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20,0x4D,0x49,0x44, + 0x49,0x20,0x6F,0x75,0x74,0x20,0x66,0x75,0x6E,0x63,0x74,0x69, + 0x6F,0x6E,0x61,0x6C,0x69,0x74,0x79,0x3F,0x4D,0x3E,0x40,0x43, + 0x30,0x30,0x32,0x41,0x3A,0x20,0x4E,0x6F,0x2C,0x20,0x73,0x6F, + 0x72,0x72,0x79,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73, + 0x20,0x76,0x65,0x72,0x79,0x20,0x64,0x69,0x66,0x66,0x69,0x63, + 0x75,0x6C,0x74,0x20,0x74,0x6F,0x20,0x69,0x6D,0x70,0x6C,0x65, + 0x6D,0x65,0x6E,0x74,0x20,0x63,0x6F,0x72,0x72,0x65,0x63,0x74, + 0x6C,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x68,0x61,0x76,0x69, + 0x6E,0x67,0x3C,0x3E,0x40,0x58,0x30,0x33,0x35,0x68,0x69,0x67, + 0x68,0x65,0x72,0x20,0x61,0x75,0x64,0x69,0x6F,0x20,0x62,0x75, + 0x66,0x66,0x65,0x72,0x20,0x73,0x69,0x7A,0x65,0x73,0x20,0x28, + 0x62,0x75,0x66,0x66,0x65,0x72,0x65,0x64,0x20,0x72,0x65,0x70, + 0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63,0x6B,0x73,0x29, + 0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x30,0x3E, + 0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x57,0x68,0x65,0x72, + 0x65,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E, + 0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66, + 0x69,0x6C,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x3F,0x3F, + 0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x57,0x69,0x6E, + 0x64,0x6F,0x77,0x73,0x3A,0x20,0x5C,0x55,0x73,0x65,0x72,0x73, + 0x5C,0x55,0x53,0x45,0x52,0x5C,0x41,0x70,0x70,0x44,0x61,0x74, + 0x61,0x5C,0x52,0x6F,0x61,0x6D,0x69,0x6E,0x67,0x5C,0x46,0x54, + 0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x5C,0x46,0x54,0x32,0x2E, + 0x43,0x46,0x47,0x45,0x3E,0x40,0x58,0x30,0x33,0x35,0x4F,0x53, + 0x20,0x58,0x3A,0x20,0x2F,0x55,0x73,0x65,0x72,0x73,0x2F,0x55, + 0x53,0x45,0x52,0x2F,0x4C,0x69,0x62,0x72,0x61,0x72,0x79,0x2F, + 0x41,0x70,0x70,0x6C,0x69,0x63,0x61,0x74,0x69,0x6F,0x6E,0x20, + 0x53,0x75,0x70,0x70,0x6F,0x72,0x74,0x2F,0x46,0x54,0x32,0x20, + 0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,0x2E,0x43,0x46, + 0x47,0x2F,0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E,0x75,0x78,0x3A, + 0x20,0x2F,0x68,0x6F,0x6D,0x65,0x2F,0x55,0x53,0x45,0x52,0x2F, + 0x2E,0x63,0x6F,0x6E,0x66,0x69,0x67,0x2F,0x46,0x54,0x32,0x20, + 0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32,0x2E,0x43,0x46, + 0x47,0x01,0x3E,0x48,0x49,0x74,0x20,0x77,0x69,0x6C,0x6C,0x20, + 0x62,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64,0x20,0x69,0x6E, + 0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D, + 0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x69, + 0x66,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74,0x68,0x20,0x63, + 0x6F,0x75,0x6C,0x64,0x6E,0x27,0x74,0x20,0x62,0x65,0x20,0x75, + 0x73,0x65,0x64,0x2E,0x4D,0x49,0x66,0x20,0x79,0x6F,0x75,0x20, + 0x70,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x63,0x6F,0x6E,0x66, + 0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x69, + 0x6C,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x72, + 0x6F,0x67,0x72,0x61,0x6D,0x20,0x64,0x69,0x72,0x65,0x63,0x74, + 0x6F,0x72,0x79,0x2C,0x20,0x69,0x74,0x20,0x77,0x69,0x6C,0x6C, + 0x20,0x72,0x65,0x61,0x64,0x20,0x74,0x68,0x61,0x74,0x4A,0x6F, + 0x6E,0x65,0x20,0x61,0x6E,0x64,0x20,0x6E,0x6F,0x74,0x20,0x61, + 0x74,0x74,0x65,0x6D,0x70,0x74,0x20,0x74,0x6F,0x20,0x63,0x72, + 0x65,0x61,0x74,0x65,0x20,0x63,0x6F,0x6E,0x66,0x69,0x67,0x20, + 0x64,0x69,0x72,0x73,0x20,0x66,0x6F,0x72,0x20,0x74,0x68,0x65, + 0x20,0x4F,0x53,0x20,0x75,0x73,0x65,0x72,0x2E,0x20,0x28,0x70, + 0x6F,0x72,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D,0x6F,0x64,0x65, + 0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x42,0x3E,0x40,0x43, + 0x30,0x30,0x31,0x51,0x3A,0x20,0x43,0x61,0x6E,0x20,0x74,0x68, + 0x65,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20,0x72,0x65,0x61,0x64, + 0x20,0x46,0x54,0x32,0x2E,0x43,0x46,0x47,0x20,0x66,0x72,0x6F, + 0x6D,0x20,0x72,0x65,0x61,0x6C,0x20,0x46,0x54,0x32,0x2C,0x20, + 0x61,0x6E,0x64,0x20,0x76,0x69,0x63,0x65,0x20,0x76,0x65,0x72, + 0x73,0x61,0x3F,0x4C,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A, + 0x20,0x59,0x65,0x73,0x2C,0x20,0x69,0x74,0x20,0x73,0x68,0x6F, + 0x75,0x6C,0x64,0x20,0x77,0x6F,0x72,0x6B,0x20,0x6A,0x75,0x73, + 0x74,0x20,0x66,0x69,0x6E,0x65,0x2E,0x20,0x50,0x75,0x74,0x20, + 0x69,0x74,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x64,0x69, + 0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x73,0x68,0x6F,0x77, + 0x6E,0x20,0x61,0x62,0x6F,0x76,0x65,0x2E,0x06,0x3E,0x40,0x58, + 0x30,0x32,0x30,0x52,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A, + 0x20,0x53,0x6D,0x70,0x2E,0x20,0x45,0x64,0x2E,0x3A,0x20,0x57, + 0x68,0x69,0x6C,0x65,0x20,0x7A,0x6F,0x6F,0x6D,0x69,0x6E,0x67, + 0x20,0x69,0x6E,0x2C,0x20,0x49,0x20,0x73,0x6F,0x6D,0x65,0x74, + 0x69,0x6D,0x65,0x73,0x20,0x63,0x61,0x6E,0x27,0x74,0x20,0x6D, + 0x61,0x72,0x6B,0x20,0x74,0x68,0x65,0x20,0x6C,0x61,0x73,0x74, + 0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70,0x6F,0x69,0x6E, + 0x74,0x21,0x47,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20, + 0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x6E,0x6F,0x72,0x6D, + 0x61,0x6C,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20, + 0x61,0x20,0x6C,0x69,0x6D,0x69,0x74,0x61,0x74,0x69,0x6F,0x6E, + 0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6E,0x61,0x74,0x75, + 0x72,0x65,0x20,0x6F,0x66,0x20,0x73,0x63,0x61,0x6C,0x69,0x6E, + 0x67,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x17,0x3E,0x40, + 0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x66,0x6F,0x75, + 0x6E,0x64,0x20,0x61,0x20,0x62,0x75,0x67,0x21,0x4C,0x3E,0x40, + 0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x6C,0x65,0x61,0x73, + 0x65,0x20,0x73,0x65,0x6E,0x64,0x20,0x6D,0x65,0x20,0x61,0x20, + 0x6D,0x61,0x69,0x6C,0x20,0x28,0x66,0x6F,0x75,0x6E,0x64,0x20, + 0x61,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74,0x73,0x2E,0x6F, + 0x72,0x67,0x29,0x20,0x61,0x6E,0x64,0x20,0x74,0x72,0x79,0x20, + 0x74,0x6F,0x20,0x65,0x78,0x70,0x6C,0x61,0x69,0x6E,0x20,0x69, + 0x74,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, @@ -2055,241 +2258,57 @@ const uint8_t helpData[27385] = 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x0E,0x40,0x4C,0x50,0x72,0x6F,0x62, - 0x6C,0x65,0x6D,0x73,0x2F,0x46,0x41,0x51,0x06,0x3E,0x40,0x58, - 0x30,0x32,0x30,0x2A,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A, - 0x20,0x48,0x6F,0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x74, - 0x6F,0x67,0x67,0x6C,0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63, - 0x72,0x65,0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x3F,0x37,0x3E, - 0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x72,0x65,0x73, - 0x73,0x20,0x41,0x6C,0x74,0x2B,0x45,0x6E,0x74,0x65,0x72,0x20, - 0x28,0x43,0x74,0x72,0x6C,0x2B,0x43,0x6D,0x64,0x2B,0x46,0x20, - 0x61,0x6C,0x73,0x6F,0x20,0x77,0x6F,0x72,0x6B,0x73,0x20,0x6F, - 0x6E,0x20,0x4D,0x61,0x63,0x29,0x06,0x3E,0x40,0x58,0x30,0x32, - 0x30,0x45,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x48, - 0x6F,0x77,0x20,0x63,0x61,0x6E,0x20,0x49,0x20,0x6D,0x61,0x6B, - 0x65,0x20,0x66,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E, - 0x20,0x6D,0x6F,0x64,0x65,0x20,0x73,0x74,0x72,0x65,0x74,0x63, - 0x68,0x20,0x6F,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x77,0x68, - 0x6F,0x6C,0x65,0x20,0x73,0x63,0x72,0x65,0x65,0x6E,0x3F,0x37, - 0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x45,0x6E,0x61, - 0x62,0x6C,0x65,0x20,0x22,0x53,0x74,0x72,0x65,0x74,0x63,0x68, - 0x65,0x64,0x22,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69, - 0x67,0x20,0x2D,0x3E,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C, - 0x61,0x6E,0x65,0x6F,0x75,0x73,0x2E,0x4E,0x3E,0x40,0x58,0x30, - 0x33,0x35,0x54,0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20, - 0x72,0x65,0x73,0x75,0x6C,0x74,0x20,0x69,0x6E,0x20,0x75,0x6E, - 0x65,0x76,0x65,0x6E,0x20,0x70,0x69,0x78,0x65,0x6C,0x20,0x77, - 0x69,0x64,0x74,0x68,0x73,0x2E,0x20,0x49,0x66,0x20,0x79,0x6F, - 0x75,0x20,0x77,0x61,0x6E,0x74,0x20,0x74,0x6F,0x20,0x66,0x69, - 0x78,0x20,0x74,0x68,0x69,0x73,0x2C,0x20,0x65,0x6E,0x61,0x62, - 0x6C,0x65,0x3D,0x22,0x50,0x69,0x78,0x65,0x6C,0x20,0x66,0x69, - 0x6C,0x74,0x65,0x72,0x22,0x20,0x28,0x74,0x68,0x6F,0x75,0x67, - 0x68,0x20,0x74,0x68,0x69,0x73,0x20,0x77,0x69,0x6C,0x6C,0x20, - 0x6D,0x61,0x6B,0x65,0x20,0x74,0x68,0x65,0x20,0x69,0x6D,0x61, - 0x67,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x20,0x62,0x6C,0x75,0x72, - 0x72,0x79,0x29,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x27, - 0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20,0x63, - 0x61,0x6E,0x27,0x74,0x20,0x75,0x73,0x65,0x20,0x41,0x6C,0x74, - 0x2B,0x46,0x34,0x20,0x61,0x6E,0x64,0x20,0x41,0x6C,0x74,0x2B, - 0x46,0x35,0x21,0x4E,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A, - 0x20,0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x49,0x66, - 0x20,0x79,0x6F,0x75,0x20,0x68,0x61,0x76,0x65,0x20,0x47,0x65, - 0x46,0x6F,0x72,0x63,0x65,0x20,0x45,0x78,0x70,0x65,0x72,0x69, - 0x65,0x6E,0x63,0x65,0x20,0x69,0x6E,0x73,0x74,0x61,0x6C,0x6C, - 0x65,0x64,0x2C,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64, - 0x20,0x74,0x6F,0x20,0x63,0x68,0x61,0x6E,0x67,0x65,0x2B,0x3E, - 0x40,0x58,0x30,0x33,0x35,0x74,0x68,0x65,0x20,0x6B,0x65,0x79, - 0x62,0x69,0x6E,0x64,0x69,0x6E,0x67,0x73,0x20,0x69,0x6E,0x20, - 0x69,0x74,0x73,0x20,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73, - 0x20,0x70,0x61,0x67,0x65,0x2E,0x57,0x3E,0x6D,0x61,0x63,0x4F, - 0x53,0x2F,0x4F,0x53,0x20,0x58,0x3A,0x20,0x43,0x68,0x61,0x6E, - 0x67,0x65,0x20,0x41,0x6C,0x74,0x2B,0x46,0x34,0x2F,0x41,0x6C, - 0x74,0x2B,0x46,0x35,0x20,0x6B,0x65,0x79,0x73,0x20,0x69,0x6E, - 0x20,0x74,0x68,0x65,0x20,0x4F,0x53,0x20,0x74,0x6F,0x20,0x73, - 0x6F,0x6D,0x65,0x74,0x68,0x69,0x6E,0x67,0x20,0x65,0x6C,0x73, - 0x65,0x2E,0x20,0x41,0x6C,0x73,0x6F,0x20,0x66,0x6F,0x72,0x20, - 0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E,0x75,0x78,0x2E,0x06,0x3E, - 0x40,0x58,0x30,0x32,0x30,0x2B,0x3E,0x40,0x43,0x30,0x30,0x31, - 0x51,0x3A,0x20,0x54,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65, - 0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x20,0x69,0x73,0x20,0x64, - 0x65,0x6C,0x61,0x79,0x65,0x64,0x2F,0x6C,0x61,0x67,0x67,0x79, - 0x21,0x44,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4D, - 0x61,0x6B,0x65,0x20,0x73,0x75,0x72,0x65,0x20,0x22,0x53,0x6F, - 0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65, - 0x22,0x20,0x69,0x73,0x20,0x64,0x69,0x73,0x61,0x62,0x6C,0x65, - 0x64,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20, - 0x2D,0x3E,0x20,0x4C,0x61,0x79,0x6F,0x75,0x74,0x2E,0x4B,0x3E, - 0x40,0x58,0x30,0x33,0x35,0x41,0x6C,0x74,0x65,0x72,0x6E,0x61, - 0x74,0x69,0x76,0x65,0x6C,0x79,0x2C,0x20,0x79,0x6F,0x75,0x20, - 0x63,0x61,0x6E,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x20,0x22, - 0x56,0x53,0x79,0x6E,0x63,0x20,0x6F,0x66,0x66,0x22,0x20,0x69, - 0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20, - 0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75, - 0x73,0x2E,0x46,0x3E,0x54,0x68,0x69,0x73,0x20,0x68,0x6F,0x77, - 0x65,0x76,0x65,0x72,0x2C,0x20,0x77,0x69,0x6C,0x6C,0x20,0x69, - 0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65,0x20,0x73,0x74,0x75, - 0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61, - 0x75,0x73,0x65,0x20,0x74,0x68,0x65,0x20,0x72,0x65,0x6E,0x64, - 0x65,0x72,0x69,0x6E,0x67,0x20,0x72,0x61,0x74,0x65,0x20,0x69, - 0x73,0x22,0x3E,0x6E,0x6F,0x74,0x20,0x65,0x78,0x61,0x63,0x74, - 0x20,0x74,0x6F,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E, - 0x69,0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x61,0x74,0x65,0x2E, - 0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x33,0x3E,0x40,0x43,0x30, - 0x30,0x31,0x51,0x3A,0x20,0x57,0x69,0x6C,0x6C,0x20,0x79,0x6F, - 0x75,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20, - 0x4D,0x49,0x44,0x49,0x20,0x6F,0x75,0x74,0x20,0x66,0x75,0x6E, - 0x63,0x74,0x69,0x6F,0x6E,0x61,0x6C,0x69,0x74,0x79,0x3F,0x4D, - 0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x4E,0x6F,0x2C, - 0x20,0x73,0x6F,0x72,0x72,0x79,0x2E,0x20,0x54,0x68,0x69,0x73, - 0x20,0x69,0x73,0x20,0x76,0x65,0x72,0x79,0x20,0x64,0x69,0x66, - 0x66,0x69,0x63,0x75,0x6C,0x74,0x20,0x74,0x6F,0x20,0x69,0x6D, - 0x70,0x6C,0x65,0x6D,0x65,0x6E,0x74,0x20,0x63,0x6F,0x72,0x72, - 0x65,0x63,0x74,0x6C,0x79,0x20,0x77,0x68,0x65,0x6E,0x20,0x68, - 0x61,0x76,0x69,0x6E,0x67,0x3C,0x3E,0x40,0x58,0x30,0x33,0x35, - 0x68,0x69,0x67,0x68,0x65,0x72,0x20,0x61,0x75,0x64,0x69,0x6F, - 0x20,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x73,0x69,0x7A,0x65, - 0x73,0x20,0x28,0x62,0x75,0x66,0x66,0x65,0x72,0x65,0x64,0x20, - 0x72,0x65,0x70,0x6C,0x61,0x79,0x65,0x72,0x20,0x74,0x69,0x63, - 0x6B,0x73,0x29,0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32, - 0x30,0x30,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x57, - 0x68,0x65,0x72,0x65,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20, - 0x63,0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F, - 0x6E,0x20,0x66,0x69,0x6C,0x65,0x20,0x73,0x74,0x6F,0x72,0x65, - 0x64,0x3F,0x3F,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20, - 0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x3A,0x20,0x5C,0x55,0x73, - 0x65,0x72,0x73,0x5C,0x55,0x53,0x45,0x52,0x5C,0x41,0x70,0x70, - 0x44,0x61,0x74,0x61,0x5C,0x52,0x6F,0x61,0x6D,0x69,0x6E,0x67, - 0x5C,0x46,0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x5C,0x46, - 0x54,0x32,0x2E,0x43,0x46,0x47,0x45,0x3E,0x40,0x58,0x30,0x33, - 0x35,0x4F,0x53,0x20,0x58,0x3A,0x20,0x2F,0x55,0x73,0x65,0x72, - 0x73,0x2F,0x55,0x53,0x45,0x52,0x2F,0x4C,0x69,0x62,0x72,0x61, - 0x72,0x79,0x2F,0x41,0x70,0x70,0x6C,0x69,0x63,0x61,0x74,0x69, - 0x6F,0x6E,0x20,0x53,0x75,0x70,0x70,0x6F,0x72,0x74,0x2F,0x46, - 0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32, - 0x2E,0x43,0x46,0x47,0x2F,0x47,0x4E,0x55,0x2F,0x4C,0x69,0x6E, - 0x75,0x78,0x3A,0x20,0x2F,0x68,0x6F,0x6D,0x65,0x2F,0x55,0x53, - 0x45,0x52,0x2F,0x2E,0x63,0x6F,0x6E,0x66,0x69,0x67,0x2F,0x46, - 0x54,0x32,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x2F,0x46,0x54,0x32, - 0x2E,0x43,0x46,0x47,0x01,0x3E,0x48,0x49,0x74,0x20,0x77,0x69, - 0x6C,0x6C,0x20,0x62,0x65,0x20,0x73,0x74,0x6F,0x72,0x65,0x64, - 0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67, - 0x72,0x61,0x6D,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72, - 0x79,0x20,0x69,0x66,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x74, - 0x68,0x20,0x63,0x6F,0x75,0x6C,0x64,0x6E,0x27,0x74,0x20,0x62, - 0x65,0x20,0x75,0x73,0x65,0x64,0x2E,0x4D,0x49,0x66,0x20,0x79, - 0x6F,0x75,0x20,0x70,0x75,0x74,0x20,0x74,0x68,0x65,0x20,0x63, - 0x6F,0x6E,0x66,0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E, - 0x20,0x66,0x69,0x6C,0x65,0x20,0x69,0x6E,0x20,0x74,0x68,0x65, - 0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x64,0x69,0x72, - 0x65,0x63,0x74,0x6F,0x72,0x79,0x2C,0x20,0x69,0x74,0x20,0x77, - 0x69,0x6C,0x6C,0x20,0x72,0x65,0x61,0x64,0x20,0x74,0x68,0x61, - 0x74,0x4A,0x6F,0x6E,0x65,0x20,0x61,0x6E,0x64,0x20,0x6E,0x6F, - 0x74,0x20,0x61,0x74,0x74,0x65,0x6D,0x70,0x74,0x20,0x74,0x6F, - 0x20,0x63,0x72,0x65,0x61,0x74,0x65,0x20,0x63,0x6F,0x6E,0x66, - 0x69,0x67,0x20,0x64,0x69,0x72,0x73,0x20,0x66,0x6F,0x72,0x20, - 0x74,0x68,0x65,0x20,0x4F,0x53,0x20,0x75,0x73,0x65,0x72,0x2E, - 0x20,0x28,0x70,0x6F,0x72,0x74,0x61,0x62,0x6C,0x65,0x20,0x6D, - 0x6F,0x64,0x65,0x29,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x42, - 0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x43,0x61,0x6E, - 0x20,0x74,0x68,0x65,0x20,0x63,0x6C,0x6F,0x6E,0x65,0x20,0x72, - 0x65,0x61,0x64,0x20,0x46,0x54,0x32,0x2E,0x43,0x46,0x47,0x20, - 0x66,0x72,0x6F,0x6D,0x20,0x72,0x65,0x61,0x6C,0x20,0x46,0x54, - 0x32,0x2C,0x20,0x61,0x6E,0x64,0x20,0x76,0x69,0x63,0x65,0x20, - 0x76,0x65,0x72,0x73,0x61,0x3F,0x4C,0x3E,0x40,0x43,0x30,0x30, - 0x32,0x41,0x3A,0x20,0x59,0x65,0x73,0x2C,0x20,0x69,0x74,0x20, - 0x73,0x68,0x6F,0x75,0x6C,0x64,0x20,0x77,0x6F,0x72,0x6B,0x20, - 0x6A,0x75,0x73,0x74,0x20,0x66,0x69,0x6E,0x65,0x2E,0x20,0x50, - 0x75,0x74,0x20,0x69,0x74,0x20,0x69,0x6E,0x20,0x74,0x68,0x65, - 0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x6F,0x72,0x79,0x20,0x73, - 0x68,0x6F,0x77,0x6E,0x20,0x61,0x62,0x6F,0x76,0x65,0x2E,0x06, - 0x3E,0x40,0x58,0x30,0x32,0x30,0x52,0x3E,0x40,0x43,0x30,0x30, - 0x31,0x51,0x3A,0x20,0x53,0x6D,0x70,0x2E,0x20,0x45,0x64,0x2E, - 0x3A,0x20,0x57,0x68,0x69,0x6C,0x65,0x20,0x7A,0x6F,0x6F,0x6D, - 0x69,0x6E,0x67,0x20,0x69,0x6E,0x2C,0x20,0x49,0x20,0x73,0x6F, - 0x6D,0x65,0x74,0x69,0x6D,0x65,0x73,0x20,0x63,0x61,0x6E,0x27, - 0x74,0x20,0x6D,0x61,0x72,0x6B,0x20,0x74,0x68,0x65,0x20,0x6C, - 0x61,0x73,0x74,0x20,0x73,0x61,0x6D,0x70,0x6C,0x65,0x20,0x70, - 0x6F,0x69,0x6E,0x74,0x21,0x47,0x3E,0x40,0x43,0x30,0x30,0x32, - 0x41,0x3A,0x20,0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x6E, - 0x6F,0x72,0x6D,0x61,0x6C,0x2E,0x20,0x54,0x68,0x69,0x73,0x20, - 0x69,0x73,0x20,0x61,0x20,0x6C,0x69,0x6D,0x69,0x74,0x61,0x74, - 0x69,0x6F,0x6E,0x20,0x69,0x6E,0x20,0x74,0x68,0x65,0x20,0x6E, - 0x61,0x74,0x75,0x72,0x65,0x20,0x6F,0x66,0x20,0x73,0x63,0x61, - 0x6C,0x69,0x6E,0x67,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30, - 0x17,0x3E,0x40,0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x49,0x20, - 0x66,0x6F,0x75,0x6E,0x64,0x20,0x61,0x20,0x62,0x75,0x67,0x21, - 0x4C,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A,0x20,0x50,0x6C, - 0x65,0x61,0x73,0x65,0x20,0x73,0x65,0x6E,0x64,0x20,0x6D,0x65, - 0x20,0x61,0x20,0x6D,0x61,0x69,0x6C,0x20,0x28,0x66,0x6F,0x75, - 0x6E,0x64,0x20,0x61,0x74,0x20,0x31,0x36,0x2D,0x62,0x69,0x74, - 0x73,0x2E,0x6F,0x72,0x67,0x29,0x20,0x61,0x6E,0x64,0x20,0x74, - 0x72,0x79,0x20,0x74,0x6F,0x20,0x65,0x78,0x70,0x6C,0x61,0x69, - 0x6E,0x20,0x69,0x74,0x2E,0x00,0x03,0x45,0x4E,0x44,0x4C,0x3B, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, - 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x0C,0x40,0x4C,0x4B, - 0x6E,0x6F,0x77,0x6E,0x20,0x62,0x75,0x67,0x73,0x06,0x3E,0x40, - 0x58,0x30,0x31,0x30,0x2C,0x3E,0x40,0x43,0x30,0x30,0x31,0x57, - 0x41,0x56,0x20,0x65,0x78,0x70,0x6F,0x72,0x74,0x69,0x6E,0x67, - 0x20,0x28,0x72,0x65,0x6E,0x64,0x65,0x72,0x69,0x6E,0x67,0x20, - 0x73,0x6F,0x6E,0x67,0x20,0x74,0x6F,0x20,0x57,0x41,0x56,0x29, - 0x3A,0x01,0x3E,0x50,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20, - 0x53,0x6F,0x6E,0x67,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x6A, - 0x75,0x6D,0x70,0x20,0x62,0x61,0x63,0x6B,0x20,0x74,0x6F,0x20, - 0x61,0x20,0x70,0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x70, - 0x61,0x74,0x74,0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20, - 0x72,0x65,0x6E,0x64,0x65,0x72,0x20,0x66,0x6F,0x72,0x65,0x76, - 0x65,0x72,0x20,0x61,0x6E,0x64,0x20,0x65,0x76,0x65,0x72,0x2C, - 0x50,0x61,0x6E,0x64,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65, - 0x64,0x20,0x74,0x6F,0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x61, - 0x20,0x6B,0x65,0x79,0x20,0x6F,0x72,0x20,0x63,0x6C,0x69,0x63, - 0x6B,0x20,0x74,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20, - 0x74,0x6F,0x20,0x61,0x62,0x6F,0x72,0x74,0x20,0x74,0x68,0x65, - 0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x20,0x77,0x68,0x65,0x6E, - 0x20,0x79,0x6F,0x75,0x20,0x77,0x61,0x6E,0x74,0x06,0x69,0x74, - 0x20,0x74,0x6F,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C, - 0x3E,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A, - 0x06,0x3E,0x40,0x43,0x30,0x30,0x32,0x50,0x3E,0x40,0x58,0x30, - 0x31,0x30,0x2D,0x20,0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65, - 0x65,0x6E,0x20,0x6D,0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,0x20, - 0x62,0x65,0x20,0x75,0x6E,0x62,0x65,0x61,0x72,0x61,0x62,0x6C, - 0x79,0x20,0x73,0x6C,0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,0x20, - 0x52,0x61,0x73,0x70,0x62,0x65,0x72,0x72,0x79,0x20,0x50,0x69, - 0x20,0x28,0x65,0x76,0x65,0x6E,0x20,0x6F,0x6E,0x20,0x52,0x50, - 0x69,0x20,0x34,0x29,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30,0x31, - 0x30,0x2D,0x20,0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75,0x67, - 0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75, - 0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x20, - 0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65, - 0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74,0x20, - 0x74,0x6F,0x20,0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72,0x20, - 0x35,0x39,0x48,0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32,0x31, - 0x79,0x6F,0x75,0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70,0x65, - 0x72,0x69,0x65,0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75,0x61, - 0x6C,0x20,0x73,0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67, - 0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53,0x79, - 0x6E,0x63,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20, - 0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65,0x6E, - 0x2E,0x51,0x49,0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20,0x72, - 0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75,0x6E, - 0x6E,0x69,0x6E,0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F, - 0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30,0x48, - 0x7A,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65,0x20, - 0x61,0x20,0x68,0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20,0x75, - 0x73,0x65,0x72,0x20,0x6F,0x66,0x20,0x74,0x68,0x69,0x73,0x08, - 0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45,0x4E, - 0x44 + 0x2A,0x2A,0x2A,0x2A,0x2A,0x0C,0x40,0x4C,0x4B,0x6E,0x6F,0x77, + 0x6E,0x20,0x62,0x75,0x67,0x73,0x06,0x3E,0x40,0x58,0x30,0x31, + 0x30,0x2C,0x3E,0x40,0x43,0x30,0x30,0x31,0x57,0x41,0x56,0x20, + 0x65,0x78,0x70,0x6F,0x72,0x74,0x69,0x6E,0x67,0x20,0x28,0x72, + 0x65,0x6E,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x73,0x6F,0x6E, + 0x67,0x20,0x74,0x6F,0x20,0x57,0x41,0x56,0x29,0x3A,0x01,0x3E, + 0x50,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20,0x53,0x6F,0x6E, + 0x67,0x73,0x20,0x74,0x68,0x61,0x74,0x20,0x6A,0x75,0x6D,0x70, + 0x20,0x62,0x61,0x63,0x6B,0x20,0x74,0x6F,0x20,0x61,0x20,0x70, + 0x72,0x65,0x76,0x69,0x6F,0x75,0x73,0x20,0x70,0x61,0x74,0x74, + 0x65,0x72,0x6E,0x20,0x77,0x69,0x6C,0x6C,0x20,0x72,0x65,0x6E, + 0x64,0x65,0x72,0x20,0x66,0x6F,0x72,0x65,0x76,0x65,0x72,0x20, + 0x61,0x6E,0x64,0x20,0x65,0x76,0x65,0x72,0x2C,0x50,0x61,0x6E, + 0x64,0x20,0x79,0x6F,0x75,0x20,0x6E,0x65,0x65,0x64,0x20,0x74, + 0x6F,0x20,0x70,0x72,0x65,0x73,0x73,0x20,0x61,0x20,0x6B,0x65, + 0x79,0x20,0x6F,0x72,0x20,0x63,0x6C,0x69,0x63,0x6B,0x20,0x74, + 0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x74,0x6F,0x20, + 0x61,0x62,0x6F,0x72,0x74,0x20,0x74,0x68,0x65,0x20,0x72,0x65, + 0x6E,0x64,0x65,0x72,0x20,0x77,0x68,0x65,0x6E,0x20,0x79,0x6F, + 0x75,0x20,0x77,0x61,0x6E,0x74,0x06,0x69,0x74,0x20,0x74,0x6F, + 0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C,0x3E,0x40,0x43, + 0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x06,0x3E,0x40, + 0x43,0x30,0x30,0x32,0x50,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D, + 0x20,0x46,0x75,0x6C,0x6C,0x73,0x63,0x72,0x65,0x65,0x6E,0x20, + 0x6D,0x6F,0x64,0x65,0x20,0x63,0x61,0x6E,0x20,0x62,0x65,0x20, + 0x75,0x6E,0x62,0x65,0x61,0x72,0x61,0x62,0x6C,0x79,0x20,0x73, + 0x6C,0x6F,0x77,0x20,0x6F,0x6E,0x20,0x61,0x20,0x52,0x61,0x73, + 0x70,0x62,0x65,0x72,0x72,0x79,0x20,0x50,0x69,0x20,0x28,0x65, + 0x76,0x65,0x6E,0x20,0x6F,0x6E,0x20,0x52,0x50,0x69,0x20,0x34, + 0x29,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20, + 0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75,0x67,0x2C,0x20,0x62, + 0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D, + 0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x65,0x66, + 0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73, + 0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74,0x20,0x74,0x6F,0x20, + 0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48, + 0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32,0x31,0x79,0x6F,0x75, + 0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70,0x65,0x72,0x69,0x65, + 0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75,0x61,0x6C,0x20,0x73, + 0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65, + 0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20, + 0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20, + 0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65,0x6E,0x2E,0x51,0x49, + 0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20,0x72,0x65,0x63,0x6F, + 0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75,0x6E,0x6E,0x69,0x6E, + 0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74, + 0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x69, + 0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65,0x20,0x61,0x20,0x68, + 0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20,0x75,0x73,0x65,0x72, + 0x20,0x6F,0x66,0x20,0x74,0x68,0x69,0x73,0x08,0x70,0x72,0x6F, + 0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45,0x4E,0x44 }; #endif diff --git a/src/libflac/COPYING.txt b/src/libflac/COPYING.txt @@ -0,0 +1,29 @@ +Copyright (C) 2000-2009 Josh Coalson +Copyright (C) 2011-2016 Xiph.Org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/libflac/FLAC/callback.h b/src/libflac/FLAC/callback.h @@ -0,0 +1,185 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__CALLBACK_H +#define FLAC__CALLBACK_H + +#include "ordinals.h" +#include <stdlib.h> /* for size_t */ + +/** \file include/FLAC/callback.h + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * See the detailed documentation for callbacks in the + * \link flac_callbacks callbacks \endlink module. + */ + +/** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures + * \ingroup flac + * + * \brief + * This module defines the structures for describing I/O callbacks + * to the other FLAC interfaces. + * + * The purpose of the I/O callback functions is to create a common way + * for the metadata interfaces to handle I/O. + * + * Originally the metadata interfaces required filenames as the way of + * specifying FLAC files to operate on. This is problematic in some + * environments so there is an additional option to specify a set of + * callbacks for doing I/O on the FLAC file, instead of the filename. + * + * In addition to the callbacks, a FLAC__IOHandle type is defined as an + * opaque structure for a data source. + * + * The callback function prototypes are similar (but not identical) to the + * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use + * stdio streams to implement the callbacks, you can pass fread, fwrite, and + * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or + * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle + * is required. \warning You generally CANNOT directly use fseek or ftell + * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems + * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with + * large files. You will have to find an equivalent function (e.g. ftello), + * or write a wrapper. The same is true for feof() since this is usually + * implemented as a macro, not as a function whose address can be taken. + * + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** This is the opaque handle type used by the callbacks. Typically + * this is a \c FILE* or address of a file descriptor. + */ +typedef void* FLAC__IOHandle; + +/** Signature for the read callback. + * The signature and semantics match POSIX fread() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the read buffer. + * \param size The size of the records to be read. + * \param nmemb The number of records to be read. + * \param handle The handle to the data source. + * \retval size_t + * The number of records read. + */ +typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the write callback. + * The signature and semantics match POSIX fwrite() implementations + * and can generally be used interchangeably. + * + * \param ptr The address of the write buffer. + * \param size The size of the records to be written. + * \param nmemb The number of records to be written. + * \param handle The handle to the data source. + * \retval size_t + * The number of records written. + */ +typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); + +/** Signature for the seek callback. + * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \param offset The new position, relative to \a whence + * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END + * \retval int + * \c 0 on success, \c -1 on error. + */ +typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); + +/** Signature for the tell callback. + * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT + * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' + * and 32-bits wide. + * + * \param handle The handle to the data source. + * \retval FLAC__int64 + * The current position on success, \c -1 on error. + */ +typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); + +/** Signature for the EOF callback. + * The signature and semantics mostly match POSIX feof() but WATCHOUT: + * on many systems, feof() is a macro, so in this case a wrapper function + * must be provided instead. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 if not at end of file, nonzero if at end of file. + */ +typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); + +/** Signature for the close callback. + * The signature and semantics match POSIX fclose() implementations + * and can generally be used interchangeably. + * + * \param handle The handle to the data source. + * \retval int + * \c 0 on success, \c EOF on error. + */ +typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); + +/** A structure for holding a set of callbacks. + * Each FLAC interface that requires a FLAC__IOCallbacks structure will + * describe which of the callbacks are required. The ones that are not + * required may be set to NULL. + * + * If the seek requirement for an interface is optional, you can signify that + * a data source is not seekable by setting the \a seek field to \c NULL. + */ +typedef struct { + FLAC__IOCallback_Read read; + FLAC__IOCallback_Write write; + FLAC__IOCallback_Seek seek; + FLAC__IOCallback_Tell tell; + FLAC__IOCallback_Eof eof; + FLAC__IOCallback_Close close; +} FLAC__IOCallbacks; + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libflac/FLAC/export.h b/src/libflac/FLAC/export.h @@ -0,0 +1,70 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__EXPORT_H +#define FLAC__EXPORT_H + +/** \file include/FLAC/export.h + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * See the \link flac_export export \endlink module. + */ + +/** \defgroup flac_export FLAC/export.h: export symbols + * \ingroup flac + * + * \brief + * This module contains #defines and symbols for exporting function + * calls, and providing version information and compiled-in features. + * + * If you are compiling with MSVC and will link to the static library + * (libFLAC.lib) you should define FLAC__NO_DLL in your project to + * make sure the symbols are exported properly. + * + * \{ + */ + +#define FLAC_API + +/** These #defines will mirror the libtool-based library version number, see + * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning + */ +#define FLAC_API_VERSION_CURRENT 11 +#define FLAC_API_VERSION_REVISION 0 /**< see above */ +#define FLAC_API_VERSION_AGE 3 /**< see above */ + +/* \} */ + +#endif diff --git a/src/libflac/FLAC/format.h b/src/libflac/FLAC/format.h @@ -0,0 +1,1025 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__FORMAT_H +#define FLAC__FORMAT_H + +#include "export.h" +#include "ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file include/FLAC/format.h + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * See the detailed documentation in the + * \link flac_format format \endlink module. + */ + +/** \defgroup flac_format FLAC/format.h: format components + * \ingroup flac + * + * \brief + * This module contains structure definitions for the representation + * of FLAC format components in memory. These are the basic + * structures used by the rest of the interfaces. + * + * First, you should be familiar with the + * <A HREF="../format.html">FLAC format</A>. Many of the values here + * follow directly from the specification. As a user of libFLAC, the + * interesting parts really are the structures that describe the frame + * header and metadata blocks. + * + * The format structures here are very primitive, designed to store + * information in an efficient way. Reading information from the + * structures is easy but creating or modifying them directly is + * more complex. For the most part, as a user of a library, editing + * is not necessary; however, for metadata blocks it is, so there are + * convenience functions provided in the \link flac_metadata metadata + * module \endlink to simplify the manipulation of metadata blocks. + * + * \note + * It's not the best convention, but symbols ending in _LEN are in bits + * and _LENGTH are in bytes. _LENGTH symbols are \#defines instead of + * global variables because they are usually used when declaring byte + * arrays and some compilers require compile-time knowledge of array + * sizes when declared on the stack. + * + * \{ + */ + + +/* + Most of the values described in this file are defined by the FLAC + format specification. There is nothing to tune here. +*/ + +/** The largest legal metadata type code. */ +#define FLAC__MAX_METADATA_TYPE_CODE (126u) + +/** The minimum block size, in samples, permitted by the format. */ +#define FLAC__MIN_BLOCK_SIZE (16u) + +/** The maximum block size, in samples, permitted by the format. */ +#define FLAC__MAX_BLOCK_SIZE (65535u) + +/** The maximum block size, in samples, permitted by the FLAC subset for + * sample rates up to 48kHz. */ +#define FLAC__SUBSET_MAX_BLOCK_SIZE_48000HZ (4608u) + +/** The maximum number of channels permitted by the format. */ +#define FLAC__MAX_CHANNELS (8u) + +/** The minimum sample resolution permitted by the format. */ +#define FLAC__MIN_BITS_PER_SAMPLE (4u) + +/** The maximum sample resolution permitted by the format. */ +#define FLAC__MAX_BITS_PER_SAMPLE (32u) + +/** The maximum sample resolution permitted by libFLAC. + * + * \warning + * FLAC__MAX_BITS_PER_SAMPLE is the limit of the FLAC format. However, + * the reference encoder/decoder is currently limited to 24 bits because + * of prevalent 32-bit math, so make sure and use this value when + * appropriate. + */ +#define FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE (24u) + +/** The maximum sample rate permitted by the format. The value is + * ((2 ^ 16) - 1) * 10; see <A HREF="../format.html">FLAC format</A> + * as to why. + */ +#define FLAC__MAX_SAMPLE_RATE (655350u) + +/** The maximum LPC order permitted by the format. */ +#define FLAC__MAX_LPC_ORDER (32u) + +/** The maximum LPC order permitted by the FLAC subset for sample rates + * up to 48kHz. */ +#define FLAC__SUBSET_MAX_LPC_ORDER_48000HZ (12u) + +/** The minimum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MIN_QLP_COEFF_PRECISION (5u) + +/** The maximum quantized linear predictor coefficient precision + * permitted by the format. + */ +#define FLAC__MAX_QLP_COEFF_PRECISION (15u) + +/** The maximum order of the fixed predictors permitted by the format. */ +#define FLAC__MAX_FIXED_ORDER (4u) + +/** The maximum Rice partition order permitted by the format. */ +#define FLAC__MAX_RICE_PARTITION_ORDER (15u) + +/** The maximum Rice partition order permitted by the FLAC Subset. */ +#define FLAC__SUBSET_MAX_RICE_PARTITION_ORDER (8u) + +/** The version string of the release, stamped onto the libraries and binaries. + * + * \note + * This does not correspond to the shared library version number, which + * is used to determine binary compatibility. + */ +extern FLAC_API const char *FLAC__VERSION_STRING; + +/** The vendor string inserted by the encoder into the VORBIS_COMMENT block. + * This is a NUL-terminated ASCII string; when inserted into the + * VORBIS_COMMENT the trailing null is stripped. + */ +extern FLAC_API const char *FLAC__VENDOR_STRING; + +/** The byte string representation of the beginning of a FLAC stream. */ +extern FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */ + +/** The 32-bit integer big-endian representation of the beginning of + * a FLAC stream. + */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC; /* = 0x664C6143 */ + +/** The length of the FLAC signature in bits. */ +extern FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN; /* = 32 bits */ + +/** The length of the FLAC signature in bytes. */ +#define FLAC__STREAM_SYNC_LENGTH (4u) + + +/***************************************************************************** + * + * Subframe structures + * + *****************************************************************************/ + +/*****************************************************************************/ + +/** An enumeration of the available entropy coding methods. */ +typedef enum { + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE = 0, + /**< Residual is coded by partitioning into contexts, each with it's own + * 4-bit Rice parameter. */ + + FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 = 1 + /**< Residual is coded by partitioning into contexts, each with it's own + * 5-bit Rice parameter. */ +} FLAC__EntropyCodingMethodType; + +/** Maps a FLAC__EntropyCodingMethodType to a C string. + * + * Using a FLAC__EntropyCodingMethodType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[]; + + +/** Contents of a Rice partitioned residual + */ +typedef struct { + + uint32_t *parameters; + /**< The Rice parameters for each context. */ + + uint32_t *raw_bits; + /**< Widths for escape-coded partitions. Will be non-zero for escaped + * partitions and zero for unescaped partitions. + */ + + uint32_t capacity_by_order; + /**< The capacity of the \a parameters and \a raw_bits arrays + * specified as an order, i.e. the number of array elements + * allocated is 2 ^ \a capacity_by_order. + */ +} FLAC__EntropyCodingMethod_PartitionedRiceContents; + +/** Header for a Rice partitioned residual. (c.f. <A HREF="../format.html#partitioned_rice">format specification</A>) + */ +typedef struct { + + uint32_t order; + /**< The partition order, i.e. # of contexts = 2 ^ \a order. */ + + const FLAC__EntropyCodingMethod_PartitionedRiceContents *contents; + /**< The context's Rice parameters and/or raw bits. */ + +} FLAC__EntropyCodingMethod_PartitionedRice; + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN; /**< == 5 (bits) */ + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; +/**< == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */ +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER; +/**< == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */ + +/** Header for the entropy coding method. (c.f. <A HREF="../format.html#residual">format specification</A>) + */ +typedef struct { + FLAC__EntropyCodingMethodType type; + union { + FLAC__EntropyCodingMethod_PartitionedRice partitioned_rice; + } data; +} FLAC__EntropyCodingMethod; + +extern FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN; /**< == 2 (bits) */ + +/*****************************************************************************/ + +/** An enumeration of the available subframe types. */ +typedef enum { + FLAC__SUBFRAME_TYPE_CONSTANT = 0, /**< constant signal */ + FLAC__SUBFRAME_TYPE_VERBATIM = 1, /**< uncompressed signal */ + FLAC__SUBFRAME_TYPE_FIXED = 2, /**< fixed polynomial prediction */ + FLAC__SUBFRAME_TYPE_LPC = 3 /**< linear prediction */ +} FLAC__SubframeType; + +/** Maps a FLAC__SubframeType to a C string. + * + * Using a FLAC__SubframeType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__SubframeTypeString[]; + + +/** CONSTANT subframe. (c.f. <A HREF="../format.html#subframe_constant">format specification</A>) + */ +typedef struct { + FLAC__int32 value; /**< The constant signal value. */ +} FLAC__Subframe_Constant; + + +/** VERBATIM subframe. (c.f. <A HREF="../format.html#subframe_verbatim">format specification</A>) + */ +typedef struct { + const FLAC__int32 *data; /**< A pointer to verbatim signal. */ +} FLAC__Subframe_Verbatim; + + +/** FIXED subframe. (c.f. <A HREF="../format.html#subframe_fixed">format specification</A>) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + uint32_t order; + /**< The polynomial order. */ + + FLAC__int32 warmup[FLAC__MAX_FIXED_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_Fixed; + + +/** LPC subframe. (c.f. <A HREF="../format.html#subframe_lpc">format specification</A>) + */ +typedef struct { + FLAC__EntropyCodingMethod entropy_coding_method; + /**< The residual coding method. */ + + uint32_t order; + /**< The FIR order. */ + + uint32_t qlp_coeff_precision; + /**< Quantized FIR filter coefficient precision in bits. */ + + int quantization_level; + /**< The qlp coeff shift needed. */ + + FLAC__int32 qlp_coeff[FLAC__MAX_LPC_ORDER]; + /**< FIR filter coefficients. */ + + FLAC__int32 warmup[FLAC__MAX_LPC_ORDER]; + /**< Warmup samples to prime the predictor, length == order. */ + + const FLAC__int32 *residual; + /**< The residual signal, length == (blocksize minus order) samples. */ +} FLAC__Subframe_LPC; + +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN; /**< == 5 (bits) */ + + +/** FLAC subframe structure. (c.f. <A HREF="../format.html#subframe">format specification</A>) + */ +typedef struct { + FLAC__SubframeType type; + union { + FLAC__Subframe_Constant constant; + FLAC__Subframe_Fixed fixed; + FLAC__Subframe_LPC lpc; + FLAC__Subframe_Verbatim verbatim; + } data; + uint32_t wasted_bits; +} FLAC__Subframe; + +/** == 1 (bit) + * + * This used to be a zero-padding bit (hence the name + * FLAC__SUBFRAME_ZERO_PAD_LEN) but is now a reserved bit. It still has a + * mandatory value of \c 0 but in the future may take on the value \c 0 or \c 1 + * to mean something else. + */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN; +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN; /**< == 6 (bits) */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN; /**< == 1 (bit) */ + +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK; /**< = 0x00 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK; /**< = 0x02 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK; /**< = 0x10 */ +extern FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK; /**< = 0x40 */ + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Frame structures + * + *****************************************************************************/ + +/** An enumeration of the available channel assignments. */ +typedef enum { + FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT = 0, /**< independent channels */ + FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE = 1, /**< left+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE = 2, /**< right+side stereo */ + FLAC__CHANNEL_ASSIGNMENT_MID_SIDE = 3 /**< mid+side stereo */ +} FLAC__ChannelAssignment; + +/** Maps a FLAC__ChannelAssignment to a C string. + * + * Using a FLAC__ChannelAssignment as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__ChannelAssignmentString[]; + +/** An enumeration of the possible frame numbering methods. */ +typedef enum { + FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER, /**< number contains the frame number */ + FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER /**< number contains the sample number of first sample in frame */ +} FLAC__FrameNumberType; + +/** Maps a FLAC__FrameNumberType to a C string. + * + * Using a FLAC__FrameNumberType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__FrameNumberTypeString[]; + + +/** FLAC frame header structure. (c.f. <A HREF="../format.html#frame_header">format specification</A>) + */ +typedef struct { + uint32_t blocksize; + /**< The number of samples per subframe. */ + + uint32_t sample_rate; + /**< The sample rate in Hz. */ + + uint32_t channels; + /**< The number of channels (== number of subframes). */ + + FLAC__ChannelAssignment channel_assignment; + /**< The channel assignment for the frame. */ + + uint32_t bits_per_sample; + /**< The sample resolution. */ + + FLAC__FrameNumberType number_type; + /**< The numbering scheme used for the frame. As a convenience, the + * decoder will always convert a frame number to a sample number because + * the rules are complex. */ + + union { + FLAC__uint32 frame_number; + FLAC__uint64 sample_number; + } number; + /**< The frame number or sample number of first sample in frame; + * use the \a number_type value to determine which to use. */ + + FLAC__uint8 crc; + /**< CRC-8 (polynomial = x^8 + x^2 + x^1 + x^0, initialized with 0) + * of the raw frame header bytes, meaning everything before the CRC byte + * including the sync code. + */ +} FLAC__FrameHeader; + +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC; /**< == 0x3ffe; the frame header sync code */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN; /**< == 14 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN; /**< == 1 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN; /**< == 4 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN; /**< == 8 (bits) */ + + +/** FLAC frame footer structure. (c.f. <A HREF="../format.html#frame_footer">format specification</A>) + */ +typedef struct { + FLAC__uint16 crc; + /**< CRC-16 (polynomial = x^16 + x^15 + x^2 + x^0, initialized with + * 0) of the bytes before the crc, back to and including the frame header + * sync code. + */ +} FLAC__FrameFooter; + +extern FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN; /**< == 16 (bits) */ + + +/** FLAC frame structure. (c.f. <A HREF="../format.html#frame">format specification</A>) + */ +typedef struct { + FLAC__FrameHeader header; + FLAC__Subframe subframes[FLAC__MAX_CHANNELS]; + FLAC__FrameFooter footer; +} FLAC__Frame; + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Meta-data structures + * + *****************************************************************************/ + +/** An enumeration of the available metadata block types. */ +typedef enum { + + FLAC__METADATA_TYPE_STREAMINFO = 0, + /**< <A HREF="../format.html#metadata_block_streaminfo">STREAMINFO</A> block */ + + FLAC__METADATA_TYPE_PADDING = 1, + /**< <A HREF="../format.html#metadata_block_padding">PADDING</A> block */ + + FLAC__METADATA_TYPE_APPLICATION = 2, + /**< <A HREF="../format.html#metadata_block_application">APPLICATION</A> block */ + + FLAC__METADATA_TYPE_SEEKTABLE = 3, + /**< <A HREF="../format.html#metadata_block_seektable">SEEKTABLE</A> block */ + + FLAC__METADATA_TYPE_VORBIS_COMMENT = 4, + /**< <A HREF="../format.html#metadata_block_vorbis_comment">VORBISCOMMENT</A> block (a.k.a. FLAC tags) */ + + FLAC__METADATA_TYPE_CUESHEET = 5, + /**< <A HREF="../format.html#metadata_block_cuesheet">CUESHEET</A> block */ + + FLAC__METADATA_TYPE_PICTURE = 6, + /**< <A HREF="../format.html#metadata_block_picture">PICTURE</A> block */ + + FLAC__METADATA_TYPE_UNDEFINED = 7, + /**< marker to denote beginning of undefined type range; this number will increase as new metadata types are added */ + + FLAC__MAX_METADATA_TYPE = FLAC__MAX_METADATA_TYPE_CODE, + /**< No type will ever be greater than this. There is not enough room in the protocol block. */ +} FLAC__MetadataType; + +/** Maps a FLAC__MetadataType to a C string. + * + * Using a FLAC__MetadataType as the index to this array will + * give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__MetadataTypeString[]; + + +/** FLAC STREAMINFO structure. (c.f. <A HREF="../format.html#metadata_block_streaminfo">format specification</A>) + */ +typedef struct { + uint32_t min_blocksize, max_blocksize; + uint32_t min_framesize, max_framesize; + uint32_t sample_rate; + uint32_t channels; + uint32_t bits_per_sample; + FLAC__uint64 total_samples; + FLAC__byte md5sum[16]; +} FLAC__StreamMetadata_StreamInfo; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; /**< == 16 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; /**< == 24 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; /**< == 20 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; /**< == 3 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; /**< == 5 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; /**< == 36 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN; /**< == 128 (bits) */ + +/** The total stream length of the STREAMINFO block in bytes. */ +#define FLAC__STREAM_METADATA_STREAMINFO_LENGTH (34u) + +/** FLAC PADDING structure. (c.f. <A HREF="../format.html#metadata_block_padding">format specification</A>) + */ +typedef struct { + int dummy; + /**< Conceptually this is an empty struct since we don't store the + * padding bytes. Empty structs are not allowed by some C compilers, + * hence the dummy. + */ +} FLAC__StreamMetadata_Padding; + + +/** FLAC APPLICATION structure. (c.f. <A HREF="../format.html#metadata_block_application">format specification</A>) + */ +typedef struct { + FLAC__byte id[4]; + FLAC__byte *data; +} FLAC__StreamMetadata_Application; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN; /**< == 32 (bits) */ + +/** SeekPoint structure used in SEEKTABLE blocks. (c.f. <A HREF="../format.html#seekpoint">format specification</A>) + */ +typedef struct { + FLAC__uint64 sample_number; + /**< The sample number of the target frame. */ + + FLAC__uint64 stream_offset; + /**< The offset, in bytes, of the target frame with respect to + * beginning of the first frame. */ + + uint32_t frame_samples; + /**< The number of samples in the target frame. */ +} FLAC__StreamMetadata_SeekPoint; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN; /**< == 16 (bits) */ + +/** The total stream length of a seek point in bytes. */ +#define FLAC__STREAM_METADATA_SEEKPOINT_LENGTH (18u) + +/** The value used in the \a sample_number field of + * FLAC__StreamMetadataSeekPoint used to indicate a placeholder + * point (== 0xffffffffffffffff). + */ +extern FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + + +/** FLAC SEEKTABLE structure. (c.f. <A HREF="../format.html#metadata_block_seektable">format specification</A>) + * + * \note From the format specification: + * - The seek points must be sorted by ascending sample number. + * - Each seek point's sample number must be the first sample of the + * target frame. + * - Each seek point's sample number must be unique within the table. + * - Existence of a SEEKTABLE block implies a correct setting of + * total_samples in the stream_info block. + * - Behavior is undefined when more than one SEEKTABLE block is + * present in a stream. + */ +typedef struct { + uint32_t num_points; + FLAC__StreamMetadata_SeekPoint *points; +} FLAC__StreamMetadata_SeekTable; + + +/** Vorbis comment entry structure used in VORBIS_COMMENT blocks. (c.f. <A HREF="../format.html#metadata_block_vorbis_comment">format specification</A>) + * + * For convenience, the APIs maintain a trailing NUL character at the end of + * \a entry which is not counted toward \a length, i.e. + * \code strlen(entry) == length \endcode + */ +typedef struct { + FLAC__uint32 length; + FLAC__byte *entry; +} FLAC__StreamMetadata_VorbisComment_Entry; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN; /**< == 32 (bits) */ + + +/** FLAC VORBIS_COMMENT structure. (c.f. <A HREF="../format.html#metadata_block_vorbis_comment">format specification</A>) + */ +typedef struct { + FLAC__StreamMetadata_VorbisComment_Entry vendor_string; + FLAC__uint32 num_comments; + FLAC__StreamMetadata_VorbisComment_Entry *comments; +} FLAC__StreamMetadata_VorbisComment; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN; /**< == 32 (bits) */ + + +/** FLAC CUESHEET track index structure. (See the + * <A HREF="../format.html#cuesheet_track_index">format specification</A> for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Offset in samples, relative to the track offset, of the index + * point. + */ + + FLAC__byte number; + /**< The index point number. */ +} FLAC__StreamMetadata_CueSheet_Index; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN; /**< == 3*8 (bits) */ + + +/** FLAC CUESHEET track structure. (See the + * <A HREF="../format.html#cuesheet_track">format specification</A> for + * the full description of each field.) + */ +typedef struct { + FLAC__uint64 offset; + /**< Track offset in samples, relative to the beginning of the FLAC audio stream. */ + + FLAC__byte number; + /**< The track number. */ + + char isrc[13]; + /**< Track ISRC. This is a 12-digit alphanumeric code plus a trailing \c NUL byte */ + + uint32_t type:1; + /**< The track type: 0 for audio, 1 for non-audio. */ + + uint32_t pre_emphasis:1; + /**< The pre-emphasis flag: 0 for no pre-emphasis, 1 for pre-emphasis. */ + + FLAC__byte num_indices; + /**< The number of track index points. */ + + FLAC__StreamMetadata_CueSheet_Index *indices; + /**< NULL if num_indices == 0, else pointer to array of index points. */ + +} FLAC__StreamMetadata_CueSheet_Track; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN; /**< == 8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN; /**< == 12*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN; /**< == 6+13*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN; /**< == 8 (bits) */ + + +/** FLAC CUESHEET structure. (See the + * <A HREF="../format.html#metadata_block_cuesheet">format specification</A> + * for the full description of each field.) + */ +typedef struct { + char media_catalog_number[129]; + /**< Media catalog number, in ASCII printable characters 0x20-0x7e. In + * general, the media catalog number may be 0 to 128 bytes long; any + * unused characters should be right-padded with NUL characters. + */ + + FLAC__uint64 lead_in; + /**< The number of lead-in samples. */ + + FLAC__bool is_cd; + /**< \c true if CUESHEET corresponds to a Compact Disc, else \c false. */ + + uint32_t num_tracks; + /**< The number of tracks. */ + + FLAC__StreamMetadata_CueSheet_Track *tracks; + /**< NULL if num_tracks == 0, else pointer to array of tracks. */ + +} FLAC__StreamMetadata_CueSheet; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN; /**< == 128*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN; /**< == 64 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN; /**< == 7+258*8 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN; /**< == 8 (bits) */ + + +/** An enumeration of the PICTURE types (see FLAC__StreamMetadataPicture and id3 v2.4 APIC tag). */ +typedef enum { + FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER = 0, /**< Other */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD = 1, /**< 32x32 pixels 'file icon' (PNG only) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON = 2, /**< Other file icon */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER = 3, /**< Cover (front) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER = 4, /**< Cover (back) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE = 5, /**< Leaflet page */ + FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA = 6, /**< Media (e.g. label side of CD) */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST = 7, /**< Lead artist/lead performer/soloist */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST = 8, /**< Artist/performer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR = 9, /**< Conductor */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND = 10, /**< Band/Orchestra */ + FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER = 11, /**< Composer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST = 12, /**< Lyricist/text writer */ + FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION = 13, /**< Recording Location */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING = 14, /**< During recording */ + FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE = 15, /**< During performance */ + FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE = 16, /**< Movie/video screen capture */ + FLAC__STREAM_METADATA_PICTURE_TYPE_FISH = 17, /**< A bright coloured fish */ + FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION = 18, /**< Illustration */ + FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE = 19, /**< Band/artist logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE = 20, /**< Publisher/Studio logotype */ + FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED +} FLAC__StreamMetadata_Picture_Type; + +/** Maps a FLAC__StreamMetadata_Picture_Type to a C string. + * + * Using a FLAC__StreamMetadata_Picture_Type as the index to this array + * will give the string equivalent. The contents should not be + * modified. + */ +extern FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[]; + +/** FLAC PICTURE structure. (See the + * <A HREF="../format.html#metadata_block_picture">format specification</A> + * for the full description of each field.) + */ +typedef struct { + FLAC__StreamMetadata_Picture_Type type; + /**< The kind of picture stored. */ + + char *mime_type; + /**< Picture data's MIME type, in ASCII printable characters + * 0x20-0x7e, NUL terminated. For best compatibility with players, + * use picture data of MIME type \c image/jpeg or \c image/png. A + * MIME type of '-->' is also allowed, in which case the picture + * data should be a complete URL. In file storage, the MIME type is + * stored as a 32-bit length followed by the ASCII string with no NUL + * terminator, but is converted to a plain C string in this structure + * for convenience. + */ + + FLAC__byte *description; + /**< Picture's description in UTF-8, NUL terminated. In file storage, + * the description is stored as a 32-bit length followed by the UTF-8 + * string with no NUL terminator, but is converted to a plain C string + * in this structure for convenience. + */ + + FLAC__uint32 width; + /**< Picture's width in pixels. */ + + FLAC__uint32 height; + /**< Picture's height in pixels. */ + + FLAC__uint32 depth; + /**< Picture's color depth in bits-per-pixel. */ + + FLAC__uint32 colors; + /**< For indexed palettes (like GIF), picture's number of colors (the + * number of palette entries), or \c 0 for non-indexed (i.e. 2^depth). + */ + + FLAC__uint32 data_length; + /**< Length of binary picture data in bytes. */ + + FLAC__byte *data; + /**< Binary picture data. */ + +} FLAC__StreamMetadata_Picture; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN; /**< == 32 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN; /**< == 32 (bits) */ + + +/** Structure that is used when a metadata block of unknown type is loaded. + * The contents are opaque. The structure is used only internally to + * correctly handle unknown metadata. + */ +typedef struct { + FLAC__byte *data; +} FLAC__StreamMetadata_Unknown; + + +/** FLAC metadata block structure. (c.f. <A HREF="../format.html#metadata_block">format specification</A>) + */ +typedef struct { + FLAC__MetadataType type; + /**< The type of the metadata block; used determine which member of the + * \a data union to dereference. If type >= FLAC__METADATA_TYPE_UNDEFINED + * then \a data.unknown must be used. */ + + FLAC__bool is_last; + /**< \c true if this metadata block is the last, else \a false */ + + uint32_t length; + /**< Length, in bytes, of the block data as it appears in the stream. */ + + union { + FLAC__StreamMetadata_StreamInfo stream_info; + FLAC__StreamMetadata_Padding padding; + FLAC__StreamMetadata_Application application; + FLAC__StreamMetadata_SeekTable seek_table; + FLAC__StreamMetadata_VorbisComment vorbis_comment; + FLAC__StreamMetadata_CueSheet cue_sheet; + FLAC__StreamMetadata_Picture picture; + FLAC__StreamMetadata_Unknown unknown; + } data; + /**< Polymorphic block data; use the \a type value to determine which + * to use. */ +} FLAC__StreamMetadata; + +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN; /**< == 1 (bit) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN; /**< == 7 (bits) */ +extern FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bits) */ + +/** The total stream length of a metadata block header in bytes. */ +#define FLAC__STREAM_METADATA_HEADER_LENGTH (4u) + +/*****************************************************************************/ + + +/***************************************************************************** + * + * Utility functions + * + *****************************************************************************/ + +/** Tests that a sample rate is valid for FLAC. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification, else + * \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate); + +/** Tests that a blocksize at the given sample rate is valid for the FLAC + * subset. + * + * \param blocksize The blocksize to test for compliance. + * \param sample_rate The sample rate is needed, since the valid subset + * blocksize depends on the sample rate. + * \retval FLAC__bool + * \c true if the given blocksize conforms to the specification for the + * subset at the given sample rate, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate); + +/** Tests that a sample rate is valid for the FLAC subset. The subset rules + * for valid sample rates are slightly more complex since the rate has to + * be expressible completely in the frame header. + * + * \param sample_rate The sample rate to test for compliance. + * \retval FLAC__bool + * \c true if the given sample rate conforms to the specification for the + * subset, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate); + +/** Check a Vorbis comment entry name to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment names must be composed only of characters from + * [0x20-0x3C,0x3E-0x7D]. + * + * \param name A NUL-terminated string to be checked. + * \assert + * \code name != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name); + +/** Check a Vorbis comment entry value to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment values must be valid UTF-8 sequences. + * + * \param value A string to be checked. + * \param length A the length of \a value in bytes. May be + * \c (uint32_t)(-1) to indicate that \a value is a plain + * UTF-8 NUL-terminated string. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length); + +/** Check a Vorbis comment entry to see if it conforms to the Vorbis + * comment specification. + * + * Vorbis comment entries must be of the form 'name=value', and 'name' and + * 'value' must be legal according to + * FLAC__format_vorbiscomment_entry_name_is_legal() and + * FLAC__format_vorbiscomment_entry_value_is_legal() respectively. + * + * \param entry An entry to be checked. + * \param length The length of \a entry in bytes. + * \assert + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if entry name is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length); + +/** Check a seek table to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seek table. + * + * \param seek_table A pointer to a seek table to be checked. + * \assert + * \code seek_table != NULL \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table); + +/** Sort a seek table's seek points according to the format specification. + * This includes a "unique-ification" step to remove duplicates, i.e. + * seek points with identical \a sample_number values. Duplicate seek + * points are converted into placeholder points and sorted to the end of + * the table. + * + * \param seek_table A pointer to a seek table to be sorted. + * \assert + * \code seek_table != NULL \endcode + * \retval uint32_t + * The number of duplicate seek points converted into placeholders. + */ +FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param cue_sheet A pointer to an existing cue sheet to be checked. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code cue_sheet != NULL \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation); + +/** Check picture data to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param picture A pointer to existing picture data to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c false if picture data is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libflac/FLAC/metadata.h b/src/libflac/FLAC/metadata.h @@ -0,0 +1,2182 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__METADATA_H +#define FLAC__METADATA_H + +#include <sys/types.h> /* for off_t */ +#include "export.h" +#include "callback.h" +#include "format.h" + +/* -------------------------------------------------------------------- + (For an example of how all these routines are used, see the source + code for the unit tests in src/test_libFLAC/metadata_*.c, or + metaflac in src/metaflac/) + ------------------------------------------------------------------*/ + +/** \file include/FLAC/metadata.h + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in FLAC files. + * + * See the detailed documentation for each interface in the + * \link flac_metadata metadata \endlink module. + */ + +/** \defgroup flac_metadata FLAC/metadata.h: metadata interfaces + * \ingroup flac + * + * \brief + * This module provides functions for creating and manipulating FLAC + * metadata blocks in memory, and three progressively more powerful + * interfaces for traversing and editing metadata in native FLAC files. + * Note that currently only the Chain interface (level 2) supports Ogg + * FLAC files, and it is read-only i.e. no writing back changed + * metadata to file. + * + * There are three metadata interfaces of increasing complexity: + * + * Level 0: + * Read-only access to the STREAMINFO, VORBIS_COMMENT, CUESHEET, and + * PICTURE blocks. + * + * Level 1: + * Read-write access to all metadata blocks. This level is write- + * efficient in most cases (more on this below), and uses less memory + * than level 2. + * + * Level 2: + * Read-write access to all metadata blocks. This level is write- + * efficient in all cases, but uses more memory since all metadata for + * the whole file is read into memory and manipulated before writing + * out again. + * + * What do we mean by efficient? Since FLAC metadata appears at the + * beginning of the file, when writing metadata back to a FLAC file + * it is possible to grow or shrink the metadata such that the entire + * file must be rewritten. However, if the size remains the same during + * changes or PADDING blocks are utilized, only the metadata needs to be + * overwritten, which is much faster. + * + * Efficient means the whole file is rewritten at most one time, and only + * when necessary. Level 1 is not efficient only in the case that you + * cause more than one metadata block to grow or shrink beyond what can + * be accommodated by padding. In this case you should probably use level + * 2, which allows you to edit all the metadata for a file in memory and + * write it out all at once. + * + * All levels know how to skip over and not disturb an ID3v2 tag at the + * front of the file. + * + * All levels access files via their filenames. In addition, level 2 + * has additional alternative read and write functions that take an I/O + * handle and callbacks, for situations where access by filename is not + * possible. + * + * In addition to the three interfaces, this module defines functions for + * creating and manipulating various metadata objects in memory. As we see + * from the Format module, FLAC metadata blocks in memory are very primitive + * structures for storing information in an efficient way. Reading + * information from the structures is easy but creating or modifying them + * directly is more complex. The metadata object routines here facilitate + * this by taking care of the consistency and memory management drudgery. + * + * Unless you will be using the level 1 or 2 interfaces to modify existing + * metadata however, you will not probably not need these. + * + * From a dependency standpoint, none of the encoders or decoders require + * the metadata module. This is so that embedded users can strip out the + * metadata module from libFLAC to reduce the size and complexity. + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \defgroup flac_metadata_level0 FLAC/metadata.h: metadata level 0 interface + * \ingroup flac_metadata + * + * \brief + * The level 0 interface consists of individual routines to read the + * STREAMINFO, VORBIS_COMMENT, CUESHEET, and PICTURE blocks, requiring + * only a filename. + * + * They try to skip any ID3v2 tag at the head of the file. + * + * \{ + */ + +/** Read the STREAMINFO metadata block of the given FLAC file. This function + * will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param streaminfo A pointer to space for the STREAMINFO block. Since + * FLAC__StreamMetadata is a simple structure with no + * memory allocation involved, you pass the address of + * an existing structure. It need not be initialized. + * \assert + * \code filename != NULL \endcode + * \code streaminfo != NULL \endcode + * \retval FLAC__bool + * \c true if a valid STREAMINFO block was read from \a filename. Returns + * \c false if there was a memory allocation error, a file decoder error, + * or the file contained no STREAMINFO block. (A memory allocation error + * is possible because this function must set up a file decoder.) + */ +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo); + +/** Read the VORBIS_COMMENT metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param tags The address where the returned pointer will be + * stored. The \a tags object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code tags != NULL \endcode + * \retval FLAC__bool + * \c true if a valid VORBIS_COMMENT block was read from \a filename, + * and \a *tags will be set to the address of the metadata structure. + * Returns \c false if there was a memory allocation error, a file + * decoder error, or the file contained no VORBIS_COMMENT block, and + * \a *tags will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags); + +/** Read the CUESHEET metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * + * \param filename The path to the FLAC file to read. + * \param cuesheet The address where the returned pointer will be + * stored. The \a cuesheet object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \assert + * \code filename != NULL \endcode + * \code cuesheet != NULL \endcode + * \retval FLAC__bool + * \c true if a valid CUESHEET block was read from \a filename, + * and \a *cuesheet will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no CUESHEET + * block, and \a *cuesheet will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet); + +/** Read a PICTURE metadata block of the given FLAC file. This + * function will try to skip any ID3v2 tag at the head of the file. + * Since there can be more than one PICTURE block in a file, this + * function takes a number of parameters that act as constraints to + * the search. The PICTURE block with the largest area matching all + * the constraints will be returned, or \a *picture will be set to + * \c NULL if there was no such block. + * + * \param filename The path to the FLAC file to read. + * \param picture The address where the returned pointer will be + * stored. The \a picture object must be deleted by + * the caller using FLAC__metadata_object_delete(). + * \param type The desired picture type. Use \c -1 to mean + * "any type". + * \param mime_type The desired MIME type, e.g. "image/jpeg". The + * string will be matched exactly. Use \c NULL to + * mean "any MIME type". + * \param description The desired description. The string will be + * matched exactly. Use \c NULL to mean "any + * description". + * \param max_width The maximum width in pixels desired. Use + * \c (uint32_t)(-1) to mean "any width". + * \param max_height The maximum height in pixels desired. Use + * \c (uint32_t)(-1) to mean "any height". + * \param max_depth The maximum color depth in bits-per-pixel desired. + * Use \c (uint32_t)(-1) to mean "any depth". + * \param max_colors The maximum number of colors desired. Use + * \c (uint32_t)(-1) to mean "any number of colors". + * \assert + * \code filename != NULL \endcode + * \code picture != NULL \endcode + * \retval FLAC__bool + * \c true if a valid PICTURE block was read from \a filename, + * and \a *picture will be set to the address of the metadata + * structure. Returns \c false if there was a memory allocation + * error, a file decoder error, or the file contained no PICTURE + * block, and \a *picture will be set to \c NULL. + */ +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors); + +/* \} */ + + +/** \defgroup flac_metadata_level1 FLAC/metadata.h: metadata level 1 interface + * \ingroup flac_metadata + * + * \brief + * The level 1 interface provides read-write access to FLAC file metadata and + * operates directly on the FLAC file. + * + * The general usage of this interface is: + * + * - Create an iterator using FLAC__metadata_simple_iterator_new() + * - Attach it to a file using FLAC__metadata_simple_iterator_init() and check + * the exit code. Call FLAC__metadata_simple_iterator_is_writable() to + * see if the file is writable, or only read access is allowed. + * - Use FLAC__metadata_simple_iterator_next() and + * FLAC__metadata_simple_iterator_prev() to traverse the blocks. + * This is does not read the actual blocks themselves. + * FLAC__metadata_simple_iterator_next() is relatively fast. + * FLAC__metadata_simple_iterator_prev() is slower since it needs to search + * forward from the front of the file. + * - Use FLAC__metadata_simple_iterator_get_block_type() or + * FLAC__metadata_simple_iterator_get_block() to access the actual data at + * the current iterator position. The returned object is yours to modify + * and free. + * - Use FLAC__metadata_simple_iterator_set_block() to write a modified block + * back. You must have write permission to the original file. Make sure to + * read the whole comment to FLAC__metadata_simple_iterator_set_block() + * below. + * - Use FLAC__metadata_simple_iterator_insert_block_after() to add new blocks. + * Use the object creation functions from + * \link flac_metadata_object here \endlink to generate new objects. + * - Use FLAC__metadata_simple_iterator_delete_block() to remove the block + * currently referred to by the iterator, or replace it with padding. + * - Destroy the iterator with FLAC__metadata_simple_iterator_delete() when + * finished. + * + * \note + * The FLAC file remains open the whole time between + * FLAC__metadata_simple_iterator_init() and + * FLAC__metadata_simple_iterator_delete(), so make sure you are not altering + * the file during this time. + * + * \note + * Do not modify the \a is_last, \a length, or \a type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * If any of the modification functions + * (FLAC__metadata_simple_iterator_set_block(), + * FLAC__metadata_simple_iterator_delete_block(), + * FLAC__metadata_simple_iterator_insert_block_after(), etc.) return \c false, + * you should delete the iterator as it may no longer be valid. + * + * \{ + */ + +struct FLAC__Metadata_SimpleIterator; +/** The opaque structure definition for the level 1 iterator type. + * See the + * \link flac_metadata_level1 metadata level 1 module \endlink + * for a detailed description. + */ +typedef struct FLAC__Metadata_SimpleIterator FLAC__Metadata_SimpleIterator; + +/** Status type for FLAC__Metadata_SimpleIterator. + * + * The iterator's current status can be obtained by calling FLAC__metadata_simple_iterator_status(). + */ +typedef enum { + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK = 0, + /**< The iterator is in the normal OK state */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE, + /**< The iterator could not open the target file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE, + /**< The iterator could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE, + /**< The iterator tried to write to a file that was not writable */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA, + /**< The iterator encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR, + /**< The iterator encountered an error while reading the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, + /**< The iterator encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR, + /**< The iterator encountered an error while writing the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR, + /**< The iterator encountered an error renaming the FLAC file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR, + /**< The iterator encountered an error removing the temporary file */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR + /**< The caller violated an assertion or an unexpected error occurred */ + +} FLAC__Metadata_SimpleIteratorStatus; + +/** Maps a FLAC__Metadata_SimpleIteratorStatus to a C string. + * + * Using a FLAC__Metadata_SimpleIteratorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[]; + + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_SimpleIterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator); + +/** Get the current status of the iterator. Call this after a function + * returns \c false to get the reason for the error. Also resets the status + * to FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__Metadata_SimpleIteratorStatus + * The current status of the iterator. + */ +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given FLAC file. + * + * \param iterator A pointer to an existing iterator. + * \param filename The path to the FLAC file. + * \param read_only If \c true, the FLAC file will be opened + * in read-only mode; if \c false, the FLAC + * file will be opened for edit even if no + * edits are performed. + * \param preserve_file_stats If \c true, the owner and modification + * time will be preserved even if the FLAC + * file is written to. + * \assert + * \code iterator != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c false if a memory allocation error occurs, the file can't be + * opened, or another error occurs, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats); + +/** Returns \c true if the FLAC file is writable. If \c false, calls to + * FLAC__metadata_simple_iterator_set_block() and + * FLAC__metadata_simple_iterator_insert_block_after() will fail. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator); + +/** Returns a flag telling if the current metadata block is the last. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the current metadata block is the last in the file, + * else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the offset of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval off_t + * The offset of the metadata block at the current iterator position. + * This is the byte offset relative to the beginning of the file of + * the current metadata block's header. + */ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the type of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the length of the metadata block at the current position. This + * avoids reading the actual block data which can save time for large + * blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval uint32_t + * The length of the metadata block at the current iterator position. + * The is same length as that in the + * <a href="http://xiph.org/flac/format.html#metadata_block_header">metadata block header</a>, + * i.e. the length of the metadata body that follows the header. + */ +FLAC_API uint32_t FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator); + +/** Get the application ID of the \c APPLICATION block at the current + * position. This avoids reading the actual block data which can save + * time for large blocks. + * + * \param iterator A pointer to an existing initialized iterator. + * \param id A pointer to a buffer of at least \c 4 bytes where + * the ID will be stored. + * \assert + * \code iterator != NULL \endcode + * \code id != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if the ID was successfully read, else \c false, in which + * case you should check FLAC__metadata_simple_iterator_status() to + * find out why. If the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT, then the + * current metadata block is not an \c APPLICATION block. Otherwise + * if the status is + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR or + * \c FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR, an I/O error + * occurred and the iterator can no longer be used. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id); + +/** Get the metadata block at the current position. You can modify the + * block but must use FLAC__metadata_simple_iterator_set_block() to + * write it back to the FLAC file. + * + * You must call FLAC__metadata_object_delete() on the returned object + * when you are finished with it. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block, or \c NULL if there was a memory + * allocation error. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator); + +/** Write a block back to the FLAC file. This function tries to be + * as efficient as possible; how the block is actually written is + * shown by the following: + * + * Existing block is a STREAMINFO block and the new block is a + * STREAMINFO block: the new block is written in place. Make sure + * you know what you're doing when changing the values of a + * STREAMINFO block. + * + * Existing block is a STREAMINFO block and the new block is a + * not a STREAMINFO block: this is an error since the first block + * must be a STREAMINFO block. Returns \c false without altering the + * file. + * + * Existing block is not a STREAMINFO block and the new block is a + * STREAMINFO block: this is an error since there may be only one + * STREAMINFO block. Returns \c false without altering the file. + * + * Existing block and new block are the same length: the existing + * block will be replaced by the new block, written in place. + * + * Existing block is longer than new block: if use_padding is \c true, + * the existing block will be overwritten in place with the new + * block followed by a PADDING block, if possible, to make the total + * size the same as the existing block. Remember that a padding + * block requires at least four bytes so if the difference in size + * between the new block and existing block is less than that, the + * entire file will have to be rewritten, using the new block's + * exact size. If use_padding is \c false, the entire file will be + * rewritten, replacing the existing block by the new block. + * + * Existing block is shorter than new block: if use_padding is \c true, + * the function will try and expand the new block into the following + * PADDING block, if it exists and doing so won't shrink the PADDING + * block to less than 4 bytes. If there is no following PADDING + * block, or it will shrink to less than 4 bytes, or use_padding is + * \c false, the entire file is rewritten, replacing the existing block + * with the new block. Note that in this case any following PADDING + * block is preserved as is. + * + * After writing the block, the iterator will remain in the same + * place, i.e. pointing to the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** This is similar to FLAC__metadata_simple_iterator_set_block() + * except that instead of writing over an existing block, it appends + * a block after the existing block. \a use_padding is again used to + * tell the function to try an expand into following padding in an + * attempt to avoid rewriting the entire file. + * + * This function will fail and return \c false if given a STREAMINFO + * block. + * + * After writing the block, the iterator will be pointing to the + * new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block The block to set. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding); + +/** Deletes the block at the current position. This will cause the + * entire FLAC file to be rewritten, unless \a use_padding is \c true, + * in which case the block will be replaced by an equal-sized PADDING + * block. The iterator will be left pointing to the block before the + * one just deleted. + * + * You may not delete the STREAMINFO block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param use_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_simple_iterator_init() + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding); + +/* \} */ + + +/** \defgroup flac_metadata_level2 FLAC/metadata.h: metadata level 2 interface + * \ingroup flac_metadata + * + * \brief + * The level 2 interface provides read-write access to FLAC file metadata; + * all metadata is read into memory, operated on in memory, and then written + * to file, which is more efficient than level 1 when editing multiple blocks. + * + * Currently Ogg FLAC is supported for read only, via + * FLAC__metadata_chain_read_ogg() but a subsequent + * FLAC__metadata_chain_write() will fail. + * + * The general usage of this interface is: + * + * - Create a new chain using FLAC__metadata_chain_new(). A chain is a + * linked list of FLAC metadata blocks. + * - Read all metadata into the chain from a FLAC file using + * FLAC__metadata_chain_read() or FLAC__metadata_chain_read_ogg() and + * check the status. + * - Optionally, consolidate the padding using + * FLAC__metadata_chain_merge_padding() or + * FLAC__metadata_chain_sort_padding(). + * - Create a new iterator using FLAC__metadata_iterator_new() + * - Initialize the iterator to point to the first element in the chain + * using FLAC__metadata_iterator_init() + * - Traverse the chain using FLAC__metadata_iterator_next and + * FLAC__metadata_iterator_prev(). + * - Get a block for reading or modification using + * FLAC__metadata_iterator_get_block(). The pointer to the object + * inside the chain is returned, so the block is yours to modify. + * Changes will be reflected in the FLAC file when you write the + * chain. You can also add and delete blocks (see functions below). + * - When done, write out the chain using FLAC__metadata_chain_write(). + * Make sure to read the whole comment to the function below. + * - Delete the chain using FLAC__metadata_chain_delete(). + * + * \note + * Even though the FLAC file is not open while the chain is being + * manipulated, you must not alter the file externally during + * this time. The chain assumes the FLAC file will not change + * between the time of FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg() + * and FLAC__metadata_chain_write(). + * + * \note + * Do not modify the is_last, length, or type fields of returned + * FLAC__StreamMetadata objects. These are managed automatically. + * + * \note + * The metadata objects returned by FLAC__metadata_iterator_get_block() + * are owned by the chain; do not FLAC__metadata_object_delete() them. + * In the same way, blocks passed to FLAC__metadata_iterator_set_block() + * become owned by the chain and they will be deleted when the chain is + * deleted. + * + * \{ + */ + +struct FLAC__Metadata_Chain; +/** The opaque structure definition for the level 2 chain type. + */ +typedef struct FLAC__Metadata_Chain FLAC__Metadata_Chain; + +struct FLAC__Metadata_Iterator; +/** The opaque structure definition for the level 2 iterator type. + */ +typedef struct FLAC__Metadata_Iterator FLAC__Metadata_Iterator; + +typedef enum { + FLAC__METADATA_CHAIN_STATUS_OK = 0, + /**< The chain is in the normal OK state */ + + FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT, + /**< The data passed into a function violated the function's usage criteria */ + + FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE, + /**< The chain could not open the target file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE, + /**< The chain could not find the FLAC signature at the start of the file */ + + FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE, + /**< The chain tried to write to a file that was not writable */ + + FLAC__METADATA_CHAIN_STATUS_BAD_METADATA, + /**< The chain encountered input that does not conform to the FLAC metadata specification */ + + FLAC__METADATA_CHAIN_STATUS_READ_ERROR, + /**< The chain encountered an error while reading the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR, + /**< The chain encountered an error while seeking in the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR, + /**< The chain encountered an error while writing the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR, + /**< The chain encountered an error renaming the FLAC file */ + + FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR, + /**< The chain encountered an error removing the temporary file */ + + FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR, + /**< Memory allocation failed */ + + FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR, + /**< The caller violated an assertion or an unexpected error occurred */ + + FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS, + /**< One or more of the required callbacks was NULL */ + + FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH, + /**< FLAC__metadata_chain_write() was called on a chain read by + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * or + * FLAC__metadata_chain_write_with_callbacks()/FLAC__metadata_chain_write_with_callbacks_and_tempfile() + * was called on a chain read by + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Matching read/write methods must always be used. */ + + FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL + /**< FLAC__metadata_chain_write_with_callbacks() was called when the + * chain write requires a tempfile; use + * FLAC__metadata_chain_write_with_callbacks_and_tempfile() instead. + * Or, FLAC__metadata_chain_write_with_callbacks_and_tempfile() was + * called when the chain write does not require a tempfile; use + * FLAC__metadata_chain_write_with_callbacks() instead. + * Always check FLAC__metadata_chain_check_if_tempfile_needed() + * before writing via callbacks. */ + +} FLAC__Metadata_ChainStatus; + +/** Maps a FLAC__Metadata_ChainStatus to a C string. + * + * Using a FLAC__Metadata_ChainStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__Metadata_ChainStatusString[]; + +/*********** FLAC__Metadata_Chain ***********/ + +/** Create a new chain instance. + * + * \retval FLAC__Metadata_Chain* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void); + +/** Free a chain instance. Deletes the object pointed to by \a chain. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain); + +/** Get the current status of the chain. Call this after a function + * returns \c false to get the reason for the error. Also resets the + * status to FLAC__METADATA_CHAIN_STATUS_OK. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__Metadata_ChainStatus + * The current status of the chain. + */ +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain); + +/** Read all metadata from a FLAC file into the chain. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from an Ogg FLAC file into the chain. + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param filename The path to the Ogg FLAC file to read. + * \assert + * \code chain != NULL \endcode + * \code filename != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a filename, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename); + +/** Read all metadata from a FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Read all metadata from an Ogg FLAC stream into the chain via I/O callbacks. + * + * The \a handle need only be open for reading, but must be seekable. + * The equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * \note Ogg FLAC metadata data writing is not supported yet and + * FLAC__metadata_chain_write() will fail. + * + * \param chain A pointer to an existing chain. + * \param handle The I/O handle of the Ogg FLAC stream to read. The + * handle will NOT be closed after the metadata is read; + * that is the duty of the caller. + * \param callbacks + * A set of callbacks to use for I/O. The mandatory + * callbacks are \a read, \a seek, and \a tell. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if a valid list of metadata blocks was read from + * \a handle, else \c false. On failure, check the status with + * FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Checks if writing the given chain would require the use of a + * temporary file, or if it could be written in place. + * + * Under certain conditions, padding can be utilized so that writing + * edited metadata back to the FLAC file does not require rewriting the + * entire file. If rewriting is required, then a temporary workfile is + * required. When writing metadata using callbacks, you must check + * this function to know whether to call + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_and_tempfile(). When + * writing with FLAC__metadata_chain_write(), the temporary file is + * handled internally. + * + * \param chain A pointer to an existing chain. + * \param use_padding + * Whether or not padding will be allowed to be used + * during the write. The value of \a use_padding given + * here must match the value later passed to + * FLAC__metadata_chain_write_with_callbacks() or + * FLAC__metadata_chain_write_with_callbacks_with_tempfile(). + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if writing the current chain would require a tempfile, or + * \c false if metadata can be written in place. + */ +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding); + +/** Write all metadata out to the FLAC file. This function tries to be as + * efficient as possible; how the metadata is actually written is shown by + * the following: + * + * If the current chain is the same size as the existing metadata, the new + * data is written in place. + * + * If the current chain is longer than the existing metadata, and + * \a use_padding is \c true, and the last block is a PADDING block of + * sufficient length, the function will truncate the final padding block + * so that the overall size of the metadata is the same as the existing + * metadata, and then just rewrite the metadata. Otherwise, if not all of + * the above conditions are met, the entire FLAC file must be rewritten. + * If you want to use padding this way it is a good idea to call + * FLAC__metadata_chain_sort_padding() first so that you have the maximum + * amount of padding to work with, unless you need to preserve ordering + * of the PADDING blocks for some reason. + * + * If the current chain is shorter than the existing metadata, and + * \a use_padding is \c true, and the final block is a PADDING block, the padding + * is extended to make the overall size the same as the existing data. If + * \a use_padding is \c true and the last block is not a PADDING block, a new + * PADDING block is added to the end of the new data to make it the same + * size as the existing data (if possible, see the note to + * FLAC__metadata_simple_iterator_set_block() about the four byte limit) + * and the new data is written in place. If none of the above apply or + * \a use_padding is \c false, the entire FLAC file is rewritten. + * + * If \a preserve_file_stats is \c true, the owner and modification time will + * be preserved even if the FLAC file is written. + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(), not + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(). + * + * \param chain A pointer to an existing chain. + * \param use_padding See above. + * \param preserve_file_stats See above. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * The \a handle must be open for updating and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r+" (or \c "r+b" + * for Windows). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c false. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O. The mandatory + * callbacks are \a write and \a seek. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks); + +/** Write all metadata out to a FLAC stream via callbacks. + * + * (See FLAC__metadata_chain_write() for the details on how padding is + * used to write metadata in place if possible.) + * + * This version of the write-with-callbacks function must be used when + * FLAC__metadata_chain_check_if_tempfile_needed() returns true. In + * this function, you must supply an I/O handle corresponding to the + * FLAC file to edit, and a temporary handle to which the new FLAC + * file will be written. It is the caller's job to move this temporary + * FLAC file on top of the original FLAC file to complete the metadata + * edit. + * + * The \a handle must be open for reading and be seekable. The + * equivalent minimum stdio fopen() file mode is \c "r" (or \c "rb" + * for Windows). + * + * The \a temp_handle must be open for writing. The + * equivalent minimum stdio fopen() file mode is \c "w" (or \c "wb" + * for Windows). It should be an empty stream, or at least positioned + * at the start-of-file (in which case it is the caller's duty to + * truncate it on return). + * + * For this write function to be used, the chain must have been read with + * FLAC__metadata_chain_read_with_callbacks()/FLAC__metadata_chain_read_ogg_with_callbacks(), + * not FLAC__metadata_chain_read()/FLAC__metadata_chain_read_ogg(). + * Also, FLAC__metadata_chain_check_if_tempfile_needed() must have returned + * \c true. + * + * \param chain A pointer to an existing chain. + * \param use_padding See FLAC__metadata_chain_write() + * \param handle The I/O handle of the original FLAC stream to read. + * The handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param callbacks A set of callbacks to use for I/O on \a handle. + * The mandatory callbacks are \a read, \a seek, and + * \a eof. + * \param temp_handle The I/O handle of the FLAC stream to write. The + * handle will NOT be closed after the metadata is + * written; that is the duty of the caller. + * \param temp_callbacks + * A set of callbacks to use for I/O on temp_handle. + * The only mandatory callback is \a write. + * \assert + * \code chain != NULL \endcode + * \retval FLAC__bool + * \c true if the write succeeded, else \c false. On failure, + * check the status with FLAC__metadata_chain_status(). + */ +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks); + +/** Merge adjacent PADDING blocks into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain); + +/** This function will move all PADDING blocks to the end on the metadata, + * then merge them into a single block. + * + * \note This function does not write to the FLAC file, it only + * modifies the chain. + * + * \warning Any iterator on the current chain will become invalid after this + * call. You should delete the iterator and get a new one. + * + * \param chain A pointer to an existing chain. + * \assert + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain); + + +/*********** FLAC__Metadata_Iterator ***********/ + +/** Create a new iterator instance. + * + * \retval FLAC__Metadata_Iterator* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void); + +/** Free an iterator instance. Deletes the object pointed to by \a iterator. + * + * \param iterator A pointer to an existing iterator. + * \assert + * \code iterator != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator); + +/** Initialize the iterator to point to the first metadata block in the + * given chain. + * + * \param iterator A pointer to an existing iterator. + * \param chain A pointer to an existing and initialized (read) chain. + * \assert + * \code iterator != NULL \endcode + * \code chain != NULL \endcode + */ +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain); + +/** Moves the iterator forward one metadata block, returning \c false if + * already at the end. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the last metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator); + +/** Moves the iterator backward one metadata block, returning \c false if + * already at the beginning. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if already at the first metadata block of the chain, else + * \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator); + +/** Get the type of the metadata block at the current position. + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__MetadataType + * The type of the metadata block at the current iterator position. + */ +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator); + +/** Get the metadata block at the current position. You can modify + * the block in place but must write the chain before the changes + * are reflected to the FLAC file. You do not need to call + * FLAC__metadata_iterator_set_block() to reflect the changes; + * the pointer returned by FLAC__metadata_iterator_get_block() + * points directly into the chain. + * + * \warning + * Do not call FLAC__metadata_object_delete() on the returned object; + * to delete a block use FLAC__metadata_iterator_delete_block(). + * + * \param iterator A pointer to an existing initialized iterator. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__StreamMetadata* + * The current metadata block. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator); + +/** Set the metadata block at the current position, replacing the existing + * block. The new block passed in becomes owned by the chain and it will be + * deleted when the chain is deleted. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \code block != NULL \endcode + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Removes the current block from the chain. If \a replace_with_padding is + * \c true, the block will instead be replaced with a padding block of equal + * size. You can not delete the STREAMINFO block. The iterator will be + * left pointing to the block before the one just "deleted", even if + * \a replace_with_padding is \c true. + * + * \param iterator A pointer to an existing initialized iterator. + * \param replace_with_padding See above. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, + * otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding); + +/** Insert a new block before the current block. You cannot insert a block + * before the first STREAMINFO block. You cannot insert a STREAMINFO block + * as there can be only one, the one that already exists at the head when you + * read in a chain. The chain takes ownership of the new block and it will be + * deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/** Insert a new block after the current block. You cannot insert a STREAMINFO + * block as there can be only one, the one that already exists at the head when + * you read in a chain. The chain takes ownership of the new block and it will + * be deleted when the chain is deleted. The iterator will be left pointing to + * the new block. + * + * \param iterator A pointer to an existing initialized iterator. + * \param block A pointer to a metadata block to insert. + * \assert + * \code iterator != NULL \endcode + * \a iterator has been successfully initialized with + * FLAC__metadata_iterator_init() + * \retval FLAC__bool + * \c false if the conditions in the above description are not met, or + * a memory allocation error occurs, otherwise \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block); + +/* \} */ + + +/** \defgroup flac_metadata_object FLAC/metadata.h: metadata object methods + * \ingroup flac_metadata + * + * \brief + * This module contains methods for manipulating FLAC metadata objects. + * + * Since many are variable length we have to be careful about the memory + * management. We decree that all pointers to data in the object are + * owned by the object and memory-managed by the object. + * + * Use the FLAC__metadata_object_new() and FLAC__metadata_object_delete() + * functions to create all instances. When using the + * FLAC__metadata_object_set_*() functions to set pointers to data, set + * \a copy to \c true to have the function make it's own copy of the data, or + * to \c false to give the object ownership of your data. In the latter case + * your pointer must be freeable by free() and will be free()d when the object + * is FLAC__metadata_object_delete()d. It is legal to pass a null pointer as + * the data pointer to a FLAC__metadata_object_set_*() function as long as + * the length argument is 0 and the \a copy argument is \c false. + * + * The FLAC__metadata_object_new() and FLAC__metadata_object_clone() function + * will return \c NULL in the case of a memory allocation error, otherwise a new + * object. The FLAC__metadata_object_set_*() functions return \c false in the + * case of a memory allocation error. + * + * We don't have the convenience of C++ here, so note that the library relies + * on you to keep the types straight. In other words, if you pass, for + * example, a FLAC__StreamMetadata* that represents a STREAMINFO block to + * FLAC__metadata_object_application_set_data(), you will get an assertion + * failure. + * + * For convenience the FLAC__metadata_object_vorbiscomment_*() functions + * maintain a trailing NUL on each Vorbis comment entry. This is not counted + * toward the length or stored in the stream, but it can make working with plain + * comments (those that don't contain embedded-NULs in the value) easier. + * Entries passed into these functions have trailing NULs added if missing, and + * returned entries are guaranteed to have a trailing NUL. + * + * The FLAC__metadata_object_vorbiscomment_*() functions that take a Vorbis + * comment entry/name/value will first validate that it complies with the Vorbis + * comment specification and return false if it does not. + * + * There is no need to recalculate the length field on metadata blocks you + * have modified. They will be calculated automatically before they are + * written back to a file. + * + * \{ + */ + + +/** Create a new metadata object instance of the given type. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0, + * with the exception of FLAC__METADATA_TYPE_VORBIS_COMMENT, which will have + * the vendor string set (but zero comments). + * + * Do not pass in a value greater than or equal to + * \a FLAC__METADATA_TYPE_UNDEFINED unless you really know what you're + * doing. + * + * \param type Type of object to create + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory or the type code is + * greater than FLAC__MAX_METADATA_TYPE_CODE, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type); + +/** Create a copy of an existing metadata object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new block and + * is responsible for freeing it with FLAC__metadata_object_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object); + +/** Free a metadata object. Deletes the object pointed to by \a object. + * + * The delete is a "deep" delete, i.e. dynamically allocated data within the + * object is also deleted. + * + * \param object A pointer to an existing object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object); + +/** Compares two metadata objects. + * + * The compare is "deep", i.e. dynamically allocated data within the + * object is also compared. + * + * \param block1 A pointer to an existing object. + * \param block2 A pointer to an existing object. + * \assert + * \code block1 != NULL \endcode + * \code block2 != NULL \endcode + * \retval FLAC__bool + * \c true if objects are identical, else \c false. + */ +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2); + +/** Sets the application data of an APPLICATION block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. The existing data will be freed if this + * function is successful, otherwise the original data will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing APPLICATION object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_APPLICATION \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, uint32_t length, FLAC__bool copy); + +/** Resize the seekpoint array. + * + * If the size shrinks, elements will truncated; if it grows, new placeholder + * points will be added to the end. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param new_num_points The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code (object->data.seek_table.points == NULL && object->data.seek_table.num_points == 0) || + * (object->data.seek_table.points != NULL && object->data.seek_table.num_points > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, uint32_t new_num_points); + +/** Set a seekpoint in a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + */ +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Insert a seekpoint into a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \param point The point to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points >= point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point); + +/** Delete a seekpoint from a seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param point_num Index into seekpoint array to set. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code object->data.seek_table.num_points > point_num \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, uint32_t point_num); + +/** Check a seektable to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * seektable. + * + * \param object A pointer to an existing SEEKTABLE object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if seek table is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object); + +/** Append a number of placeholder points to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, uint32_t num); + +/** Append a specific seek point template to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_number The sample number of the seek point template. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number); + +/** Append specific seek point templates to the end of a seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param sample_numbers An array of sample numbers for the seek points. + * \param num The number of seek point templates to append. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], uint32_t num); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param num The number of placeholder points to append. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced approximately + * \a total_samples / \a num samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, uint32_t num, FLAC__uint64 total_samples); + +/** Append a set of evenly-spaced seek point templates to the end of a + * seek table. + * + * \note + * As with the other ..._seektable_template_... functions, you should + * call FLAC__metadata_object_seektable_template_sort() when finished + * to make the seek table legal. + * + * \param object A pointer to an existing SEEKTABLE object. + * \param samples The number of samples apart to space the placeholder + * points. The first point will be at sample \c 0, the + * second at sample \a samples, then 2*\a samples, and + * so on. As long as \a samples and \a total_samples + * are greater than \c 0, there will always be at least + * one seekpoint at sample \c 0. + * \param total_samples The total number of samples to be encoded; + * the seekpoints will be spaced + * \a samples samples apart. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \code samples > 0 \endcode + * \code total_samples > 0 \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, uint32_t samples, FLAC__uint64 total_samples); + +/** Sort a seek table's seek points according to the format specification, + * removing duplicates. + * + * \param object A pointer to a seek table to be sorted. + * \param compact If \c false, behaves like FLAC__format_seektable_sort(). + * If \c true, duplicates are deleted and the seek table is + * shrunk appropriately; the number of placeholder points + * present in the seek table will be the same after the call + * as before. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_SEEKTABLE \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact); + +/** Sets the vendor string in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The entry to set the vendor string to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Resize the comment array. + * + * If the size shrinks, elements will truncated; if it grows, new empty + * fields will be added to the end. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param new_num_comments The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (object->data.vorbis_comment.comments == NULL && object->data.vorbis_comment.num_comments == 0) || + * (object->data.vorbis_comment.comments != NULL && object->data.vorbis_comment.num_comments > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, uint32_t new_num_comments); + +/** Sets a comment in a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num Index into comment array to set. + * \param entry The entry to set the comment to. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code comment_num < object->data.vorbis_comment.num_comments \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Insert a comment in a VORBIS_COMMENT block at the given index. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index at which to insert the comment. The comments + * at and after \a comment_num move right one position. + * To append a comment to the end, set \a comment_num to + * \c object->data.vorbis_comment.num_comments . + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments >= comment_num \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Appends a comment to a VORBIS_COMMENT block. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy); + +/** Replaces comments in a VORBIS_COMMENT block with a new one. + * + * For convenience, a trailing NUL is added to the entry if it doesn't have + * one already. + * + * Depending on the value of \a all, either all or just the first comment + * whose field name(s) match the given entry's name will be replaced by the + * given entry. If no comments match, \a entry will simply be appended. + * + * If \a copy is \c true, a copy of the entry is stored; otherwise, the object + * takes ownership of the \c entry.entry pointer. + * + * \note If this function returns \c false, the caller still owns the + * pointer. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param entry The comment to insert. + * \param all If \c true, all comments whose field name matches + * \a entry's field name will be removed, and \a entry will + * be inserted at the position of the first matching + * comment. If \c false, only the first comment whose + * field name matches \a entry's field name will be + * replaced with \a entry. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code (entry.entry != NULL && entry.length > 0) || + * (entry.entry == NULL && entry.length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy); + +/** Delete a comment in a VORBIS_COMMENT block at the given index. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param comment_num The index of the comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code object->data.vorbis_comment.num_comments > comment_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, uint32_t comment_num); + +/** Creates a Vorbis comment entry from NUL-terminated name and value strings. + * + * On return, the filled-in \a entry->entry pointer will point to malloc()ed + * memory and shall be owned by the caller. For convenience the entry will + * have a terminating NUL. + * + * \param entry A pointer to a Vorbis comment entry. The entry's + * \c entry pointer should not point to allocated + * memory as it will be overwritten. + * \param field_name The field name in ASCII, \c NUL terminated. + * \param field_value The field value in UTF-8, \c NUL terminated. + * \assert + * \code entry != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if malloc() fails, or if \a field_name or \a field_value does + * not comply with the Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value); + +/** Splits a Vorbis comment entry into NUL-terminated name and value strings. + * + * The returned pointers to name and value will be allocated by malloc() + * and shall be owned by the caller. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The address of where the returned pointer to the + * field name will be stored. + * \param field_value The address of where the returned pointer to the + * field value will be stored. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \code memchr(entry.entry, '=', entry.length) != NULL \endcode + * \code field_name != NULL \endcode + * \code field_value != NULL \endcode + * \retval FLAC__bool + * \c false if memory allocation fails or \a entry does not comply with the + * Vorbis comment specification, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value); + +/** Check if the given Vorbis comment entry's field name matches the given + * field name. + * + * \param entry An existing Vorbis comment entry. + * \param field_name The field name to check. + * \param field_name_length The length of \a field_name, not including the + * terminating \c NUL. + * \assert + * \code (entry.entry != NULL && entry.length > 0) \endcode + * \retval FLAC__bool + * \c true if the field names match, else \c false + */ +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, uint32_t field_name_length); + +/** Find a Vorbis comment with the given field name. + * + * The search begins at entry number \a offset; use an offset of 0 to + * search from the beginning of the comment array. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param offset The offset into the comment array from where to start + * the search. + * \param field_name The field name of the comment to find. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \code field_name != NULL \endcode + * \retval int + * The offset in the comment array of the first comment whose field + * name matches \a field_name, or \c -1 if no match was found. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name); + +/** Remove first Vorbis comment matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comment to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * \c 1 for one matching entry deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Remove all Vorbis comments matching the given field name. + * + * \param object A pointer to an existing VORBIS_COMMENT object. + * \param field_name The field name of comments to delete. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT \endcode + * \retval int + * \c -1 for memory allocation error, \c 0 for no matching entries, + * else the number of matching entries deleted. + */ +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name); + +/** Create a new CUESHEET track instance. + * + * The object will be "empty"; i.e. values and data pointers will be \c 0. + * + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void); + +/** Create a copy of an existing CUESHEET track object. + * + * The copy is a "deep" copy, i.e. dynamically allocated data within the + * object is also copied. The caller takes ownership of the new object and + * is responsible for freeing it with + * FLAC__metadata_object_cuesheet_track_delete(). + * + * \param object Pointer to object to copy. + * \assert + * \code object != NULL \endcode + * \retval FLAC__StreamMetadata_CueSheet_Track* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object); + +/** Delete a CUESHEET track object + * + * \param object A pointer to an existing CUESHEET track object. + * \assert + * \code object != NULL \endcode + */ +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object); + +/** Resize a track's index point array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * indices will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param new_num_indices The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code (object->data.cue_sheet.tracks[track_num].indices == NULL && object->data.cue_sheet.tracks[track_num].num_indices == 0) || + * (object->data.cue_sheet.tracks[track_num].indices != NULL && object->data.cue_sheet.tracks[track_num].num_indices > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t new_num_indices); + +/** Insert an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \param index The index point to insert. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num, FLAC__StreamMetadata_CueSheet_Index index); + +/** Insert a blank index point in a CUESHEET track at the given index. + * + * A blank index point is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index of the track to modify. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param index_num The index into the track's index array at which to + * insert the index point. NOTE: this is not necessarily + * the same as the index point's \a number field. The + * indices at and after \a index_num move right one + * position. To append an index point to the end, set + * \a index_num to + * \c object->data.cue_sheet.tracks[track_num].num_indices . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices >= index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num); + +/** Delete an index point in a CUESHEET track at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * modify. NOTE: this is not necessarily the same + * as the track's \a number field. + * \param index_num The index into the track's index array of the index + * to delete. NOTE: this is not necessarily the same + * as the index's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \code object->data.cue_sheet.tracks[track_num].num_indices > index_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num); + +/** Resize the track array. + * + * If the size shrinks, elements will truncated; if it grows, new blank + * tracks will be added to the end. + * + * \param object A pointer to an existing CUESHEET object. + * \param new_num_tracks The desired length of the array; may be \c 0. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code (object->data.cue_sheet.tracks == NULL && object->data.cue_sheet.num_tracks == 0) || + * (object->data.cue_sheet.tracks != NULL && object->data.cue_sheet.num_tracks > 0) \endcode + * \retval FLAC__bool + * \c false if memory allocation error, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, uint32_t new_num_tracks); + +/** Sets a track in a CUESHEET block. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num Index into track array to set. NOTE: this is not + * necessarily the same as the track's \a number field. + * \param track The track to set the track to. You may safely pass in + * a const pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code track_num < object->data.cue_sheet.num_tracks \endcode + * \code (track->indices != NULL && track->num_indices > 0) || + * (track->indices == NULL && track->num_indices == 0) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a track in a CUESHEET block at the given index. + * + * If \a copy is \c true, a copy of the track is stored; otherwise, the object + * takes ownership of the \a track pointer. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \param track The track to insert. You may safely pass in a const + * pointer if \a copy is \c true. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy); + +/** Insert a blank track in a CUESHEET block at the given index. + * + * A blank track is one in which all field values are zero. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index at which to insert the track. NOTE: this + * is not necessarily the same as the track's \a number + * field. The tracks at and after \a track_num move right + * one position. To append a track to the end, set + * \a track_num to \c object->data.cue_sheet.num_tracks . + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks >= track_num \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, uint32_t track_num); + +/** Delete a track in a CUESHEET block at the given index. + * + * \param object A pointer to an existing CUESHEET object. + * \param track_num The index into the track array of the track to + * delete. NOTE: this is not necessarily the same + * as the track's \a number field. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \code object->data.cue_sheet.num_tracks > track_num \endcode + * \retval FLAC__bool + * \c false if realloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, uint32_t track_num); + +/** Check a cue sheet to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * cue sheet. + * + * \param object A pointer to an existing CUESHEET object. + * \param check_cd_da_subset If \c true, check CUESHEET against more + * stringent requirements for a CD-DA (audio) disc. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__bool + * \c false if cue sheet is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation); + +/** Calculate and return the CDDB/freedb ID for a cue sheet. The function + * assumes the cue sheet corresponds to a CD; the result is undefined + * if the cuesheet's is_cd bit is not set. + * + * \param object A pointer to an existing CUESHEET object. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_CUESHEET \endcode + * \retval FLAC__uint32 + * The unsigned integer representation of the CDDB/freedb ID + */ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object); + +/** Sets the MIME type of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a mime_type if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param mime_type A pointer to the MIME type string. The string must be + * ASCII characters 0x20-0x7e, NUL-terminated. No validation + * is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (mime_type != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy); + +/** Sets the description of a PICTURE block. + * + * If \a copy is \c true, a copy of the string is stored; otherwise, the object + * takes ownership of the pointer. The existing string will be freed if this + * function is successful, otherwise the original string will remain if \a copy + * is \c true and malloc() fails. + * + * \note It is safe to pass a const pointer to \a description if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param description A pointer to the description string. The string must be + * valid UTF-8, NUL-terminated. No validation is done. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (description != NULL) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy); + +/** Sets the picture data of a PICTURE block. + * + * If \a copy is \c true, a copy of the data is stored; otherwise, the object + * takes ownership of the pointer. Also sets the \a data_length field of the + * metadata object to what is passed in as the \a length parameter. The + * existing data will be freed if this function is successful, otherwise the + * original data and data_length will remain if \a copy is \c true and + * malloc() fails. + * + * \note It is safe to pass a const pointer to \a data if \a copy is \c true. + * + * \param object A pointer to an existing PICTURE object. + * \param data A pointer to the data to set. + * \param length The length of \a data in bytes. + * \param copy See above. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \code (data != NULL && length > 0) || + * (data == NULL && length == 0 && copy == false) \endcode + * \retval FLAC__bool + * \c false if \a copy is \c true and malloc() fails, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy); + +/** Check a PICTURE block to see if it conforms to the FLAC specification. + * See the format specification for limits on the contents of the + * PICTURE block. + * + * \param object A pointer to existing PICTURE block to be checked. + * \param violation Address of a pointer to a string. If there is a + * violation, a pointer to a string explanation of the + * violation will be returned here. \a violation may be + * \c NULL if you don't need the returned string. Do not + * free the returned string; it will always point to static + * data. + * \assert + * \code object != NULL \endcode + * \code object->type == FLAC__METADATA_TYPE_PICTURE \endcode + * \retval FLAC__bool + * \c false if PICTURE block is illegal, else \c true. + */ +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libflac/FLAC/ordinals.h b/src/libflac/FLAC/ordinals.h @@ -0,0 +1,85 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__ORDINALS_H +#define FLAC__ORDINALS_H + +#if defined(_MSC_VER) && _MSC_VER < 1600 + +/* Microsoft Visual Studio earlier than the 2010 version did not provide + * the 1999 ISO C Standard header file <stdint.h>. + */ + +typedef signed __int8 FLAC__int8; +typedef signed __int16 FLAC__int16; +typedef signed __int32 FLAC__int32; +typedef signed __int64 FLAC__int64; +typedef unsigned __int8 FLAC__uint8; +typedef unsigned __int16 FLAC__uint16; +typedef unsigned __int32 FLAC__uint32; +typedef unsigned __int64 FLAC__uint64; + +#else + +/* For MSVC 2010 and everything else which provides <stdint.h>. */ + +#include <stdint.h> + +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + +typedef int16_t FLAC__int16; +typedef int32_t FLAC__int32; +typedef int64_t FLAC__int64; +typedef uint16_t FLAC__uint16; +typedef uint32_t FLAC__uint32; +typedef uint64_t FLAC__uint64; + +#endif + +typedef int FLAC__bool; + +typedef FLAC__uint8 FLAC__byte; + + +#ifdef true +#undef true +#endif +#ifdef false +#undef false +#endif +#ifndef __cplusplus +#define true 1 +#define false 0 +#endif + +#endif diff --git a/src/libflac/FLAC/stream_decoder.h b/src/libflac/FLAC/stream_decoder.h @@ -0,0 +1,1559 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__STREAM_DECODER_H +#define FLAC__STREAM_DECODER_H + +#include <stdio.h> /* for FILE */ +#include "export.h" +#include "format.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file include/FLAC/stream_decoder.h + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * See the detailed documentation in the + * \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_decoder FLAC/ \*_decoder.h: decoder interfaces + * \ingroup flac + * + * \brief + * This module describes the decoder layers provided by libFLAC. + * + * The stream decoder can be used to decode complete streams either from + * the client via callbacks, or directly from a file, depending on how + * it is initialized. When decoding via callbacks, the client provides + * callbacks for reading FLAC data and writing decoded samples, and + * handling metadata and errors. If the client also supplies seek-related + * callback, the decoder function for sample-accurate seeking within the + * FLAC input is also available. When decoding from a file, the client + * needs only supply a filename or open \c FILE* and write/metadata/error + * callbacks; the rest of the callbacks are supplied internally. For more + * info see the \link flac_stream_decoder stream decoder \endlink module. + */ + +/** \defgroup flac_stream_decoder FLAC/stream_decoder.h: stream decoder interface + * \ingroup flac_decoder + * + * \brief + * This module contains the functions which implement the stream + * decoder. + * + * The stream decoder can decode native FLAC, and optionally Ogg FLAC + * (check FLAC_API_SUPPORTS_OGG_FLAC) streams and files. + * + * The basic usage of this decoder is as follows: + * - The program creates an instance of a decoder using + * FLAC__stream_decoder_new(). + * - The program overrides the default settings using + * FLAC__stream_decoder_set_*() functions. + * - The program initializes the instance to validate the settings and + * prepare for decoding using + * - FLAC__stream_decoder_init_stream() or FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file() for native FLAC, + * - FLAC__stream_decoder_init_ogg_stream() or FLAC__stream_decoder_init_ogg_FILE() + * or FLAC__stream_decoder_init_ogg_file() for Ogg FLAC + * - The program calls the FLAC__stream_decoder_process_*() functions + * to decode data, which subsequently calls the callbacks. + * - The program finishes the decoding with FLAC__stream_decoder_finish(), + * which flushes the input and output and resets the decoder to the + * uninitialized state. + * - The instance may be used again or deleted with + * FLAC__stream_decoder_delete(). + * + * In more detail, the program will create a new instance by calling + * FLAC__stream_decoder_new(), then call FLAC__stream_decoder_set_*() + * functions to override the default decoder options, and call + * one of the FLAC__stream_decoder_init_*() functions. + * + * There are three initialization functions for native FLAC, one for + * setting up the decoder to decode FLAC data from the client via + * callbacks, and two for decoding directly from a FLAC file. + * + * For decoding via callbacks, use FLAC__stream_decoder_init_stream(). + * You must also supply several callbacks for handling I/O. Some (like + * seeking) are optional, depending on the capabilities of the input. + * + * For decoding directly from a file, use FLAC__stream_decoder_init_FILE() + * or FLAC__stream_decoder_init_file(). Then you must only supply an open + * \c FILE* or filename and fewer callbacks; the decoder will handle + * the other callbacks internally. + * + * There are three similarly-named init functions for decoding from Ogg + * FLAC streams. Check \c FLAC_API_SUPPORTS_OGG_FLAC to find out if the + * library has been built with Ogg support. + * + * Once the decoder is initialized, your program will call one of several + * functions to start the decoding process: + * + * - FLAC__stream_decoder_process_single() - Tells the decoder to process at + * most one metadata block or audio frame and return, calling either the + * metadata callback or write callback, respectively, once. If the decoder + * loses sync it will return with only the error callback being called. + * - FLAC__stream_decoder_process_until_end_of_metadata() - Tells the decoder + * to process the stream from the current location and stop upon reaching + * the first audio frame. The client will get one metadata, write, or error + * callback per metadata block, audio frame, or sync error, respectively. + * - FLAC__stream_decoder_process_until_end_of_stream() - Tells the decoder + * to process the stream from the current location until the read callback + * returns FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM or + * FLAC__STREAM_DECODER_READ_STATUS_ABORT. The client will get one metadata, + * write, or error callback per metadata block, audio frame, or sync error, + * respectively. + * + * When the decoder has finished decoding (normally or through an abort), + * the instance is finished by calling FLAC__stream_decoder_finish(), which + * ensures the decoder is in the correct state and frees memory. Then the + * instance may be deleted with FLAC__stream_decoder_delete() or initialized + * again to decode another stream. + * + * Seeking is exposed through the FLAC__stream_decoder_seek_absolute() method. + * At any point after the stream decoder has been initialized, the client can + * call this function to seek to an exact sample within the stream. + * Subsequently, the first time the write callback is called it will be + * passed a (possibly partial) block starting at that sample. + * + * If the client cannot seek via the callback interface provided, but still + * has another way of seeking, it can flush the decoder using + * FLAC__stream_decoder_flush() and start feeding data from the new position + * through the read callback. + * + * The stream decoder also provides MD5 signature checking. If this is + * turned on before initialization, FLAC__stream_decoder_finish() will + * report when the decoded MD5 signature does not match the one stored + * in the STREAMINFO block. MD5 checking is automatically turned off + * (until the next FLAC__stream_decoder_reset()) if there is no signature + * in the STREAMINFO block or when a seek is attempted. + * + * The FLAC__stream_decoder_set_metadata_*() functions deserve special + * attention. By default, the decoder only calls the metadata_callback for + * the STREAMINFO block. These functions allow you to tell the decoder + * explicitly which blocks to parse and return via the metadata_callback + * and/or which to skip. Use a FLAC__stream_decoder_set_metadata_respond_all(), + * FLAC__stream_decoder_set_metadata_ignore() ... or FLAC__stream_decoder_set_metadata_ignore_all(), + * FLAC__stream_decoder_set_metadata_respond() ... sequence to exactly specify + * which blocks to return. Remember that metadata blocks can potentially + * be big (for example, cover art) so filtering out the ones you don't + * use can reduce the memory requirements of the decoder. Also note the + * special forms FLAC__stream_decoder_set_metadata_respond_application(id) + * and FLAC__stream_decoder_set_metadata_ignore_application(id) for + * filtering APPLICATION blocks based on the application ID. + * + * STREAMINFO and SEEKTABLE blocks are always parsed and used internally, but + * they still can legally be filtered from the metadata_callback. + * + * \note + * The "set" functions may only be called when the decoder is in the + * state FLAC__STREAM_DECODER_UNINITIALIZED, i.e. after + * FLAC__stream_decoder_new() or FLAC__stream_decoder_finish(), but + * before FLAC__stream_decoder_init_*(). If this is the case they will + * return \c true, otherwise \c false. + * + * \note + * FLAC__stream_decoder_finish() resets all settings to the constructor + * defaults, including the callbacks. + * + * \{ + */ + + +/** State values for a FLAC__StreamDecoder + * + * The decoder's state can be obtained by calling FLAC__stream_decoder_get_state(). + */ +typedef enum { + + FLAC__STREAM_DECODER_SEARCH_FOR_METADATA = 0, + /**< The decoder is ready to search for metadata. */ + + FLAC__STREAM_DECODER_READ_METADATA, + /**< The decoder is ready to or is in the process of reading metadata. */ + + FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC, + /**< The decoder is ready to or is in the process of searching for the + * frame sync code. + */ + + FLAC__STREAM_DECODER_READ_FRAME, + /**< The decoder is ready to or is in the process of reading a frame. */ + + FLAC__STREAM_DECODER_END_OF_STREAM, + /**< The decoder has reached the end of the stream. */ + + FLAC__STREAM_DECODER_OGG_ERROR, + /**< An error occurred in the underlying Ogg layer. */ + + FLAC__STREAM_DECODER_SEEK_ERROR, + /**< An error occurred while seeking. The decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + */ + + FLAC__STREAM_DECODER_ABORTED, + /**< The decoder was aborted by the read or write callback. */ + + FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. The decoder is in an invalid + * state and can no longer be used. + */ + + FLAC__STREAM_DECODER_UNINITIALIZED + /**< The decoder is in the uninitialized state; one of the + * FLAC__stream_decoder_init_*() functions must be called before samples + * can be processed. + */ + +} FLAC__StreamDecoderState; + +/** Maps a FLAC__StreamDecoderState to a C string. + * + * Using a FLAC__StreamDecoderState as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderStateString[]; + + +/** Possible return values for the FLAC__stream_decoder_init_*() functions. + */ +typedef enum { + + FLAC__STREAM_DECODER_INIT_STATUS_OK = 0, + /**< Initialization was successful. */ + + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, + /**< The library was not compiled with support for the given container + * format. + */ + + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + /**< A required callback was not supplied. */ + + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + /**< An error occurred allocating memory. */ + + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + /**< fopen() failed in FLAC__stream_decoder_init_file() or + * FLAC__stream_decoder_init_ogg_file(). */ + + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED + /**< FLAC__stream_decoder_init_*() was called when the decoder was + * already initialized, usually because + * FLAC__stream_decoder_finish() was not called. + */ + +} FLAC__StreamDecoderInitStatus; + +/** Maps a FLAC__StreamDecoderInitStatus to a C string. + * + * Using a FLAC__StreamDecoderInitStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderInitStatusString[]; + + +/** Return values for the FLAC__StreamDecoder read callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, + /**< The read was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM, + /**< The read was attempted while at the end of the stream. Note that + * the client must only return this value when the read callback was + * called when already at the end of the stream. Otherwise, if the read + * itself moves to the end of the stream, the client should still return + * the data and \c FLAC__STREAM_DECODER_READ_STATUS_CONTINUE, and then on + * the next read callback it should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM with a byte count + * of \c 0. + */ + + FLAC__STREAM_DECODER_READ_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderReadStatus; + +/** Maps a FLAC__StreamDecoderReadStatus to a C string. + * + * Using a FLAC__StreamDecoderReadStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderReadStatusString[]; + + +/** Return values for the FLAC__StreamDecoder seek callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_SEEK_STATUS_OK, + /**< The seek was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + /**< Client does not support seeking. */ + +} FLAC__StreamDecoderSeekStatus; + +/** Maps a FLAC__StreamDecoderSeekStatus to a C string. + * + * Using a FLAC__StreamDecoderSeekStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[]; + + +/** Return values for the FLAC__StreamDecoder tell callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_TELL_STATUS_OK, + /**< The tell was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_TELL_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + /**< Client does not support telling the position. */ + +} FLAC__StreamDecoderTellStatus; + +/** Maps a FLAC__StreamDecoderTellStatus to a C string. + * + * Using a FLAC__StreamDecoderTellStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderTellStatusString[]; + + +/** Return values for the FLAC__StreamDecoder length callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_LENGTH_STATUS_OK, + /**< The length call was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR, + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + + FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + /**< Client does not support reporting the length. */ + +} FLAC__StreamDecoderLengthStatus; + +/** Maps a FLAC__StreamDecoderLengthStatus to a C string. + * + * Using a FLAC__StreamDecoderLengthStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[]; + + +/** Return values for the FLAC__StreamDecoder write callback. + */ +typedef enum { + + FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE, + /**< The write was OK and decoding can continue. */ + + FLAC__STREAM_DECODER_WRITE_STATUS_ABORT + /**< An unrecoverable error occurred. The decoder will return from the process call. */ + +} FLAC__StreamDecoderWriteStatus; + +/** Maps a FLAC__StreamDecoderWriteStatus to a C string. + * + * Using a FLAC__StreamDecoderWriteStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[]; + + +/** Possible values passed back to the FLAC__StreamDecoder error callback. + * \c FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC is the generic catch- + * all. The rest could be caused by bad sync (false synchronization on + * data that is not the start of a frame) or corrupted data. The error + * itself is the decoder's best guess at what happened assuming a correct + * sync. For example \c FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER + * could be caused by a correct sync on the start of a frame, but some + * data in the frame header was corrupted. Or it could be the result of + * syncing on a point the stream that looked like the starting of a frame + * but was not. \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + * could be because the decoder encountered a valid frame made by a future + * version of the encoder which it cannot parse, or because of a false + * sync making it appear as though an encountered frame was generated by + * a future encoder. + */ +typedef enum { + + FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC, + /**< An error in the stream caused the decoder to lose synchronization. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER, + /**< The decoder encountered a corrupted frame header. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH, + /**< The frame's data did not match the CRC in the footer. */ + + FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM + /**< The decoder encountered reserved fields in use in the stream. */ + +} FLAC__StreamDecoderErrorStatus; + +/** Maps a FLAC__StreamDecoderErrorStatus to a C string. + * + * Using a FLAC__StreamDecoderErrorStatus as the index to this array + * will give the string equivalent. The contents should not be modified. + */ +extern FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[]; + + +/*********************************************************************** + * + * class FLAC__StreamDecoder + * + ***********************************************************************/ + +struct FLAC__StreamDecoderProtected; +struct FLAC__StreamDecoderPrivate; +/** The opaque structure definition for the stream decoder type. + * See the \link flac_stream_decoder stream decoder module \endlink + * for a detailed description. + */ +typedef struct { + struct FLAC__StreamDecoderProtected *protected_; /* avoid the C++ keyword 'protected' */ + struct FLAC__StreamDecoderPrivate *private_; /* avoid the C++ keyword 'private' */ +} FLAC__StreamDecoder; + +/** Signature for the read callback. + * + * A function pointer matching this signature must be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs more input data. The address of the + * buffer to be filled is supplied, along with the number of bytes the + * buffer can hold. The callback may choose to supply less data and + * modify the byte count but must be careful not to overflow the buffer. + * The callback then returns a status code chosen from + * FLAC__StreamDecoderReadStatus. + * + * Here is an example of a read callback for stdio streams: + * \code + * FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(*bytes > 0) { + * *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, file); + * if(ferror(file)) + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * else if(*bytes == 0) + * return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + * else + * return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + * } + * else + * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param buffer A pointer to a location for the callee to store + * data to be decoded. + * \param bytes A pointer to the size of the buffer. On entry + * to the callback, it contains the maximum number + * of bytes that may be stored in \a buffer. The + * callee must set it to the actual number of bytes + * stored (0 in case of error or end-of-stream) before + * returning. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderReadStatus + * The callee's return status. Note that the callback should return + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM if and only if + * zero bytes were read and there is no more data to be read. + */ +typedef FLAC__StreamDecoderReadStatus (*FLAC__StreamDecoderReadCallback)(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); + +/** Signature for the seek callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to seek the input stream. The decoder + * will pass the absolute byte offset to seek to, 0 meaning the + * beginning of the stream. + * + * Here is an example of a seek callback for stdio streams: + * \code + * FLAC__StreamDecoderSeekStatus seek_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * if(file == stdin) + * return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + * else if(fseeko(file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + * return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + * else + * return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset The offset from the beginning of the stream + * to seek to. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderSeekStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderSeekStatus (*FLAC__StreamDecoderSeekCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); + +/** Signature for the tell callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the current position of the + * stream. The callback should return the byte offset from the + * beginning of the stream. + * + * Here is an example of a tell callback for stdio streams: + * \code + * FLAC__StreamDecoderTellStatus tell_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * off_t pos; + * if(file == stdin) + * return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + * else if((pos = ftello(file)) < 0) + * return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + * else { + * *absolute_byte_offset = (FLAC__uint64)pos; + * return FLAC__STREAM_DECODER_TELL_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param absolute_byte_offset A pointer to storage for the current offset + * from the beginning of the stream. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderTellStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderTellStatus (*FLAC__StreamDecoderTellCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); + +/** Signature for the length callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder wants to know the total length of the stream + * in bytes. + * + * Here is an example of a length callback for stdio streams: + * \code + * FLAC__StreamDecoderLengthStatus length_cb(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) + * { + * FILE *file = ((MyClientData*)client_data)->file; + * struct stat filestats; + * + * if(file == stdin) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + * else if(fstat(fileno(file), &filestats) != 0) + * return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + * else { + * *stream_length = (FLAC__uint64)filestats.st_size; + * return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + * } + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param stream_length A pointer to storage for the length of the stream + * in bytes. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderLengthStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderLengthStatus (*FLAC__StreamDecoderLengthCallback)(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); + +/** Signature for the EOF callback. + * + * A function pointer matching this signature may be passed to + * FLAC__stream_decoder_init*_stream(). The supplied function will be + * called when the decoder needs to know if the end of the stream has + * been reached. + * + * Here is an example of a EOF callback for stdio streams: + * FLAC__bool eof_cb(const FLAC__StreamDecoder *decoder, void *client_data) + * \code + * { + * FILE *file = ((MyClientData*)client_data)->file; + * return feof(file)? true : false; + * } + * \endcode + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__bool + * \c true if the currently at the end of the stream, else \c false. + */ +typedef FLAC__bool (*FLAC__StreamDecoderEofCallback)(const FLAC__StreamDecoder *decoder, void *client_data); + +/** Signature for the write callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * single audio frame. The decoder will pass the frame metadata as well + * as an array of pointers (one for each channel) pointing to the + * decoded audio. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param frame The description of the decoded frame. See + * FLAC__Frame. + * \param buffer An array of pointers to decoded channels of data. + * Each pointer will point to an array of signed + * samples of length \a frame->header.blocksize. + * Channels will be ordered according to the FLAC + * specification; see the documentation for the + * <A HREF="../format.html#frame_header">frame header</A>. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + * \retval FLAC__StreamDecoderWriteStatus + * The callee's return status. + */ +typedef FLAC__StreamDecoderWriteStatus (*FLAC__StreamDecoderWriteCallback)(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + +/** Signature for the metadata callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called when the decoder has decoded a + * metadata block. In a valid FLAC file there will always be one + * \c STREAMINFO block, followed by zero or more other metadata blocks. + * These will be supplied by the decoder in the same order as they + * appear in the stream and always before the first audio frame (i.e. + * write callback). The metadata block that is passed in must not be + * modified, and it doesn't live beyond the callback, so you should make + * a copy of it with FLAC__metadata_object_clone() if you will need it + * elsewhere. Since metadata blocks can potentially be large, by + * default the decoder only calls the metadata callback for the + * \c STREAMINFO block; you can instruct the decoder to pass or filter + * other blocks with FLAC__stream_decoder_set_metadata_*() calls. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param metadata The decoded metadata block. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderMetadataCallback)(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + +/** Signature for the error callback. + * + * A function pointer matching this signature must be passed to one of + * the FLAC__stream_decoder_init_*() functions. + * The supplied function will be called whenever an error occurs during + * decoding. + * + * \note In general, FLAC__StreamDecoder functions which change the + * state should not be called on the \a decoder while in the callback. + * + * \param decoder The decoder instance calling the callback. + * \param status The error encountered by the decoder. + * \param client_data The callee's client data set through + * FLAC__stream_decoder_init_*(). + */ +typedef void (*FLAC__StreamDecoderErrorCallback)(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +/** Create a new stream decoder instance. The instance is created with + * default settings; see the individual FLAC__stream_decoder_set_*() + * functions for each setting's default. + * + * \retval FLAC__StreamDecoder* + * \c NULL if there was an error allocating memory, else the new instance. + */ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void); + +/** Free a decoder instance. Deletes the object pointed to by \a decoder. + * + * \param decoder A pointer to an existing decoder. + * \assert + * \code decoder != NULL \endcode + */ +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder); + + +/*********************************************************************** + * + * Public class method prototypes + * + ***********************************************************************/ + +/** Set the serial number for the FLAC stream within the Ogg container. + * The default behavior is to use the serial number of the first Ogg + * page. Setting a serial number here will explicitly specify which + * stream is to be decoded. + * + * \note + * This does not need to be set for native FLAC decoding. + * + * \default \c use serial number of first page + * \param decoder A decoder instance to set. + * \param serial_number See above. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number); + +/** Set the "MD5 signature checking" flag. If \c true, the decoder will + * compute the MD5 signature of the unencoded audio data while decoding + * and compare it to the signature from the STREAMINFO block, if it + * exists, during FLAC__stream_decoder_finish(). + * + * MD5 signature checking will be turned off (until the next + * FLAC__stream_decoder_reset()) if there is no signature in the + * STREAMINFO block or when a seek is attempted. + * + * Clients that do not use the MD5 check should leave this off to speed + * up decoding. + * + * \default \c false + * \param decoder A decoder instance to set. + * \param value Flag value (see above). + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value); + +/** Direct the decoder to pass on all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to pass on all APPLICATION metadata blocks of the + * given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to pass on all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder); + +/** Direct the decoder to filter out all metadata blocks of type \a type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param type See above. + * \assert + * \code decoder != NULL \endcode + * \a type is valid + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type); + +/** Direct the decoder to filter out all APPLICATION metadata blocks of + * the given \a id. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \param id See above. + * \assert + * \code decoder != NULL \endcode + * \code id != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]); + +/** Direct the decoder to filter out all metadata blocks of any type. + * + * \default By default, only the \c STREAMINFO block is returned via the + * metadata callback. + * \param decoder A decoder instance to set. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if the decoder is already initialized, else \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder); + +/** Get the current decoder state. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderState + * The current decoder state. + */ +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder); + +/** Get the current decoder state as a C string. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval const char * + * The decoder state as a C string. Do not modify the contents. + */ +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder); + +/** Get the "MD5 signature checking" flag. + * This is the value of the setting, not whether or not the decoder is + * currently checking the MD5 (remember, it can be turned off automatically + * by a seek). When the decoder is reset the flag will be restored to the + * value returned by this function. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * See above. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder); + +/** Get the total number of samples in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the \c STREAMINFO block. A value of \c 0 means "unknown". + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder); + +/** Get the current number of channels in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder); + +/** Get the current channel assignment in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__ChannelAssignment + * See above. + */ +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder); + +/** Get the current sample resolution in the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder); + +/** Get the current sample rate in Hz of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder); + +/** Get the current blocksize of the stream being decoded. + * Will only be valid after decoding has started and will contain the + * value from the most recently decoded frame header. + * + * \param decoder A decoder instance to query. + * \assert + * \code decoder != NULL \endcode + * \retval uint32_t + * See above. + */ +FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder); + +/** Returns the decoder's current read position within the stream. + * The position is the byte offset from the start of the stream. + * Bytes before this position have been fully decoded. Note that + * there may still be undecoded bytes in the decoder's read FIFO. + * The returned position is correct even after a seek. + * + * \warning This function currently only works for native FLAC, + * not Ogg FLAC streams. + * + * \param decoder A decoder instance to query. + * \param position Address at which to return the desired position. + * \assert + * \code decoder != NULL \endcode + * \code position != NULL \endcode + * \retval FLAC__bool + * \c true if successful, \c false if the stream is not native FLAC, + * or there was an error from the 'tell' callback or it returned + * \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position); + +/** Initialize the decoder instance to decode native FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * native FLAC stream. I/O is performed via callbacks to the client. + * For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_file() and FLAC__stream_decoder_init_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC streams. + * + * This flavor of initialization sets up the decoder to decode from a + * FLAC stream in an Ogg container. I/O is performed via callbacks to the + * client. For decoding from a plain file via filename or open FILE*, + * FLAC__stream_decoder_init_ogg_file() and FLAC__stream_decoder_init_ogg_FILE() + * provide a simpler interface. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param read_callback See FLAC__StreamDecoderReadCallback. This + * pointer must not be \c NULL. + * \param seek_callback See FLAC__StreamDecoderSeekCallback. This + * pointer may be \c NULL if seeking is not + * supported. If \a seek_callback is not \c NULL then a + * \a tell_callback, \a length_callback, and \a eof_callback must also be supplied. + * Alternatively, a dummy seek callback that just + * returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param tell_callback See FLAC__StreamDecoderTellCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a tell_callback must also be supplied. + * Alternatively, a dummy tell callback that just + * returns \c FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param length_callback See FLAC__StreamDecoderLengthCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a length_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param eof_callback See FLAC__StreamDecoderEofCallback. This + * pointer may be \c NULL if not supported by the client. If + * \a seek_callback is not \c NULL then a + * \a eof_callback must also be supplied. + * Alternatively, a dummy length callback that just + * returns \c false + * may also be supplied, all though this is slightly + * less efficient for the decoder. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain native FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdin since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a + * plain Ogg FLAC file. For non-stdio streams, you must use + * FLAC__stream_decoder_init_ogg_stream() and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param file An open FLAC file. The file should have been + * opened with mode \c "rb" and rewound. The file + * becomes owned by the decoder and should not be + * manipulated by the client while decoding. + * Unless \a file is \c stdin, it will be closed + * when FLAC__stream_decoder_finish() is called. + * Note however that seeking will not work when + * decoding from \c stdin since it is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \code file != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode native FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * native FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_FILE(), or FLAC__stream_decoder_init_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Initialize the decoder instance to decode Ogg FLAC files. + * + * This flavor of initialization sets up the decoder to decode from a plain + * Ogg FLAC file. If POSIX fopen() semantics are not sufficient, (for + * example, with Unicode filenames on Windows), you must use + * FLAC__stream_decoder_init_ogg_FILE(), or FLAC__stream_decoder_init_ogg_stream() + * and provide callbacks for the I/O. + * + * This function should be called after FLAC__stream_decoder_new() and + * FLAC__stream_decoder_set_*() but before any of the + * FLAC__stream_decoder_process_*() functions. Will set and return the + * decoder state, which will be FLAC__STREAM_DECODER_SEARCH_FOR_METADATA + * if initialization succeeded. + * + * \note Support for Ogg FLAC in the library is optional. If this + * library has been built without support for Ogg FLAC, this function + * will return \c FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER. + * + * \param decoder An uninitialized decoder instance. + * \param filename The name of the file to decode from. The file will + * be opened with fopen(). Use \c NULL to decode from + * \c stdin. Note that \c stdin is not seekable. + * \param write_callback See FLAC__StreamDecoderWriteCallback. This + * pointer must not be \c NULL. + * \param metadata_callback See FLAC__StreamDecoderMetadataCallback. This + * pointer may be \c NULL if the callback is not + * desired. + * \param error_callback See FLAC__StreamDecoderErrorCallback. This + * pointer must not be \c NULL. + * \param client_data This value will be supplied to callbacks in their + * \a client_data argument. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__StreamDecoderInitStatus + * \c FLAC__STREAM_DECODER_INIT_STATUS_OK if initialization was successful; + * see FLAC__StreamDecoderInitStatus for the meanings of other return values. + */ +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +); + +/** Finish the decoding process. + * Flushes the decoding buffer, releases resources, resets the decoder + * settings to their defaults, and returns the decoder state to + * FLAC__STREAM_DECODER_UNINITIALIZED. + * + * In the event of a prematurely-terminated decode, it is not strictly + * necessary to call this immediately before FLAC__stream_decoder_delete() + * but it is good practice to match every FLAC__stream_decoder_init_*() + * with a FLAC__stream_decoder_finish(). + * + * \param decoder An uninitialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if MD5 checking is on AND a STREAMINFO block was available + * AND the MD5 signature in the STREAMINFO block was non-zero AND the + * signature does not match the one computed by the decoder; else + * \c true. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder); + +/** Flush the stream input. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC. This will also turn + * off MD5 checking. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation + * error occurs (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder); + +/** Reset the decoding process. + * The decoder's input buffer will be cleared and the state set to + * \c FLAC__STREAM_DECODER_SEARCH_FOR_METADATA. This is similar to + * FLAC__stream_decoder_finish() except that the settings are + * preserved; there is no need to call FLAC__stream_decoder_init_*() + * before decoding again. MD5 checking will be restored to its original + * setting. + * + * If the decoder is seekable, or was initialized with + * FLAC__stream_decoder_init*_FILE() or FLAC__stream_decoder_init*_file(), + * the decoder will also attempt to seek to the beginning of the file. + * If this rewind fails, this function will return \c false. It follows + * that FLAC__stream_decoder_reset() cannot be used when decoding from + * \c stdin. + * + * If the decoder was initialized with FLAC__stream_encoder_init*_stream() + * and is not seekable (i.e. no seek callback was provided or the seek + * callback returns \c FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED), it + * is the duty of the client to start feeding data from the beginning of + * the stream on the next FLAC__stream_decoder_process_*() call. + * + * \param decoder A decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false if a memory allocation occurs + * (in which case the state will be set to + * \c FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR) or a seek error + * occurs (the state will be unchanged). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder); + +/** Decode one metadata block or audio frame. + * This version instructs the decoder to decode a either a single metadata + * block or a single frame and stop, unless the callbacks return a fatal + * error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * Depending on what was decoded, the metadata or write callback will be + * called with the decoded metadata block or audio frame. + * + * Unless there is a fatal read error or end of stream, this function + * will return once one whole frame is decoded. In other words, if the + * stream is not synchronized or points to a corrupt frame header, the + * decoder will continue to try and resync until it gets to a valid + * frame, then decode one frame, then return. If the decoder points to + * a frame whose frame CRC in the frame footer does not match the + * computed frame CRC, this function will issue a + * FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH error to the + * error callback, and return, having decoded one complete, although + * corrupt, frame. (Such corrupted frames are sent as silence of the + * correct length to the write callback.) + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the metadata. + * This version instructs the decoder to decode from the current position + * and continue until all the metadata has been read, or until the + * callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block is decoded, the metadata callback will be called + * with the decoded metadata. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder); + +/** Decode until the end of the stream. + * This version instructs the decoder to decode from the current position + * and continue until the end of stream (the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM), or until the + * callbacks return a fatal error. + * + * As the decoder needs more input it will call the read callback. + * As each metadata block and frame is decoded, the metadata or write + * callback will be called with the decoded metadata or frame. + * + * \param decoder An initialized decoder instance. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder); + +/** Skip one audio frame. + * This version instructs the decoder to 'skip' a single frame and stop, + * unless the callbacks return a fatal error or the read callback returns + * \c FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM. + * + * The decoding flow is the same as what occurs when + * FLAC__stream_decoder_process_single() is called to process an audio + * frame, except that this function does not decode the parsed data into + * PCM or call the write callback. The integrity of the frame is still + * checked the same way as in the other process functions. + * + * This function will return once one whole frame is skipped, in the + * same way that FLAC__stream_decoder_process_single() will return once + * one whole frame is decoded. + * + * This function can be used in more quickly determining FLAC frame + * boundaries when decoding of the actual data is not needed, for + * example when an application is separating a FLAC stream into frames + * for editing or storing in a container. To do this, the application + * can use FLAC__stream_decoder_skip_single_frame() to quickly advance + * to the next frame, then use + * FLAC__stream_decoder_get_decode_position() to find the new frame + * boundary. + * + * This function should only be called when the stream has advanced + * past all the metadata, otherwise it will return \c false. + * + * \param decoder An initialized decoder instance not in a metadata + * state. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c false if any fatal read, write, or memory allocation error + * occurred (meaning decoding must stop), or if the decoder + * is in the FLAC__STREAM_DECODER_SEARCH_FOR_METADATA or + * FLAC__STREAM_DECODER_READ_METADATA state, else \c true; for more + * information about the decoder, check the decoder state with + * FLAC__stream_decoder_get_state(). + */ +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder); + +/** Flush the input and seek to an absolute sample. + * Decoding will resume at the given sample. Note that because of + * this, the next write callback may contain a partial block. The + * client must support seeking the input or this function will fail + * and return \c false. Furthermore, if the decoder state is + * \c FLAC__STREAM_DECODER_SEEK_ERROR, then the decoder must be flushed + * with FLAC__stream_decoder_flush() or reset with + * FLAC__stream_decoder_reset() before decoding can continue. + * + * \param decoder A decoder instance. + * \param sample The target sample number to seek to. + * \assert + * \code decoder != NULL \endcode + * \retval FLAC__bool + * \c true if successful, else \c false. + */ +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample); + +/* \} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libflac/NOTE.txt b/src/libflac/NOTE.txt @@ -0,0 +1,4 @@ +8bitbubsy: +This is a cut down and modified version of libFLAC v1.3.3. +I have removed stuff I don't want, and only left the decoder in. Please don't +use this outside of the FT2 clone project! diff --git a/src/libflac/bitmath.c b/src/libflac/bitmath.c @@ -0,0 +1,69 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "private/bitmath.h" + +/* An example of what FLAC__bitmath_silog2() computes: + * + * silog2(-10) = 5 + * silog2(- 9) = 5 + * silog2(- 8) = 4 + * silog2(- 7) = 4 + * silog2(- 6) = 4 + * silog2(- 5) = 4 + * silog2(- 4) = 3 + * silog2(- 3) = 3 + * silog2(- 2) = 2 + * silog2(- 1) = 2 + * silog2( 0) = 0 + * silog2( 1) = 2 + * silog2( 2) = 3 + * silog2( 3) = 3 + * silog2( 4) = 4 + * silog2( 5) = 4 + * silog2( 6) = 4 + * silog2( 7) = 4 + * silog2( 8) = 5 + * silog2( 9) = 5 + * silog2( 10) = 5 + */ +uint32_t FLAC__bitmath_silog2(FLAC__int64 v) +{ + if(v == 0) + return 0; + + if(v == -1) + return 2; + + v = (v < 0) ? (-(v+1)) : v; + return FLAC__bitmath_ilog2_wide(v)+2; +} diff --git a/src/libflac/bitreader.c b/src/libflac/bitreader.c @@ -0,0 +1,914 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2018 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <string.h> +#include "private/bitmath.h" +#include "private/bitreader.h" +#include "private/crc.h" +#include "private/macros.h" +#include "share/compat.h" +#include "share/endswap.h" + +/* Things should be fastest when this matches the machine word size */ +/* WATCHOUT: if you change this you must also change the following #defines down to COUNT_ZERO_MSBS2 below to match */ +/* WATCHOUT: there are a few places where the code will not work unless brword is >= 32 bits wide */ +/* also, some sections currently only have fast versions for 4 or 8 bytes per word */ + +#if (ENABLE_64_BIT_WORDS == 0) + +typedef FLAC__uint32 brword; +#define FLAC__BYTES_PER_WORD 4 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 32 +#define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x) +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint32(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint32(word) + +#else + +typedef FLAC__uint64 brword; +#define FLAC__BYTES_PER_WORD 8 /* sizeof brword */ +#define FLAC__BITS_PER_WORD 64 +#define FLAC__WORD_ALL_ONES ((FLAC__uint64)FLAC__U64L(0xffffffffffffffff)) +/* SWAP_BE_WORD_TO_HOST swaps bytes in a brword (which is always big-endian) if necessary to match host byte order */ +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_64(x) +/* counts the # of zero MSBs in a word */ +#define COUNT_ZERO_MSBS(word) FLAC__clz_uint64(word) +#define COUNT_ZERO_MSBS2(word) FLAC__clz2_uint64(word) + +#endif + +/* + * This should be at least twice as large as the largest number of words + * required to represent any 'number' (in any encoding) you are going to + * read. With FLAC this is on the order of maybe a few hundred bits. + * If the buffer is smaller than that, the decoder won't be able to read + * in a whole number that is in a variable length encoding (e.g. Rice). + * But to be practical it should be at least 1K bytes. + * + * Increase this number to decrease the number of read callbacks, at the + * expense of using more memory. Or decrease for the reverse effect, + * keeping in mind the limit from the first paragraph. The optimal size + * also depends on the CPU cache size and other factors; some twiddling + * may be necessary to squeeze out the best performance. + */ +static const uint32_t FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ + +struct FLAC__BitReader { + /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ + /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ + brword *buffer; + uint32_t capacity; /* in words */ + uint32_t words; /* # of completed words in buffer */ + uint32_t bytes; /* # of bytes in incomplete word at buffer[words] */ + uint32_t consumed_words; /* #words ... */ + uint32_t consumed_bits; /* ... + (#bits of head word) already consumed from the front of buffer */ + uint32_t read_crc16; /* the running frame CRC */ + uint32_t crc16_offset; /* the number of words in the current buffer that should not be CRC'd */ + uint32_t crc16_align; /* the number of bits in the current consumed word that should not be CRC'd */ + FLAC__BitReaderReadCallback read_callback; + void *client_data; +}; + +static inline void crc16_update_word_(FLAC__BitReader *br, brword word) +{ + register uint32_t crc = br->read_crc16; + + for( ; br->crc16_align < FLAC__BITS_PER_WORD; br->crc16_align += 8) + crc = FLAC__CRC16_UPDATE((uint32_t)((word >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), crc); + + br->read_crc16 = crc; + br->crc16_align = 0; +} + +static inline void crc16_update_block_(FLAC__BitReader *br) +{ + if(br->consumed_words > br->crc16_offset && br->crc16_align) + crc16_update_word_(br, br->buffer[br->crc16_offset++]); + +#if FLAC__BYTES_PER_WORD == 4 + br->read_crc16 = FLAC__crc16_update_words32(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, (FLAC__uint16)br->read_crc16); +#elif FLAC__BYTES_PER_WORD == 8 + br->read_crc16 = FLAC__crc16_update_words64(br->buffer + br->crc16_offset, br->consumed_words - br->crc16_offset, br->read_crc16); +#else + unsigned i; + + for(i = br->crc16_offset; i < br->consumed_words; i++) + crc16_update_word_(br, br->buffer[i]); +#endif + + br->crc16_offset = 0; +} + +static FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) +{ + uint32_t start, end; + size_t bytes; + FLAC__byte *target; + + /* first shift the unconsumed buffer data toward the front as much as possible */ + if(br->consumed_words > 0) { + crc16_update_block_(br); /* CRC consumed words */ + + start = br->consumed_words; + end = br->words + (br->bytes? 1:0); + memmove(br->buffer, br->buffer+start, FLAC__BYTES_PER_WORD * (end - start)); + + br->words -= start; + br->consumed_words = 0; + } + + /* + * set the target for reading, taking into account word alignment and endianness + */ + bytes = (br->capacity - br->words) * FLAC__BYTES_PER_WORD - br->bytes; + if(bytes == 0) + return false; /* no space left, buffer is too small; see note for FLAC__BITREADER_DEFAULT_CAPACITY */ + target = ((FLAC__byte*)(br->buffer+br->words)) + br->bytes; + + /* before reading, if the existing reader looks like this (say brword is 32 bits wide) + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 (partial tail word is left-justified) + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? (shown laid out as bytes sequentially in memory) + * buffer[LE]: 44 33 22 11 ?? ?? ?? 55 (?? being don't-care) + * ^^-------target, bytes=3 + * on LE machines, have to byteswap the odd tail word so nothing is + * overwritten: + */ + if(br->bytes) + br->buffer[br->words] = SWAP_BE_WORD_TO_HOST(br->buffer[br->words]); + + /* now it looks like: + * bitstream : 11 22 33 44 55 br->words=1 br->bytes=1 + * buffer[BE]: 11 22 33 44 55 ?? ?? ?? + * buffer[LE]: 44 33 22 11 55 ?? ?? ?? + * ^^-------target, bytes=3 + */ + + /* read in the data; note that the callback may return a smaller number of bytes */ + if(!br->read_callback(target, &bytes, br->client_data)) + return false; + + /* after reading bytes 66 77 88 99 AA BB CC DD EE FF from the client: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 55 66 77 88 99 AA BB CC DD EE FF ?? + * now have to byteswap on LE machines: + */ + end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; + for(start = br->words; start < end; start++) + br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); + + /* now it looks like: + * bitstream : 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + * buffer[BE]: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF ?? + * buffer[LE]: 44 33 22 11 88 77 66 55 CC BB AA 99 ?? FF EE DD + * finally we'll update the reader values: + */ + end = br->words*FLAC__BYTES_PER_WORD + br->bytes + (uint32_t)bytes; + br->words = end / FLAC__BYTES_PER_WORD; + br->bytes = end % FLAC__BYTES_PER_WORD; + + return true; +} + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ + +FLAC__BitReader *FLAC__bitreader_new(void) +{ + FLAC__BitReader *br = calloc(1, sizeof(FLAC__BitReader)); + + /* calloc() implies: + memset(br, 0, sizeof(FLAC__BitReader)); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; + */ + return br; +} + +void FLAC__bitreader_delete(FLAC__BitReader *br) +{ + FLAC__bitreader_free(br); + free(br); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; + br->buffer = malloc(sizeof(brword) * br->capacity); + if(br->buffer == 0) + return false; + br->read_callback = rcb; + br->client_data = cd; + + return true; +} + +void FLAC__bitreader_free(FLAC__BitReader *br) +{ + if(0 != br->buffer) + free(br->buffer); + br->buffer = 0; + br->capacity = 0; + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + br->read_callback = 0; + br->client_data = 0; +} + +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br) +{ + br->words = br->bytes = 0; + br->consumed_words = br->consumed_bits = 0; + return true; +} + +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out) +{ + uint32_t i, j; + if(br == 0) { + fprintf(out, "bitreader is NULL\n"); + } + else { + fprintf(out, "bitreader: capacity=%u words=%u bytes=%u consumed: words=%u, bits=%u\n", br->capacity, br->words, br->bytes, br->consumed_words, br->consumed_bits); + + for(i = 0; i < br->words; i++) { + fprintf(out, "%08X: ", i); + for(j = 0; j < FLAC__BITS_PER_WORD; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01d", br->buffer[i] & ((brword)1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); + fprintf(out, "\n"); + } + if(br->bytes > 0) { + fprintf(out, "%08X: ", i); + for(j = 0; j < br->bytes*8; j++) + if(i < br->consumed_words || (i == br->consumed_words && j < br->consumed_bits)) + fprintf(out, "."); + else + fprintf(out, "%01d", br->buffer[i] & ((brword)1 << (br->bytes*8-j-1)) ? 1:0); + fprintf(out, "\n"); + } + } +} + +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed) +{ + br->read_crc16 = (uint32_t)seed; + br->crc16_offset = br->consumed_words; + br->crc16_align = br->consumed_bits; +} + +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) +{ + /* CRC consumed words up to here */ + crc16_update_block_(br); + + /* CRC any tail bytes in a partially-consumed word */ + if(br->consumed_bits) { + const brword tail = br->buffer[br->consumed_words]; + for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) + br->read_crc16 = FLAC__CRC16_UPDATE((uint32_t)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); + } + return (FLAC__uint16)br->read_crc16; +} + +inline FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +{ + return ((br->consumed_bits & 7) == 0); +} + +inline uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +{ + return 8 - (br->consumed_bits & 7); +} + +inline uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +{ + return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; +} + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits) +{ + if(bits == 0) { /* OPT: investigate if this can ever happen, maybe change to assertion */ + *val = 0; + return true; + } + + while((br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits < bits) { + if(!bitreader_read_from_client_(br)) + return false; + } + if(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + const uint32_t n = FLAC__BITS_PER_WORD - br->consumed_bits; + const brword word = br->buffer[br->consumed_words]; + if(bits < n) { + *val = (FLAC__uint32)((word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits)); /* The result has <= 32 non-zero bits */ + br->consumed_bits += bits; + return true; + } + /* (FLAC__BITS_PER_WORD - br->consumed_bits <= bits) ==> (FLAC__WORD_ALL_ONES >> br->consumed_bits) has no more than 'bits' non-zero bits */ + *val = (FLAC__uint32)(word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)); + bits -= n; + br->consumed_words++; + br->consumed_bits = 0; + if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ + *val <<= bits; + *val |= (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + } + return true; + } + else { /* br->consumed_bits == 0 */ + const brword word = br->buffer[br->consumed_words]; + if(bits < FLAC__BITS_PER_WORD) { + *val = (FLAC__uint32)(word >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits = bits; + return true; + } + /* at this point bits == FLAC__BITS_PER_WORD == 32; because of previous assertions, it can't be larger */ + *val = (FLAC__uint32)word; + br->consumed_words++; + return true; + } + } + else { + /* in this case we're starting our read at a partial tail word; + * the reader has guaranteed that we have at least 'bits' bits + * available to read, which makes this case simpler. + */ + /* OPT: taking out the consumed_bits==0 "else" case below might make things faster if less code allows the compiler to inline this function */ + if(br->consumed_bits) { + /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ + *val = (FLAC__uint32)((br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (FLAC__BITS_PER_WORD-br->consumed_bits-bits)); + br->consumed_bits += bits; + return true; + } + else { + *val = (FLAC__uint32)(br->buffer[br->consumed_words] >> (FLAC__BITS_PER_WORD-bits)); + br->consumed_bits += bits; + return true; + } + } +} + +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits) +{ + FLAC__uint32 uval, mask; + /* OPT: inline raw uint32 code here, or make into a macro if possible in the .h file */ + if(!FLAC__bitreader_read_raw_uint32(br, &uval, bits)) + return false; + /* sign-extend *val assuming it is currently bits wide. */ + /* From: https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend */ + mask = 1u << (bits - 1); + *val = (uval ^ mask) - mask; + return true; +} + +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits) +{ + FLAC__uint32 hi, lo; + + if(bits > 32) { + if(!FLAC__bitreader_read_raw_uint32(br, &hi, bits-32)) + return false; + if(!FLAC__bitreader_read_raw_uint32(br, &lo, 32)) + return false; + *val = hi; + *val <<= 32; + *val |= lo; + } + else { + if(!FLAC__bitreader_read_raw_uint32(br, &lo, bits)) + return false; + *val = lo; + } + return true; +} + +inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) +{ + FLAC__uint32 x8, x32 = 0; + + /* this doesn't need to be that fast as currently it is only used for vorbis comments */ + + if(!FLAC__bitreader_read_raw_uint32(br, &x32, 8)) + return false; + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 8); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 16); + + if(!FLAC__bitreader_read_raw_uint32(br, &x8, 8)) + return false; + x32 |= (x8 << 24); + + *val = x32; + return true; +} + +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits) +{ + /* + * OPT: a faster implementation is possible but probably not that useful + * since this is only called a couple of times in the metadata readers. + */ + + if(bits > 0) { + const uint32_t n = br->consumed_bits & 7; + uint32_t m; + FLAC__uint32 x; + + if(n != 0) { + m = flac_min(8-n, bits); + if(!FLAC__bitreader_read_raw_uint32(br, &x, m)) + return false; + bits -= m; + } + m = bits / 8; + if(m > 0) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(br, m)) + return false; + bits %= 8; + } + if(bits > 0) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, bits)) + return false; + } + } + + return true; +} + +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals) +{ + FLAC__uint32 x; + + /* step 1: skip over partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: skip whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + br->consumed_words++; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: skip any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals) +{ + FLAC__uint32 x; + + /* step 1: read from partial head word to get word aligned */ + while(nvals && br->consumed_bits) { /* i.e. run until we read 'nvals' bytes or we hit the end of the head word */ + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + if(0 == nvals) + return true; + /* step 2: read whole words in chunks */ + while(nvals >= FLAC__BYTES_PER_WORD) { + if(br->consumed_words < br->words) { + const brword word = br->buffer[br->consumed_words++]; +#if FLAC__BYTES_PER_WORD == 4 + val[0] = (FLAC__byte)(word >> 24); + val[1] = (FLAC__byte)(word >> 16); + val[2] = (FLAC__byte)(word >> 8); + val[3] = (FLAC__byte)word; +#elif FLAC__BYTES_PER_WORD == 8 + val[0] = (FLAC__byte)(word >> 56); + val[1] = (FLAC__byte)(word >> 48); + val[2] = (FLAC__byte)(word >> 40); + val[3] = (FLAC__byte)(word >> 32); + val[4] = (FLAC__byte)(word >> 24); + val[5] = (FLAC__byte)(word >> 16); + val[6] = (FLAC__byte)(word >> 8); + val[7] = (FLAC__byte)word; +#else + for(x = 0; x < FLAC__BYTES_PER_WORD; x++) + val[x] = (FLAC__byte)(word >> (8*(FLAC__BYTES_PER_WORD-x-1))); +#endif + val += FLAC__BYTES_PER_WORD; + nvals -= FLAC__BYTES_PER_WORD; + } + else if(!bitreader_read_from_client_(br)) + return false; + } + /* step 3: read any remainder from partial tail bytes */ + while(nvals) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + *val++ = (FLAC__byte)x; + nvals--; + } + + return true; +} + +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val) +{ + uint32_t i; + + *val = 0; + while(1) { + while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ + brword b = br->buffer[br->consumed_words] << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + if(br->consumed_bits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(br->consumed_bits == FLAC__BITS_PER_WORD) */ + br->consumed_words++; + br->consumed_bits = 0; + } + return true; + } + else { + *val += FLAC__BITS_PER_WORD - br->consumed_bits; + br->consumed_words++; + br->consumed_bits = 0; + /* didn't find stop bit yet, have to keep going... */ + } + } + /* at this point we've eaten up all the whole words; have to try + * reading through any tail bytes before calling the read callback. + * this is a repeat of the above logic adjusted for the fact we + * don't have a whole word. note though if the client is feeding + * us data a byte at a time (unlikely), br->consumed_bits may not + * be zero. + */ + if(br->bytes*8 > br->consumed_bits) { + const uint32_t end = br->bytes * 8; + brword b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; + if(b) { + i = COUNT_ZERO_MSBS(b); + *val += i; + i++; + br->consumed_bits += i; + return true; + } + else { + *val += end - br->consumed_bits; + br->consumed_bits = end; + /* didn't find stop bit yet, have to keep going... */ + } + } + if(!bitreader_read_from_client_(br)) + return false; + } +} + +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter) +{ + FLAC__uint32 lsbs = 0, msbs = 0; + uint32_t uval; + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter)) + return false; + + /* compose the value */ + uval = (msbs << parameter) | lsbs; + if(uval & 1) + *val = -((int)(uval >> 1)) - 1; + else + *val = (int)(uval >> 1); + + return true; +} + +/* this is by far the most heavily used reader call. it ain't pretty but it's fast */ +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter) +{ + /* try and get br->consumed_words and br->consumed_bits into register; + * must remember to flush them back to *br before calling other + * bitreader functions that use them, and before returning */ + uint32_t cwords, words, lsbs, msbs, x, y; + uint32_t ucbits; /* keep track of the number of unconsumed bits in word */ + brword b; + int *val, *end; + + val = vals; + end = vals + nvals; + + if(parameter == 0) { + while(val < end) { + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + + *val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1); + } + + return true; + } + + cwords = br->consumed_words; + words = br->words; + + /* if we've not consumed up to a partial tail word... */ + if(cwords >= words) { + x = 0; + goto process_tail; + } + + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; /* keep unconsumed bits aligned to left */ + + while(val < end) { + /* read the unary MSBs and end bit */ + x = y = COUNT_ZERO_MSBS2(b); + if(x == FLAC__BITS_PER_WORD) { + x = ucbits; + do { + /* didn't find stop bit yet, have to keep going... */ + cwords++; + if (cwords >= words) + goto incomplete_msbs; + b = br->buffer[cwords]; + y = COUNT_ZERO_MSBS2(b); + x += y; + } while(y == FLAC__BITS_PER_WORD); + } + b <<= y; + b <<= 1; /* account for stop bit */ + ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD; + msbs = x; + + /* read the binary LSBs */ + x = (FLAC__uint32)(b >> (FLAC__BITS_PER_WORD - parameter)); /* parameter < 32, so we can cast to 32-bit uint32_t */ + if(parameter <= ucbits) { + ucbits -= parameter; + b <<= parameter; + } else { + /* there are still bits left to read, they will all be in the next word */ + cwords++; + if (cwords >= words) + goto incomplete_lsbs; + b = br->buffer[cwords]; + ucbits += FLAC__BITS_PER_WORD - parameter; + x |= (FLAC__uint32)(b >> ucbits); + b <<= FLAC__BITS_PER_WORD - ucbits; + } + lsbs = x; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + + continue; + + /* at this point we've eaten up all the whole words */ +process_tail: + do { + if(0) { +incomplete_msbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + msbs += x; + x = ucbits = 0; + + if(0) { +incomplete_lsbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits)) + return false; + lsbs = x | lsbs; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + x = 0; + + cwords = br->consumed_words; + words = br->words; + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; + } while(cwords >= words && val < end); + } + + if(ucbits == 0 && cwords < words) { + /* don't leave the head word with no unconsumed bits */ + cwords++; + ucbits = FLAC__BITS_PER_WORD; + } + + br->consumed_bits = FLAC__BITS_PER_WORD - ucbits; + br->consumed_words = cwords; + + return true; +} + +/* on return, if *val == 0xffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen) +{ + FLAC__uint32 v = 0; + FLAC__uint32 x; + uint32_t i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else { + *val = 0xffffffff; + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = 0xffffffff; + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* on return, if *val == 0xffffffffffffffff then the utf-8 sequence was invalid, but the return value will be true */ +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen) +{ + FLAC__uint64 v = 0; + FLAC__uint32 x; + uint32_t i; + + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80)) { /* 0xxxxxxx */ + v = x; + i = 0; + } + else if(x & 0xC0 && !(x & 0x20)) { /* 110xxxxx */ + v = x & 0x1F; + i = 1; + } + else if(x & 0xE0 && !(x & 0x10)) { /* 1110xxxx */ + v = x & 0x0F; + i = 2; + } + else if(x & 0xF0 && !(x & 0x08)) { /* 11110xxx */ + v = x & 0x07; + i = 3; + } + else if(x & 0xF8 && !(x & 0x04)) { /* 111110xx */ + v = x & 0x03; + i = 4; + } + else if(x & 0xFC && !(x & 0x02)) { /* 1111110x */ + v = x & 0x01; + i = 5; + } + else if(x & 0xFE && !(x & 0x01)) { /* 11111110 */ + v = 0; + i = 6; + } + else { + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + for( ; i; i--) { + if(!FLAC__bitreader_read_raw_uint32(br, &x, 8)) + return false; + if(raw) + raw[(*rawlen)++] = (FLAC__byte)x; + if(!(x & 0x80) || (x & 0x40)) { /* 10xxxxxx */ + *val = FLAC__U64L(0xffffffffffffffff); + return true; + } + v <<= 6; + v |= (x & 0x3F); + } + *val = v; + return true; +} + +/* These functions are declared inline in this file but are also callable as + * externs from elsewhere. + * According to the C99 spec, section 6.7.4, simply providing a function + * prototype in a header file without 'inline' and making the function inline + * in this file should be sufficient. + * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To + * fix that we add extern declarations here. + */ +extern FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +extern uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +extern uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +extern FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); diff --git a/src/libflac/crc.c b/src/libflac/crc.c @@ -0,0 +1,410 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2018 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "private/crc.h" + +/* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ + +FLAC__uint8 const FLAC__crc8_table[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +/* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ + +FLAC__uint16 const FLAC__crc16_table[8][256] = { + { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 }, + + { 0x0000, 0x8603, 0x8c03, 0x0a00, 0x9803, 0x1e00, 0x1400, 0x9203, + 0xb003, 0x3600, 0x3c00, 0xba03, 0x2800, 0xae03, 0xa403, 0x2200, + 0xe003, 0x6600, 0x6c00, 0xea03, 0x7800, 0xfe03, 0xf403, 0x7200, + 0x5000, 0xd603, 0xdc03, 0x5a00, 0xc803, 0x4e00, 0x4400, 0xc203, + 0x4003, 0xc600, 0xcc00, 0x4a03, 0xd800, 0x5e03, 0x5403, 0xd200, + 0xf000, 0x7603, 0x7c03, 0xfa00, 0x6803, 0xee00, 0xe400, 0x6203, + 0xa000, 0x2603, 0x2c03, 0xaa00, 0x3803, 0xbe00, 0xb400, 0x3203, + 0x1003, 0x9600, 0x9c00, 0x1a03, 0x8800, 0x0e03, 0x0403, 0x8200, + 0x8006, 0x0605, 0x0c05, 0x8a06, 0x1805, 0x9e06, 0x9406, 0x1205, + 0x3005, 0xb606, 0xbc06, 0x3a05, 0xa806, 0x2e05, 0x2405, 0xa206, + 0x6005, 0xe606, 0xec06, 0x6a05, 0xf806, 0x7e05, 0x7405, 0xf206, + 0xd006, 0x5605, 0x5c05, 0xda06, 0x4805, 0xce06, 0xc406, 0x4205, + 0xc005, 0x4606, 0x4c06, 0xca05, 0x5806, 0xde05, 0xd405, 0x5206, + 0x7006, 0xf605, 0xfc05, 0x7a06, 0xe805, 0x6e06, 0x6406, 0xe205, + 0x2006, 0xa605, 0xac05, 0x2a06, 0xb805, 0x3e06, 0x3406, 0xb205, + 0x9005, 0x1606, 0x1c06, 0x9a05, 0x0806, 0x8e05, 0x8405, 0x0206, + 0x8009, 0x060a, 0x0c0a, 0x8a09, 0x180a, 0x9e09, 0x9409, 0x120a, + 0x300a, 0xb609, 0xbc09, 0x3a0a, 0xa809, 0x2e0a, 0x240a, 0xa209, + 0x600a, 0xe609, 0xec09, 0x6a0a, 0xf809, 0x7e0a, 0x740a, 0xf209, + 0xd009, 0x560a, 0x5c0a, 0xda09, 0x480a, 0xce09, 0xc409, 0x420a, + 0xc00a, 0x4609, 0x4c09, 0xca0a, 0x5809, 0xde0a, 0xd40a, 0x5209, + 0x7009, 0xf60a, 0xfc0a, 0x7a09, 0xe80a, 0x6e09, 0x6409, 0xe20a, + 0x2009, 0xa60a, 0xac0a, 0x2a09, 0xb80a, 0x3e09, 0x3409, 0xb20a, + 0x900a, 0x1609, 0x1c09, 0x9a0a, 0x0809, 0x8e0a, 0x840a, 0x0209, + 0x000f, 0x860c, 0x8c0c, 0x0a0f, 0x980c, 0x1e0f, 0x140f, 0x920c, + 0xb00c, 0x360f, 0x3c0f, 0xba0c, 0x280f, 0xae0c, 0xa40c, 0x220f, + 0xe00c, 0x660f, 0x6c0f, 0xea0c, 0x780f, 0xfe0c, 0xf40c, 0x720f, + 0x500f, 0xd60c, 0xdc0c, 0x5a0f, 0xc80c, 0x4e0f, 0x440f, 0xc20c, + 0x400c, 0xc60f, 0xcc0f, 0x4a0c, 0xd80f, 0x5e0c, 0x540c, 0xd20f, + 0xf00f, 0x760c, 0x7c0c, 0xfa0f, 0x680c, 0xee0f, 0xe40f, 0x620c, + 0xa00f, 0x260c, 0x2c0c, 0xaa0f, 0x380c, 0xbe0f, 0xb40f, 0x320c, + 0x100c, 0x960f, 0x9c0f, 0x1a0c, 0x880f, 0x0e0c, 0x040c, 0x820f }, + + { 0x0000, 0x8017, 0x802b, 0x003c, 0x8053, 0x0044, 0x0078, 0x806f, + 0x80a3, 0x00b4, 0x0088, 0x809f, 0x00f0, 0x80e7, 0x80db, 0x00cc, + 0x8143, 0x0154, 0x0168, 0x817f, 0x0110, 0x8107, 0x813b, 0x012c, + 0x01e0, 0x81f7, 0x81cb, 0x01dc, 0x81b3, 0x01a4, 0x0198, 0x818f, + 0x8283, 0x0294, 0x02a8, 0x82bf, 0x02d0, 0x82c7, 0x82fb, 0x02ec, + 0x0220, 0x8237, 0x820b, 0x021c, 0x8273, 0x0264, 0x0258, 0x824f, + 0x03c0, 0x83d7, 0x83eb, 0x03fc, 0x8393, 0x0384, 0x03b8, 0x83af, + 0x8363, 0x0374, 0x0348, 0x835f, 0x0330, 0x8327, 0x831b, 0x030c, + 0x8503, 0x0514, 0x0528, 0x853f, 0x0550, 0x8547, 0x857b, 0x056c, + 0x05a0, 0x85b7, 0x858b, 0x059c, 0x85f3, 0x05e4, 0x05d8, 0x85cf, + 0x0440, 0x8457, 0x846b, 0x047c, 0x8413, 0x0404, 0x0438, 0x842f, + 0x84e3, 0x04f4, 0x04c8, 0x84df, 0x04b0, 0x84a7, 0x849b, 0x048c, + 0x0780, 0x8797, 0x87ab, 0x07bc, 0x87d3, 0x07c4, 0x07f8, 0x87ef, + 0x8723, 0x0734, 0x0708, 0x871f, 0x0770, 0x8767, 0x875b, 0x074c, + 0x86c3, 0x06d4, 0x06e8, 0x86ff, 0x0690, 0x8687, 0x86bb, 0x06ac, + 0x0660, 0x8677, 0x864b, 0x065c, 0x8633, 0x0624, 0x0618, 0x860f, + 0x8a03, 0x0a14, 0x0a28, 0x8a3f, 0x0a50, 0x8a47, 0x8a7b, 0x0a6c, + 0x0aa0, 0x8ab7, 0x8a8b, 0x0a9c, 0x8af3, 0x0ae4, 0x0ad8, 0x8acf, + 0x0b40, 0x8b57, 0x8b6b, 0x0b7c, 0x8b13, 0x0b04, 0x0b38, 0x8b2f, + 0x8be3, 0x0bf4, 0x0bc8, 0x8bdf, 0x0bb0, 0x8ba7, 0x8b9b, 0x0b8c, + 0x0880, 0x8897, 0x88ab, 0x08bc, 0x88d3, 0x08c4, 0x08f8, 0x88ef, + 0x8823, 0x0834, 0x0808, 0x881f, 0x0870, 0x8867, 0x885b, 0x084c, + 0x89c3, 0x09d4, 0x09e8, 0x89ff, 0x0990, 0x8987, 0x89bb, 0x09ac, + 0x0960, 0x8977, 0x894b, 0x095c, 0x8933, 0x0924, 0x0918, 0x890f, + 0x0f00, 0x8f17, 0x8f2b, 0x0f3c, 0x8f53, 0x0f44, 0x0f78, 0x8f6f, + 0x8fa3, 0x0fb4, 0x0f88, 0x8f9f, 0x0ff0, 0x8fe7, 0x8fdb, 0x0fcc, + 0x8e43, 0x0e54, 0x0e68, 0x8e7f, 0x0e10, 0x8e07, 0x8e3b, 0x0e2c, + 0x0ee0, 0x8ef7, 0x8ecb, 0x0edc, 0x8eb3, 0x0ea4, 0x0e98, 0x8e8f, + 0x8d83, 0x0d94, 0x0da8, 0x8dbf, 0x0dd0, 0x8dc7, 0x8dfb, 0x0dec, + 0x0d20, 0x8d37, 0x8d0b, 0x0d1c, 0x8d73, 0x0d64, 0x0d58, 0x8d4f, + 0x0cc0, 0x8cd7, 0x8ceb, 0x0cfc, 0x8c93, 0x0c84, 0x0cb8, 0x8caf, + 0x8c63, 0x0c74, 0x0c48, 0x8c5f, 0x0c30, 0x8c27, 0x8c1b, 0x0c0c }, + + { 0x0000, 0x9403, 0xa803, 0x3c00, 0xd003, 0x4400, 0x7800, 0xec03, + 0x2003, 0xb400, 0x8800, 0x1c03, 0xf000, 0x6403, 0x5803, 0xcc00, + 0x4006, 0xd405, 0xe805, 0x7c06, 0x9005, 0x0406, 0x3806, 0xac05, + 0x6005, 0xf406, 0xc806, 0x5c05, 0xb006, 0x2405, 0x1805, 0x8c06, + 0x800c, 0x140f, 0x280f, 0xbc0c, 0x500f, 0xc40c, 0xf80c, 0x6c0f, + 0xa00f, 0x340c, 0x080c, 0x9c0f, 0x700c, 0xe40f, 0xd80f, 0x4c0c, + 0xc00a, 0x5409, 0x6809, 0xfc0a, 0x1009, 0x840a, 0xb80a, 0x2c09, + 0xe009, 0x740a, 0x480a, 0xdc09, 0x300a, 0xa409, 0x9809, 0x0c0a, + 0x801d, 0x141e, 0x281e, 0xbc1d, 0x501e, 0xc41d, 0xf81d, 0x6c1e, + 0xa01e, 0x341d, 0x081d, 0x9c1e, 0x701d, 0xe41e, 0xd81e, 0x4c1d, + 0xc01b, 0x5418, 0x6818, 0xfc1b, 0x1018, 0x841b, 0xb81b, 0x2c18, + 0xe018, 0x741b, 0x481b, 0xdc18, 0x301b, 0xa418, 0x9818, 0x0c1b, + 0x0011, 0x9412, 0xa812, 0x3c11, 0xd012, 0x4411, 0x7811, 0xec12, + 0x2012, 0xb411, 0x8811, 0x1c12, 0xf011, 0x6412, 0x5812, 0xcc11, + 0x4017, 0xd414, 0xe814, 0x7c17, 0x9014, 0x0417, 0x3817, 0xac14, + 0x6014, 0xf417, 0xc817, 0x5c14, 0xb017, 0x2414, 0x1814, 0x8c17, + 0x803f, 0x143c, 0x283c, 0xbc3f, 0x503c, 0xc43f, 0xf83f, 0x6c3c, + 0xa03c, 0x343f, 0x083f, 0x9c3c, 0x703f, 0xe43c, 0xd83c, 0x4c3f, + 0xc039, 0x543a, 0x683a, 0xfc39, 0x103a, 0x8439, 0xb839, 0x2c3a, + 0xe03a, 0x7439, 0x4839, 0xdc3a, 0x3039, 0xa43a, 0x983a, 0x0c39, + 0x0033, 0x9430, 0xa830, 0x3c33, 0xd030, 0x4433, 0x7833, 0xec30, + 0x2030, 0xb433, 0x8833, 0x1c30, 0xf033, 0x6430, 0x5830, 0xcc33, + 0x4035, 0xd436, 0xe836, 0x7c35, 0x9036, 0x0435, 0x3835, 0xac36, + 0x6036, 0xf435, 0xc835, 0x5c36, 0xb035, 0x2436, 0x1836, 0x8c35, + 0x0022, 0x9421, 0xa821, 0x3c22, 0xd021, 0x4422, 0x7822, 0xec21, + 0x2021, 0xb422, 0x8822, 0x1c21, 0xf022, 0x6421, 0x5821, 0xcc22, + 0x4024, 0xd427, 0xe827, 0x7c24, 0x9027, 0x0424, 0x3824, 0xac27, + 0x6027, 0xf424, 0xc824, 0x5c27, 0xb024, 0x2427, 0x1827, 0x8c24, + 0x802e, 0x142d, 0x282d, 0xbc2e, 0x502d, 0xc42e, 0xf82e, 0x6c2d, + 0xa02d, 0x342e, 0x082e, 0x9c2d, 0x702e, 0xe42d, 0xd82d, 0x4c2e, + 0xc028, 0x542b, 0x682b, 0xfc28, 0x102b, 0x8428, 0xb828, 0x2c2b, + 0xe02b, 0x7428, 0x4828, 0xdc2b, 0x3028, 0xa42b, 0x982b, 0x0c28 }, + + { 0x0000, 0x807b, 0x80f3, 0x0088, 0x81e3, 0x0198, 0x0110, 0x816b, + 0x83c3, 0x03b8, 0x0330, 0x834b, 0x0220, 0x825b, 0x82d3, 0x02a8, + 0x8783, 0x07f8, 0x0770, 0x870b, 0x0660, 0x861b, 0x8693, 0x06e8, + 0x0440, 0x843b, 0x84b3, 0x04c8, 0x85a3, 0x05d8, 0x0550, 0x852b, + 0x8f03, 0x0f78, 0x0ff0, 0x8f8b, 0x0ee0, 0x8e9b, 0x8e13, 0x0e68, + 0x0cc0, 0x8cbb, 0x8c33, 0x0c48, 0x8d23, 0x0d58, 0x0dd0, 0x8dab, + 0x0880, 0x88fb, 0x8873, 0x0808, 0x8963, 0x0918, 0x0990, 0x89eb, + 0x8b43, 0x0b38, 0x0bb0, 0x8bcb, 0x0aa0, 0x8adb, 0x8a53, 0x0a28, + 0x9e03, 0x1e78, 0x1ef0, 0x9e8b, 0x1fe0, 0x9f9b, 0x9f13, 0x1f68, + 0x1dc0, 0x9dbb, 0x9d33, 0x1d48, 0x9c23, 0x1c58, 0x1cd0, 0x9cab, + 0x1980, 0x99fb, 0x9973, 0x1908, 0x9863, 0x1818, 0x1890, 0x98eb, + 0x9a43, 0x1a38, 0x1ab0, 0x9acb, 0x1ba0, 0x9bdb, 0x9b53, 0x1b28, + 0x1100, 0x917b, 0x91f3, 0x1188, 0x90e3, 0x1098, 0x1010, 0x906b, + 0x92c3, 0x12b8, 0x1230, 0x924b, 0x1320, 0x935b, 0x93d3, 0x13a8, + 0x9683, 0x16f8, 0x1670, 0x960b, 0x1760, 0x971b, 0x9793, 0x17e8, + 0x1540, 0x953b, 0x95b3, 0x15c8, 0x94a3, 0x14d8, 0x1450, 0x942b, + 0xbc03, 0x3c78, 0x3cf0, 0xbc8b, 0x3de0, 0xbd9b, 0xbd13, 0x3d68, + 0x3fc0, 0xbfbb, 0xbf33, 0x3f48, 0xbe23, 0x3e58, 0x3ed0, 0xbeab, + 0x3b80, 0xbbfb, 0xbb73, 0x3b08, 0xba63, 0x3a18, 0x3a90, 0xbaeb, + 0xb843, 0x3838, 0x38b0, 0xb8cb, 0x39a0, 0xb9db, 0xb953, 0x3928, + 0x3300, 0xb37b, 0xb3f3, 0x3388, 0xb2e3, 0x3298, 0x3210, 0xb26b, + 0xb0c3, 0x30b8, 0x3030, 0xb04b, 0x3120, 0xb15b, 0xb1d3, 0x31a8, + 0xb483, 0x34f8, 0x3470, 0xb40b, 0x3560, 0xb51b, 0xb593, 0x35e8, + 0x3740, 0xb73b, 0xb7b3, 0x37c8, 0xb6a3, 0x36d8, 0x3650, 0xb62b, + 0x2200, 0xa27b, 0xa2f3, 0x2288, 0xa3e3, 0x2398, 0x2310, 0xa36b, + 0xa1c3, 0x21b8, 0x2130, 0xa14b, 0x2020, 0xa05b, 0xa0d3, 0x20a8, + 0xa583, 0x25f8, 0x2570, 0xa50b, 0x2460, 0xa41b, 0xa493, 0x24e8, + 0x2640, 0xa63b, 0xa6b3, 0x26c8, 0xa7a3, 0x27d8, 0x2750, 0xa72b, + 0xad03, 0x2d78, 0x2df0, 0xad8b, 0x2ce0, 0xac9b, 0xac13, 0x2c68, + 0x2ec0, 0xaebb, 0xae33, 0x2e48, 0xaf23, 0x2f58, 0x2fd0, 0xafab, + 0x2a80, 0xaafb, 0xaa73, 0x2a08, 0xab63, 0x2b18, 0x2b90, 0xabeb, + 0xa943, 0x2938, 0x29b0, 0xa9cb, 0x28a0, 0xa8db, 0xa853, 0x2828 }, + + { 0x0000, 0xf803, 0x7003, 0x8800, 0xe006, 0x1805, 0x9005, 0x6806, + 0x4009, 0xb80a, 0x300a, 0xc809, 0xa00f, 0x580c, 0xd00c, 0x280f, + 0x8012, 0x7811, 0xf011, 0x0812, 0x6014, 0x9817, 0x1017, 0xe814, + 0xc01b, 0x3818, 0xb018, 0x481b, 0x201d, 0xd81e, 0x501e, 0xa81d, + 0x8021, 0x7822, 0xf022, 0x0821, 0x6027, 0x9824, 0x1024, 0xe827, + 0xc028, 0x382b, 0xb02b, 0x4828, 0x202e, 0xd82d, 0x502d, 0xa82e, + 0x0033, 0xf830, 0x7030, 0x8833, 0xe035, 0x1836, 0x9036, 0x6835, + 0x403a, 0xb839, 0x3039, 0xc83a, 0xa03c, 0x583f, 0xd03f, 0x283c, + 0x8047, 0x7844, 0xf044, 0x0847, 0x6041, 0x9842, 0x1042, 0xe841, + 0xc04e, 0x384d, 0xb04d, 0x484e, 0x2048, 0xd84b, 0x504b, 0xa848, + 0x0055, 0xf856, 0x7056, 0x8855, 0xe053, 0x1850, 0x9050, 0x6853, + 0x405c, 0xb85f, 0x305f, 0xc85c, 0xa05a, 0x5859, 0xd059, 0x285a, + 0x0066, 0xf865, 0x7065, 0x8866, 0xe060, 0x1863, 0x9063, 0x6860, + 0x406f, 0xb86c, 0x306c, 0xc86f, 0xa069, 0x586a, 0xd06a, 0x2869, + 0x8074, 0x7877, 0xf077, 0x0874, 0x6072, 0x9871, 0x1071, 0xe872, + 0xc07d, 0x387e, 0xb07e, 0x487d, 0x207b, 0xd878, 0x5078, 0xa87b, + 0x808b, 0x7888, 0xf088, 0x088b, 0x608d, 0x988e, 0x108e, 0xe88d, + 0xc082, 0x3881, 0xb081, 0x4882, 0x2084, 0xd887, 0x5087, 0xa884, + 0x0099, 0xf89a, 0x709a, 0x8899, 0xe09f, 0x189c, 0x909c, 0x689f, + 0x4090, 0xb893, 0x3093, 0xc890, 0xa096, 0x5895, 0xd095, 0x2896, + 0x00aa, 0xf8a9, 0x70a9, 0x88aa, 0xe0ac, 0x18af, 0x90af, 0x68ac, + 0x40a3, 0xb8a0, 0x30a0, 0xc8a3, 0xa0a5, 0x58a6, 0xd0a6, 0x28a5, + 0x80b8, 0x78bb, 0xf0bb, 0x08b8, 0x60be, 0x98bd, 0x10bd, 0xe8be, + 0xc0b1, 0x38b2, 0xb0b2, 0x48b1, 0x20b7, 0xd8b4, 0x50b4, 0xa8b7, + 0x00cc, 0xf8cf, 0x70cf, 0x88cc, 0xe0ca, 0x18c9, 0x90c9, 0x68ca, + 0x40c5, 0xb8c6, 0x30c6, 0xc8c5, 0xa0c3, 0x58c0, 0xd0c0, 0x28c3, + 0x80de, 0x78dd, 0xf0dd, 0x08de, 0x60d8, 0x98db, 0x10db, 0xe8d8, + 0xc0d7, 0x38d4, 0xb0d4, 0x48d7, 0x20d1, 0xd8d2, 0x50d2, 0xa8d1, + 0x80ed, 0x78ee, 0xf0ee, 0x08ed, 0x60eb, 0x98e8, 0x10e8, 0xe8eb, + 0xc0e4, 0x38e7, 0xb0e7, 0x48e4, 0x20e2, 0xd8e1, 0x50e1, 0xa8e2, + 0x00ff, 0xf8fc, 0x70fc, 0x88ff, 0xe0f9, 0x18fa, 0x90fa, 0x68f9, + 0x40f6, 0xb8f5, 0x30f5, 0xc8f6, 0xa0f0, 0x58f3, 0xd0f3, 0x28f0 }, + + { 0x0000, 0x8113, 0x8223, 0x0330, 0x8443, 0x0550, 0x0660, 0x8773, + 0x8883, 0x0990, 0x0aa0, 0x8bb3, 0x0cc0, 0x8dd3, 0x8ee3, 0x0ff0, + 0x9103, 0x1010, 0x1320, 0x9233, 0x1540, 0x9453, 0x9763, 0x1670, + 0x1980, 0x9893, 0x9ba3, 0x1ab0, 0x9dc3, 0x1cd0, 0x1fe0, 0x9ef3, + 0xa203, 0x2310, 0x2020, 0xa133, 0x2640, 0xa753, 0xa463, 0x2570, + 0x2a80, 0xab93, 0xa8a3, 0x29b0, 0xaec3, 0x2fd0, 0x2ce0, 0xadf3, + 0x3300, 0xb213, 0xb123, 0x3030, 0xb743, 0x3650, 0x3560, 0xb473, + 0xbb83, 0x3a90, 0x39a0, 0xb8b3, 0x3fc0, 0xbed3, 0xbde3, 0x3cf0, + 0xc403, 0x4510, 0x4620, 0xc733, 0x4040, 0xc153, 0xc263, 0x4370, + 0x4c80, 0xcd93, 0xcea3, 0x4fb0, 0xc8c3, 0x49d0, 0x4ae0, 0xcbf3, + 0x5500, 0xd413, 0xd723, 0x5630, 0xd143, 0x5050, 0x5360, 0xd273, + 0xdd83, 0x5c90, 0x5fa0, 0xdeb3, 0x59c0, 0xd8d3, 0xdbe3, 0x5af0, + 0x6600, 0xe713, 0xe423, 0x6530, 0xe243, 0x6350, 0x6060, 0xe173, + 0xee83, 0x6f90, 0x6ca0, 0xedb3, 0x6ac0, 0xebd3, 0xe8e3, 0x69f0, + 0xf703, 0x7610, 0x7520, 0xf433, 0x7340, 0xf253, 0xf163, 0x7070, + 0x7f80, 0xfe93, 0xfda3, 0x7cb0, 0xfbc3, 0x7ad0, 0x79e0, 0xf8f3, + 0x0803, 0x8910, 0x8a20, 0x0b33, 0x8c40, 0x0d53, 0x0e63, 0x8f70, + 0x8080, 0x0193, 0x02a3, 0x83b0, 0x04c3, 0x85d0, 0x86e0, 0x07f3, + 0x9900, 0x1813, 0x1b23, 0x9a30, 0x1d43, 0x9c50, 0x9f60, 0x1e73, + 0x1183, 0x9090, 0x93a0, 0x12b3, 0x95c0, 0x14d3, 0x17e3, 0x96f0, + 0xaa00, 0x2b13, 0x2823, 0xa930, 0x2e43, 0xaf50, 0xac60, 0x2d73, + 0x2283, 0xa390, 0xa0a0, 0x21b3, 0xa6c0, 0x27d3, 0x24e3, 0xa5f0, + 0x3b03, 0xba10, 0xb920, 0x3833, 0xbf40, 0x3e53, 0x3d63, 0xbc70, + 0xb380, 0x3293, 0x31a3, 0xb0b0, 0x37c3, 0xb6d0, 0xb5e0, 0x34f3, + 0xcc00, 0x4d13, 0x4e23, 0xcf30, 0x4843, 0xc950, 0xca60, 0x4b73, + 0x4483, 0xc590, 0xc6a0, 0x47b3, 0xc0c0, 0x41d3, 0x42e3, 0xc3f0, + 0x5d03, 0xdc10, 0xdf20, 0x5e33, 0xd940, 0x5853, 0x5b63, 0xda70, + 0xd580, 0x5493, 0x57a3, 0xd6b0, 0x51c3, 0xd0d0, 0xd3e0, 0x52f3, + 0x6e03, 0xef10, 0xec20, 0x6d33, 0xea40, 0x6b53, 0x6863, 0xe970, + 0xe680, 0x6793, 0x64a3, 0xe5b0, 0x62c3, 0xe3d0, 0xe0e0, 0x61f3, + 0xff00, 0x7e13, 0x7d23, 0xfc30, 0x7b43, 0xfa50, 0xf960, 0x7873, + 0x7783, 0xf690, 0xf5a0, 0x74b3, 0xf3c0, 0x72d3, 0x71e3, 0xf0f0 }, + + { 0x0000, 0x1006, 0x200c, 0x300a, 0x4018, 0x501e, 0x6014, 0x7012, + 0x8030, 0x9036, 0xa03c, 0xb03a, 0xc028, 0xd02e, 0xe024, 0xf022, + 0x8065, 0x9063, 0xa069, 0xb06f, 0xc07d, 0xd07b, 0xe071, 0xf077, + 0x0055, 0x1053, 0x2059, 0x305f, 0x404d, 0x504b, 0x6041, 0x7047, + 0x80cf, 0x90c9, 0xa0c3, 0xb0c5, 0xc0d7, 0xd0d1, 0xe0db, 0xf0dd, + 0x00ff, 0x10f9, 0x20f3, 0x30f5, 0x40e7, 0x50e1, 0x60eb, 0x70ed, + 0x00aa, 0x10ac, 0x20a6, 0x30a0, 0x40b2, 0x50b4, 0x60be, 0x70b8, + 0x809a, 0x909c, 0xa096, 0xb090, 0xc082, 0xd084, 0xe08e, 0xf088, + 0x819b, 0x919d, 0xa197, 0xb191, 0xc183, 0xd185, 0xe18f, 0xf189, + 0x01ab, 0x11ad, 0x21a7, 0x31a1, 0x41b3, 0x51b5, 0x61bf, 0x71b9, + 0x01fe, 0x11f8, 0x21f2, 0x31f4, 0x41e6, 0x51e0, 0x61ea, 0x71ec, + 0x81ce, 0x91c8, 0xa1c2, 0xb1c4, 0xc1d6, 0xd1d0, 0xe1da, 0xf1dc, + 0x0154, 0x1152, 0x2158, 0x315e, 0x414c, 0x514a, 0x6140, 0x7146, + 0x8164, 0x9162, 0xa168, 0xb16e, 0xc17c, 0xd17a, 0xe170, 0xf176, + 0x8131, 0x9137, 0xa13d, 0xb13b, 0xc129, 0xd12f, 0xe125, 0xf123, + 0x0101, 0x1107, 0x210d, 0x310b, 0x4119, 0x511f, 0x6115, 0x7113, + 0x8333, 0x9335, 0xa33f, 0xb339, 0xc32b, 0xd32d, 0xe327, 0xf321, + 0x0303, 0x1305, 0x230f, 0x3309, 0x431b, 0x531d, 0x6317, 0x7311, + 0x0356, 0x1350, 0x235a, 0x335c, 0x434e, 0x5348, 0x6342, 0x7344, + 0x8366, 0x9360, 0xa36a, 0xb36c, 0xc37e, 0xd378, 0xe372, 0xf374, + 0x03fc, 0x13fa, 0x23f0, 0x33f6, 0x43e4, 0x53e2, 0x63e8, 0x73ee, + 0x83cc, 0x93ca, 0xa3c0, 0xb3c6, 0xc3d4, 0xd3d2, 0xe3d8, 0xf3de, + 0x8399, 0x939f, 0xa395, 0xb393, 0xc381, 0xd387, 0xe38d, 0xf38b, + 0x03a9, 0x13af, 0x23a5, 0x33a3, 0x43b1, 0x53b7, 0x63bd, 0x73bb, + 0x02a8, 0x12ae, 0x22a4, 0x32a2, 0x42b0, 0x52b6, 0x62bc, 0x72ba, + 0x8298, 0x929e, 0xa294, 0xb292, 0xc280, 0xd286, 0xe28c, 0xf28a, + 0x82cd, 0x92cb, 0xa2c1, 0xb2c7, 0xc2d5, 0xd2d3, 0xe2d9, 0xf2df, + 0x02fd, 0x12fb, 0x22f1, 0x32f7, 0x42e5, 0x52e3, 0x62e9, 0x72ef, + 0x8267, 0x9261, 0xa26b, 0xb26d, 0xc27f, 0xd279, 0xe273, 0xf275, + 0x0257, 0x1251, 0x225b, 0x325d, 0x424f, 0x5249, 0x6243, 0x7245, + 0x0202, 0x1204, 0x220e, 0x3208, 0x421a, 0x521c, 0x6216, 0x7210, + 0x8232, 0x9234, 0xa23e, 0xb238, 0xc22a, 0xd22c, 0xe226, 0xf220 } +}; + +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len) +{ + FLAC__uint8 crc = 0; + + while(len--) + crc = FLAC__crc8_table[crc ^ *data++]; + + return crc; +} + +FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len) +{ + FLAC__uint16 crc = 0; + + while(len >= 8){ + crc ^= data[0] << 8 | data[1]; + + crc = FLAC__crc16_table[7][crc >> 8] ^ FLAC__crc16_table[6][crc & 0xFF] ^ + FLAC__crc16_table[5][data[2] ] ^ FLAC__crc16_table[4][data[3] ] ^ + FLAC__crc16_table[3][data[4] ] ^ FLAC__crc16_table[2][data[5] ] ^ + FLAC__crc16_table[1][data[6] ] ^ FLAC__crc16_table[0][data[7] ]; + + data += 8; + len -= 8; + } + + while(len--) + crc = (crc<<8) ^ FLAC__crc16_table[0][(crc>>8) ^ *data++]; + + return crc; +} + +FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc) +{ + while (len >= 2) { + crc ^= words[0] >> 16; + + crc = FLAC__crc16_table[7][crc >> 8 ] ^ FLAC__crc16_table[6][crc & 0xFF ] ^ + FLAC__crc16_table[5][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[4][ words[0] & 0xFF] ^ + FLAC__crc16_table[3][ words[1] >> 24 ] ^ FLAC__crc16_table[2][(words[1] >> 16) & 0xFF] ^ + FLAC__crc16_table[1][(words[1] >> 8) & 0xFF] ^ FLAC__crc16_table[0][ words[1] & 0xFF]; + + words += 2; + len -= 2; + } + + if (len) { + crc ^= words[0] >> 16; + + crc = FLAC__crc16_table[3][crc >> 8 ] ^ FLAC__crc16_table[2][crc & 0xFF ] ^ + FLAC__crc16_table[1][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[0][words[0] & 0xFF]; + } + + return crc; +} + +FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc) +{ + while (len--) { + crc ^= words[0] >> 48; + + crc = FLAC__crc16_table[7][crc >> 8 ] ^ FLAC__crc16_table[6][crc & 0xFF ] ^ + FLAC__crc16_table[5][(words[0] >> 40) & 0xFF] ^ FLAC__crc16_table[4][(words[0] >> 32) & 0xFF] ^ + FLAC__crc16_table[3][(words[0] >> 24) & 0xFF] ^ FLAC__crc16_table[2][(words[0] >> 16) & 0xFF] ^ + FLAC__crc16_table[1][(words[0] >> 8) & 0xFF] ^ FLAC__crc16_table[0][ words[0] & 0xFF]; + + words++; + } + + return crc; +} diff --git a/src/libflac/fixed.c b/src/libflac/fixed.c @@ -0,0 +1,100 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <math.h> +#include <string.h> +#include "share/compat.h" +#include "private/bitmath.h" +#include "private/fixed.h" +#include "private/macros.h" + +#ifdef local_abs +#undef local_abs +#endif +#define local_abs(x) ((uint32_t)((x)<0? -(x) : (x))) + +void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]) +{ + const int idata_len = (int)data_len; + int i; + + switch(order) { + case 0: + memcpy(residual, data, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 2*data[i-1] + data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; + break; + default: break; + } +} + +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]) +{ + int i, idata_len = (int)data_len; + + switch(order) { + case 0: + memcpy(data, residual, sizeof(residual[0])*data_len); + break; + case 1: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + data[i-1]; + break; + case 2: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 2*data[i-1] - data[i-2]; + break; + case 3: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; + break; + case 4: + for(i = 0; i < idata_len; i++) + data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; + break; + default: break; + } +} diff --git a/src/libflac/format.c b/src/libflac/format.c @@ -0,0 +1,566 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> /* for qsort() */ +#include <string.h> /* for memset() */ +#include "FLAC/format.h" +#include "share/alloc.h" +#include "share/compat.h" +#include "private/format.h" +#include "private/macros.h" + +/* PACKAGE_VERSION should come from configure */ +FLAC_API const char *FLAC__VERSION_STRING = "1.3.3"; + +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " "1.3.3" " 20190804"; + +FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; +FLAC_API const uint32_t FLAC__STREAM_SYNC = 0x664C6143; +FLAC_API const uint32_t FLAC__STREAM_SYNC_LEN = 32; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ + +FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); + +FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ + +FLAC_API const uint32_t FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ +FLAC_API const uint32_t FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ + +FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC = 0x3ffe; +FLAC_API const uint32_t FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const uint32_t FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ + +FLAC_API const uint32_t FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ + +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ + +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */ +FLAC_API const uint32_t FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER = 31; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */ + +FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[] = { + "PARTITIONED_RICE", + "PARTITIONED_RICE2" +}; + +FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = 4; /* bits */ +FLAC_API const uint32_t FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = 5; /* bits */ + +FLAC_API const uint32_t FLAC__SUBFRAME_ZERO_PAD_LEN = 1; /* bits */ +FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LEN = 6; /* bits */ +FLAC_API const uint32_t FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = 1; /* bits */ + +FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = 0x00; +FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = 0x02; +FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = 0x10; +FLAC_API const uint32_t FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = 0x40; + +FLAC_API const char * const FLAC__SubframeTypeString[] = { + "CONSTANT", + "VERBATIM", + "FIXED", + "LPC" +}; + +FLAC_API const char * const FLAC__ChannelAssignmentString[] = { + "INDEPENDENT", + "LEFT_SIDE", + "RIGHT_SIDE", + "MID_SIDE" +}; + +FLAC_API const char * const FLAC__FrameNumberTypeString[] = { + "FRAME_NUMBER_TYPE_FRAME_NUMBER", + "FRAME_NUMBER_TYPE_SAMPLE_NUMBER" +}; + +FLAC_API const char * const FLAC__MetadataTypeString[] = { + "STREAMINFO", + "PADDING", + "APPLICATION", + "SEEKTABLE", + "VORBIS_COMMENT", + "CUESHEET", + "PICTURE" +}; + +FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[] = { + "Other", + "32x32 pixels 'file icon' (PNG only)", + "Other file icon", + "Cover (front)", + "Cover (back)", + "Leaflet page", + "Media (e.g. label side of CD)", + "Lead artist/lead performer/soloist", + "Artist/performer", + "Conductor", + "Band/Orchestra", + "Composer", + "Lyricist/text writer", + "Recording Location", + "During recording", + "During performance", + "Movie/video screen capture", + "A bright coloured fish", + "Illustration", + "Band/artist logotype", + "Publisher/Studio logotype" +}; + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(uint32_t sample_rate) +{ + if(sample_rate == 0 || sample_rate > FLAC__MAX_SAMPLE_RATE) { + return false; + } + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(uint32_t blocksize, uint32_t sample_rate) +{ + if(blocksize > 16384) + return false; + else if(sample_rate <= 48000 && blocksize > 4608) + return false; + else + return true; +} + +FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(uint32_t sample_rate) +{ + if( + !FLAC__format_sample_rate_is_valid(sample_rate) || + ( + sample_rate >= (1u << 16) && + !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) + ) + ) { + return false; + } + else + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) +{ + uint32_t i; + FLAC__uint64 prev_sample_number = 0; + FLAC__bool got_prev = false; + + for(i = 0; i < seek_table->num_points; i++) { + if(got_prev) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].sample_number <= prev_sample_number + ) + return false; + } + prev_sample_number = seek_table->points[i].sample_number; + got_prev = true; + } + + return true; +} + +/* used as the sort predicate for qsort() */ +static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +{ + /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ + if(l->sample_number == r->sample_number) + return 0; + else if(l->sample_number < r->sample_number) + return -1; + else + return 1; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API uint32_t FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) +{ + uint32_t i, j; + FLAC__bool first; + + if (seek_table->num_points == 0) + return 0; + + /* sort the seekpoints */ + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); + + /* uniquify the seekpoints */ + first = true; + for(i = j = 0; i < seek_table->num_points; i++) { + if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { + if(!first) { + if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) + continue; + } + } + first = false; + seek_table->points[j++] = seek_table->points[i]; + } + + for(i = j; i < seek_table->num_points; i++) { + seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + + return j; +} + +/* + * also disallows non-shortest-form encodings, c.f. + * http://www.unicode.org/versions/corrigendum1.html + * and a more clear explanation at the end of this section: + * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + */ +static uint32_t utf8len_(const FLAC__byte *utf8) +{ + if ((utf8[0] & 0x80) == 0) { + return 1; + } + else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { + if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ + return 0; + return 2; + } + else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { + if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ + return 0; + /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ + if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ + return 0; + if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ + return 0; + return 3; + } + else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { + if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ + return 0; + return 4; + } + else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { + if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ + return 0; + return 5; + } + else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { + if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ + return 0; + return 6; + } + else { + return 0; + } +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) +{ + char c; + for(c = *name; c; c = *(++name)) + if(c < 0x20 || c == 0x3d || c > 0x7d) + return false; + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, uint32_t length) +{ + if(length == (uint32_t)(-1)) { + while(*value) { + uint32_t n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + } + else { + const FLAC__byte *end = value + length; + while(value < end) { + uint32_t n = utf8len_(value); + if(n == 0) + return false; + value += n; + } + if(value != end) + return false; + } + return true; +} + +FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, uint32_t length) +{ + const FLAC__byte *s, *end; + + for(s = entry, end = s + length; s < end && *s != '='; s++) { + if(*s < 0x20 || *s > 0x7D) + return false; + } + if(s == end) + return false; + + s++; /* skip '=' */ + + while(s < end) { + uint32_t n = utf8len_(s); + if(n == 0) + return false; + s += n; + } + if(s != end) + return false; + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) +{ + uint32_t i, j; + + if(check_cd_da_subset) { + if(cue_sheet->lead_in < 2 * 44100) { + if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; + return false; + } + if(cue_sheet->lead_in % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; + return false; + } + } + + if(cue_sheet->num_tracks == 0) { + if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; + return false; + } + + if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { + if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; + return false; + } + + for(i = 0; i < cue_sheet->num_tracks; i++) { + if(cue_sheet->tracks[i].number == 0) { + if(violation) *violation = "cue sheet may not have a track number 0"; + return false; + } + + if(check_cd_da_subset) { + if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { + if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; + return false; + } + } + + if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { + if(violation) { + if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ + *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; + else + *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; + } + return false; + } + + if(i < cue_sheet->num_tracks - 1) { + if(cue_sheet->tracks[i].num_indices == 0) { + if(violation) *violation = "cue sheet track must have at least one index point"; + return false; + } + + if(cue_sheet->tracks[i].indices[0].number > 1) { + if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; + return false; + } + } + + for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { + if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { + if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; + return false; + } + + if(j > 0) { + if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { + if(violation) *violation = "cue sheet track index numbers must increase by 1"; + return false; + } + } + } + } + + return true; +} + +/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ +FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) +{ + char *p; + FLAC__byte *b; + + for(p = picture->mime_type; *p; p++) { + if(*p < 0x20 || *p > 0x7e) { + if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; + return false; + } + } + + for(b = picture->description; *b; ) { + uint32_t n = utf8len_(b); + if(n == 0) { + if(violation) *violation = "description string must be valid UTF-8"; + return false; + } + b += n; + } + + return true; +} + +/* + * These routines are private to libFLAC + */ +uint32_t FLAC__format_get_max_rice_partition_order(uint32_t blocksize, uint32_t predictor_order) +{ + return + FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( + FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), + blocksize, + predictor_order + ); +} + +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize(uint32_t blocksize) +{ + uint32_t max_rice_partition_order = 0; + while(!(blocksize & 1)) { + max_rice_partition_order++; + blocksize >>= 1; + } + return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); +} + +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(uint32_t limit, uint32_t blocksize, uint32_t predictor_order) +{ + uint32_t max_rice_partition_order = limit; + + while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) + max_rice_partition_order--; + + return max_rice_partition_order; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + object->parameters = 0; + object->raw_bits = 0; + object->capacity_by_order = 0; +} + +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) +{ + if(0 != object->parameters) + free(object->parameters); + if(0 != object->raw_bits) + free(object->raw_bits); + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); +} + +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, uint32_t max_partition_order) +{ + if(object->capacity_by_order < max_partition_order) { + if(0 == (object->parameters = safe_realloc_(object->parameters, sizeof(uint32_t)*(1ULL << max_partition_order)))) + return false; + if(0 == (object->raw_bits = safe_realloc_(object->raw_bits, sizeof(uint32_t)*(1ULL << max_partition_order)))) + return false; + memset(object->raw_bits, 0, sizeof(uint32_t)*(1ULL << max_partition_order)); + object->capacity_by_order = max_partition_order; + } + + return true; +} diff --git a/src/libflac/lpc.c b/src/libflac/lpc.c @@ -0,0 +1,1233 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <math.h> +#include "FLAC/format.h" +#include "share/compat.h" +#include "private/bitmath.h" +#include "private/lpc.h" +#include "private/macros.h" + +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + +#if defined(_MSC_VER) && (_MSC_VER < 1800) +#include <float.h> +static inline long int lround(double x) { + return (long)(x + _copysign(0.5, x)); +} +#endif + +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len) +{ + uint32_t i; + for(i = 0; i < data_len; i++) + out[i] = in[i] * window[i]; +} + +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]) +{ + /* + * this version tends to run faster because of better data locality + * ('data_len' is usually much larger than 'lag') + */ + FLAC__real d; + uint32_t sample, coeff; + const uint32_t limit = data_len - lag; + + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] = 0.0; + for(sample = 0; sample <= limit; sample++) { + d = data[sample]; + for(coeff = 0; coeff < lag; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } + for(; sample < data_len; sample++) { + d = data[sample]; + for(coeff = 0; coeff < data_len - sample; coeff++) + autoc[coeff] += d * data[sample+coeff]; + } +} + +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], uint32_t *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]) +{ + uint32_t i, j; + double r, err, lpc[FLAC__MAX_LPC_ORDER]; + + err = autoc[0]; + + for(i = 0; i < *max_order; i++) { + /* Sum up this iteration's reflection coefficient. */ + r = -autoc[i+1]; + for(j = 0; j < i; j++) + r -= lpc[j] * autoc[i-j]; + r /= err; + + /* Update LPC coefficients and total error. */ + lpc[i]=r; + for(j = 0; j < (i>>1); j++) { + double tmp = lpc[j]; + lpc[j] += r * lpc[i-1-j]; + lpc[i-1-j] += r * tmp; + } + if(i & 1) + lpc[j] += lpc[j] * r; + + err *= (1.0 - r * r); + + /* save this order */ + for(j = 0; j <= i; j++) + lp_coeff[i][j] = (FLAC__real)(-lpc[j]); /* negate FIR filter coeff to get predictor coeff */ + error[i] = err; + + /* see SF bug https://sourceforge.net/p/flac/bugs/234/ */ + if(err == 0.0) { + *max_order = i+1; + return; + } + } +} + +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift) +{ + uint32_t i; + double cmax; + FLAC__int32 qmax, qmin; + + /* drop one bit for the sign; from here on out we consider only |lp_coeff[i]| */ + precision--; + qmax = 1 << precision; + qmin = -qmax; + qmax--; + + /* calc cmax = max( |lp_coeff[i]| ) */ + cmax = 0.0; + for(i = 0; i < order; i++) { + const double d = fabs(lp_coeff[i]); + if(d > cmax) + cmax = d; + } + + if(cmax <= 0.0) { + /* => coefficients are all 0, which means our constant-detect didn't work */ + return 2; + } + else { + const int max_shiftlimit = (1 << (FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN-1)) - 1; + const int min_shiftlimit = -max_shiftlimit - 1; + int log2cmax; + + (void)frexp(cmax, &log2cmax); + log2cmax--; + *shift = (int)precision - log2cmax - 1; + + if(*shift > max_shiftlimit) + *shift = max_shiftlimit; + else if(*shift < min_shiftlimit) + return 1; + } + + if(*shift >= 0) { + double error = 0.0; + FLAC__int32 q; + for(i = 0; i < order; i++) { + error += lp_coeff[i] * (1 << *shift); + q = lround(error); + + if(q > qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + } + /* negative shift is very rare but due to design flaw, negative shift is + * not allowed in the decoder, so it must be handled specially by scaling + * down coeffs + */ + else { + const int nshift = -(*shift); + double error = 0.0; + FLAC__int32 q; + + for(i = 0; i < order; i++) { + error += lp_coeff[i] / (1 << nshift); + q = lround(error); + if(q > qmax) + q = qmax; + else if(q < qmin) + q = qmin; + error -= q; + qlp_coeff[i] = q; + } + *shift = 0; + } + + return 0; +} + +#if defined(_MSC_VER) +// silence MSVC warnings about __restrict modifier +#pragma warning ( disable : 4028 ) +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) +#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + uint32_t i, j; + FLAC__int32 sum; + const FLAC__int32 *history; + + FLAC__ASSERT(order > 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); + } + *(residual++) = *(data++) - (sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + residual[i] = data[i] - (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + default: + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + residual[i] = data[i] - (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 * flac_restrict data, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict residual) +#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + uint32_t i, j; + FLAC__int64 sum; + const FLAC__int32 *history; + + FLAC__ASSERT(order > 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) { + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); + break; + } + if(FLAC__bitmath_silog2((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (int64_t)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization))); + break; + } + *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + residual[i] = data[i] - (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + default: + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + residual[i] = data[i] - (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data) +#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + FLAC__int64 sumo; + uint32_t i, j; + FLAC__int32 sum; + const FLAC__int32 *r = residual, *history; + + FLAC__ASSERT(order > 0); + + for(i = 0; i < data_len; i++) { + sumo = 0; + sum = 0; + history = data; + for(j = 0; j < order; j++) { + sum += qlp_coeff[j] * (*(--history)); + sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); + if(sumo > 2147483647ll || sumo < -2147483648ll) + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); + } + *(data++) = *(r++) + (sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int32 sum; + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * data[i-10]; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * data[i-9]; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * data[i-8]; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * data[i-7]; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * data[i-6]; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * data[i-5]; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * data[i-4]; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * data[i-3]; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * data[i-2]; + sum += qlp_coeff[0] * data[i-1]; + data[i] = residual[i] + (sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + ((qlp_coeff[0] * data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + default: + case 32: sum += qlp_coeff[31] * data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * data[i-13]; + sum += qlp_coeff[11] * data[i-12]; + sum += qlp_coeff[10] * data[i-11]; + sum += qlp_coeff[ 9] * data[i-10]; + sum += qlp_coeff[ 8] * data[i- 9]; + sum += qlp_coeff[ 7] * data[i- 8]; + sum += qlp_coeff[ 6] * data[i- 7]; + sum += qlp_coeff[ 5] * data[i- 6]; + sum += qlp_coeff[ 4] * data[i- 5]; + sum += qlp_coeff[ 3] * data[i- 4]; + sum += qlp_coeff[ 2] * data[i- 3]; + sum += qlp_coeff[ 1] * data[i- 2]; + sum += qlp_coeff[ 0] * data[i- 1]; + } + data[i] = residual[i] + (sum >> lp_quantization); + } + } +} +#endif + +void FLAC__lpc_restore_signal_wide(const FLAC__int32 * flac_restrict residual, uint32_t data_len, const FLAC__int32 * flac_restrict qlp_coeff, uint32_t order, int lp_quantization, FLAC__int32 * flac_restrict data) +#if !defined(FLAC__LPC_UNROLLED_FILTER_LOOPS) +{ + uint32_t i, j; + FLAC__int64 sum; + const FLAC__int32 *r = residual, *history; + + FLAC__ASSERT(order > 0); + + for(i = 0; i < data_len; i++) { + sum = 0; + history = data; + for(j = 0; j < order; j++) + sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); + if(FLAC__bitmath_silog2(sum >> lp_quantization) > 32) { + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); + break; + } + if(FLAC__bitmath_silog2((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization))); + break; + } + *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); + } +} +#else /* fully unrolled version for normal use */ +{ + int i; + FLAC__int64 sum; + + /* + * We do unique versions up to 12th order since that's the subset limit. + * Also they are roughly ordered to match frequency of occurrence to + * minimize branching. + */ + if(order <= 12) { + if(order > 8) { + if(order > 10) { + if(order == 12) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 11 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 10) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 9 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[8] * (FLAC__int64)data[i-9]; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else if(order > 4) { + if(order > 6) { + if(order == 8) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[7] * (FLAC__int64)data[i-8]; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 7 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[6] * (FLAC__int64)data[i-7]; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 6) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[5] * (FLAC__int64)data[i-6]; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 5 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[4] * (FLAC__int64)data[i-5]; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + } + else { + if(order > 2) { + if(order == 4) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[3] * (FLAC__int64)data[i-4]; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 3 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[2] * (FLAC__int64)data[i-3]; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + } + else { + if(order == 2) { + for(i = 0; i < (int)data_len; i++) { + sum = 0; + sum += qlp_coeff[1] * (FLAC__int64)data[i-2]; + sum += qlp_coeff[0] * (FLAC__int64)data[i-1]; + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } + else { /* order == 1 */ + for(i = 0; i < (int)data_len; i++) + data[i] = residual[i] + (FLAC__int32)((qlp_coeff[0] * (FLAC__int64)data[i-1]) >> lp_quantization); + } + } + } + } + else { /* order > 12 */ + for(i = 0; i < (int)data_len; i++) { + sum = 0; + switch(order) { + default: + case 32: sum += qlp_coeff[31] * (FLAC__int64)data[i-32]; /* Falls through. */ + case 31: sum += qlp_coeff[30] * (FLAC__int64)data[i-31]; /* Falls through. */ + case 30: sum += qlp_coeff[29] * (FLAC__int64)data[i-30]; /* Falls through. */ + case 29: sum += qlp_coeff[28] * (FLAC__int64)data[i-29]; /* Falls through. */ + case 28: sum += qlp_coeff[27] * (FLAC__int64)data[i-28]; /* Falls through. */ + case 27: sum += qlp_coeff[26] * (FLAC__int64)data[i-27]; /* Falls through. */ + case 26: sum += qlp_coeff[25] * (FLAC__int64)data[i-26]; /* Falls through. */ + case 25: sum += qlp_coeff[24] * (FLAC__int64)data[i-25]; /* Falls through. */ + case 24: sum += qlp_coeff[23] * (FLAC__int64)data[i-24]; /* Falls through. */ + case 23: sum += qlp_coeff[22] * (FLAC__int64)data[i-23]; /* Falls through. */ + case 22: sum += qlp_coeff[21] * (FLAC__int64)data[i-22]; /* Falls through. */ + case 21: sum += qlp_coeff[20] * (FLAC__int64)data[i-21]; /* Falls through. */ + case 20: sum += qlp_coeff[19] * (FLAC__int64)data[i-20]; /* Falls through. */ + case 19: sum += qlp_coeff[18] * (FLAC__int64)data[i-19]; /* Falls through. */ + case 18: sum += qlp_coeff[17] * (FLAC__int64)data[i-18]; /* Falls through. */ + case 17: sum += qlp_coeff[16] * (FLAC__int64)data[i-17]; /* Falls through. */ + case 16: sum += qlp_coeff[15] * (FLAC__int64)data[i-16]; /* Falls through. */ + case 15: sum += qlp_coeff[14] * (FLAC__int64)data[i-15]; /* Falls through. */ + case 14: sum += qlp_coeff[13] * (FLAC__int64)data[i-14]; /* Falls through. */ + case 13: sum += qlp_coeff[12] * (FLAC__int64)data[i-13]; + sum += qlp_coeff[11] * (FLAC__int64)data[i-12]; + sum += qlp_coeff[10] * (FLAC__int64)data[i-11]; + sum += qlp_coeff[ 9] * (FLAC__int64)data[i-10]; + sum += qlp_coeff[ 8] * (FLAC__int64)data[i- 9]; + sum += qlp_coeff[ 7] * (FLAC__int64)data[i- 8]; + sum += qlp_coeff[ 6] * (FLAC__int64)data[i- 7]; + sum += qlp_coeff[ 5] * (FLAC__int64)data[i- 6]; + sum += qlp_coeff[ 4] * (FLAC__int64)data[i- 5]; + sum += qlp_coeff[ 3] * (FLAC__int64)data[i- 4]; + sum += qlp_coeff[ 2] * (FLAC__int64)data[i- 3]; + sum += qlp_coeff[ 1] * (FLAC__int64)data[i- 2]; + sum += qlp_coeff[ 0] * (FLAC__int64)data[i- 1]; + } + data[i] = residual[i] + (FLAC__int32)(sum >> lp_quantization); + } + } +} +#endif + +#if defined(_MSC_VER) +#pragma warning ( default : 4028 ) +#endif + +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples) +{ + double error_scale; + + error_scale = 0.5 / (double)total_samples; + + return FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error, error_scale); +} + +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale) +{ + if(lpc_error > 0.0) { + double bps = (double)0.5 * log(error_scale * lpc_error) / M_LN2; + if(bps >= 0.0) + return bps; + else + return 0.0; + } + else if(lpc_error < 0.0) { /* error should not be negative but can happen due to inadequate floating-point resolution */ + return 1e32; + } + else { + return 0.0; + } +} + +uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order) +{ + uint32_t order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + double bits, best_bits, error_scale; + + error_scale = 0.5 / (double)total_samples; + + best_index = 0; + best_bits = (uint32_t)(-1); + + for(indx = 0, order = 1; indx < max_order; indx++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (double)(total_samples - order) + (double)(order * overhead_bits_per_order); + if(bits < best_bits) { + best_index = indx; + best_bits = bits; + } + } + + return best_index+1; /* +1 since indx of lpc_error[] is order-1 */ +} diff --git a/src/libflac/md5.c b/src/libflac/md5.c @@ -0,0 +1,479 @@ +#include <stdlib.h> /* for malloc() */ +#include <string.h> /* for memcpy() */ + +#include "private/md5.h" +#include "share/alloc.h" +#include "share/compat.h" +#include "share/endswap.h" + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson <ijackson@nyx.cs.du.edu>. + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain. + */ + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) +{ + register FLAC__uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#define byteSwap(buf, words) +#define byteSwapX16(buf) + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, uint32_t len) +{ + FLAC__uint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void FLAC__MD5Init(FLAC__MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; + + ctx->internal_buf.p8 = 0; + ctx->capacity = 0; +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + FLAC__byte *p = (FLAC__byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwapX16(ctx->in); + FLAC__MD5Transform(ctx->buf, ctx->in); + p = (FLAC__byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + FLAC__MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + if (0 != ctx->internal_buf.p8) { + free(ctx->internal_buf.p8); + ctx->internal_buf.p8 = 0; + ctx->capacity = 0; + } + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* + * Convert the incoming audio signal to a byte stream + */ +static void format_input_(FLAC__multibyte *mbuf, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample) +{ + FLAC__byte *buf_ = mbuf->p8; + FLAC__int16 *buf16 = mbuf->p16; + FLAC__int32 *buf32 = mbuf->p32; + FLAC__int32 a_word; + uint32_t channel, sample; + + /* Storage in the output buffer, buf, is little endian. */ + +#define BYTES_CHANNEL_SELECTOR(bytes, channels) (bytes * 100 + channels) + + /* First do the most commonly used combinations. */ + switch (BYTES_CHANNEL_SELECTOR (bytes_per_sample, channels)) { + /* One byte per sample. */ + case (BYTES_CHANNEL_SELECTOR (1, 1)): + for (sample = 0; sample < samples; sample++) + *buf_++ = (FLAC__byte)signal[0][sample]; + return; + + case (BYTES_CHANNEL_SELECTOR (1, 2)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = (FLAC__byte)signal[0][sample]; + *buf_++ = (FLAC__byte)signal[1][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 4)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = (FLAC__byte)signal[0][sample]; + *buf_++ = (FLAC__byte)signal[1][sample]; + *buf_++ = (FLAC__byte)signal[2][sample]; + *buf_++ = (FLAC__byte)signal[3][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 6)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = (FLAC__byte)signal[0][sample]; + *buf_++ = (FLAC__byte)signal[1][sample]; + *buf_++ = (FLAC__byte)signal[2][sample]; + *buf_++ = (FLAC__byte)signal[3][sample]; + *buf_++ = (FLAC__byte)signal[4][sample]; + *buf_++ = (FLAC__byte)signal[5][sample]; + } + return; + + case (BYTES_CHANNEL_SELECTOR (1, 8)): + for (sample = 0; sample < samples; sample++) { + *buf_++ = (FLAC__byte)signal[0][sample]; + *buf_++ = (FLAC__byte)signal[1][sample]; + *buf_++ = (FLAC__byte)signal[2][sample]; + *buf_++ = (FLAC__byte)signal[3][sample]; + *buf_++ = (FLAC__byte)signal[4][sample]; + *buf_++ = (FLAC__byte)signal[5][sample]; + *buf_++ = (FLAC__byte)signal[6][sample]; + *buf_++ = (FLAC__byte)signal[7][sample]; + } + return; + + /* Two bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (2, 1)): + for (sample = 0; sample < samples; sample++) + *buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (2, 2)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 4)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 6)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[4][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[5][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (2, 8)): + for (sample = 0; sample < samples; sample++) { + *buf16++ = (FLAC__int16)H2LE_16(signal[0][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[1][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[2][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[3][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[4][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[5][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[6][sample]); + *buf16++ = (FLAC__int16)H2LE_16(signal[7][sample]); + } + return; + + /* Three bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (3, 1)): + for (sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + case (BYTES_CHANNEL_SELECTOR (3, 2)): + for (sample = 0; sample < samples; sample++) { + a_word = signal[0][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + a_word = signal[1][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + /* Four bytes per sample. */ + case (BYTES_CHANNEL_SELECTOR (4, 1)): + for (sample = 0; sample < samples; sample++) + *buf32++ = H2LE_32(signal[0][sample]); + return; + + case (BYTES_CHANNEL_SELECTOR (4, 2)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 4)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 6)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); + } + return; + + case (BYTES_CHANNEL_SELECTOR (4, 8)): + for (sample = 0; sample < samples; sample++) { + *buf32++ = H2LE_32(signal[0][sample]); + *buf32++ = H2LE_32(signal[1][sample]); + *buf32++ = H2LE_32(signal[2][sample]); + *buf32++ = H2LE_32(signal[3][sample]); + *buf32++ = H2LE_32(signal[4][sample]); + *buf32++ = H2LE_32(signal[5][sample]); + *buf32++ = H2LE_32(signal[6][sample]); + *buf32++ = H2LE_32(signal[7][sample]); + } + return; + + default: + break; + } + + /* General version. */ + switch (bytes_per_sample) { + case 1: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf_++ = (FLAC__byte)signal[channel][sample]; + return; + + case 2: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf16++ = (FLAC__int16)H2LE_16(signal[channel][sample]); + return; + + case 3: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) { + a_word = signal[channel][sample]; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; a_word >>= 8; + *buf_++ = (FLAC__byte)a_word; + } + return; + + case 4: + for (sample = 0; sample < samples; sample++) + for (channel = 0; channel < channels; channel++) + *buf32++ = H2LE_32(signal[channel][sample]); + return; + + default: + break; + } +} + +/* + * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. + */ +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample) +{ + const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; + + /* overflow check */ + if ((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) + return false; + if ((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) + return false; + + if (ctx->capacity < bytes_needed) { + if (0 == (ctx->internal_buf.p8 = safe_realloc_(ctx->internal_buf.p8, bytes_needed))) { + if (0 == (ctx->internal_buf.p8 = safe_malloc_(bytes_needed))) { + ctx->capacity = 0; + return false; + } + } + ctx->capacity = bytes_needed; + } + + format_input_(&ctx->internal_buf, signal, channels, samples, bytes_per_sample); + + FLAC__MD5Update(ctx, ctx->internal_buf.p8, (uint32_t)bytes_needed); + + return true; +} diff --git a/src/libflac/memory.c b/src/libflac/memory.c @@ -0,0 +1,175 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdint.h> +#include "private/memory.h" +#include "share/compat.h" +#include "share/alloc.h" + +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) +{ + void *x; + + x = safe_malloc_(bytes); + *aligned_address = x; + + return x; +} + +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +{ + FLAC__int32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__int32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +{ + FLAC__uint32 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint32 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +{ + FLAC__uint64 *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__uint64 *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, uint32_t **unaligned_pointer, uint32_t **aligned_pointer) +{ + uint32_t *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + uint32_t *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +{ + FLAC__real *pu; /* unaligned pointer */ + union { /* union needed to comply with C99 pointer aliasing rules */ + FLAC__real *pa; /* aligned pointer */ + void *pv; /* aligned pointer alias */ + } u; + + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(0 == pu) { + return false; + } + else { + if(*unaligned_pointer != 0) + free(*unaligned_pointer); + *unaligned_pointer = pu; + *aligned_pointer = u.pa; + return true; + } +} + +void *safe_malloc_mul_2op_p(size_t size1, size_t size2) +{ + if(!size1 || !size2) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} diff --git a/src/libflac/metadata_iterators.c b/src/libflac/metadata_iterators.c @@ -0,0 +1,3147 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <sys/stat.h> /* for stat(), maybe chmod() */ + +#include "private/metadata.h" + +#include "FLAC/stream_decoder.h" +#include "share/alloc.h" +#include "share/compat.h" +#include "share/macros.h" +#include "share/safe_str.h" +#include "private/macros.h" +#include "private/memory.h" + +// 8bb: hide POSIX warnings +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */ +#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p + +/**************************************************************************** + * + * Local function declarations + * + ***************************************************************************/ + +static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes); +static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes); +static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes); +static FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes); +static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes); +static FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes); + +static FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block); +static FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block); +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length); + +static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block); +static FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length); +static FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length); +static FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block); +static FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block); +static FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block); +static FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block); +static FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length); + +static FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block); +static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last); +static FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append); + +static void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator); +static FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator); + +static uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb); +static uint32_t seek_to_first_metadata_block_(FILE *f); + +static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append); +static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup); + +static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status); + +static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status); +static void cleanup_tempfile_(FILE **tempfile, char **tempfilename); + +static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats); +static void set_file_stats_(const char *filename, struct flac_stat_s *stats); + +static int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence); +static FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle); + +static FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status); + + +#ifdef FLAC__VALGRIND_TESTING +static size_t local__fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + size_t ret = fwrite(ptr, size, nmemb, stream); + if(!ferror(stream)) + fflush(stream); + return ret; +} +#else +#define local__fwrite fwrite +#endif + +/**************************************************************************** + * + * Level 0 implementation + * + ***************************************************************************/ + +static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool got_error; + FLAC__StreamMetadata *object; +} level0_client_data; + +static FLAC__StreamMetadata *get_one_metadata_block_(const char *filename, FLAC__MetadataType type) +{ + level0_client_data cd; + FLAC__StreamDecoder *decoder; + + cd.got_error = false; + cd.object = 0; + + decoder = FLAC__stream_decoder_new(); + + if(0 == decoder) + return 0; + + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, type); + + if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &cd) != FLAC__STREAM_DECODER_INIT_STATUS_OK || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + return 0; + } + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || cd.got_error) { + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + if(0 != cd.object) + FLAC__metadata_object_delete(cd.object); + return 0; + } + + (void)FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + return cd.object; +} + +FLAC_API FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetadata *streaminfo) +{ + FLAC__StreamMetadata *object; + + object = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_STREAMINFO); + + if (object) { + /* can just copy the contents since STREAMINFO has no internal structure */ + *streaminfo = *object; + FLAC__metadata_object_delete(object); + return true; + } + else { + return false; + } +} + +FLAC_API FLAC__bool FLAC__metadata_get_tags(const char *filename, FLAC__StreamMetadata **tags) +{ + *tags = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + return 0 != *tags; +} + +FLAC_API FLAC__bool FLAC__metadata_get_cuesheet(const char *filename, FLAC__StreamMetadata **cuesheet) +{ + *cuesheet = get_one_metadata_block_(filename, FLAC__METADATA_TYPE_CUESHEET); + + return 0 != *cuesheet; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + /* + * we assume we only get here when the one metadata block we were + * looking for was passed to us + */ + if(!cd->got_error && 0 == cd->object) { + if(0 == (cd->object = FLAC__metadata_object_clone(metadata))) + cd->got_error = true; + } +} + +void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + level0_client_data *cd = (level0_client_data *)client_data; + (void)decoder; + + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + cd->got_error = true; +} + +FLAC_API FLAC__bool FLAC__metadata_get_picture(const char *filename, FLAC__StreamMetadata **picture, FLAC__StreamMetadata_Picture_Type type, const char *mime_type, const FLAC__byte *description, uint32_t max_width, uint32_t max_height, uint32_t max_depth, uint32_t max_colors) +{ + FLAC__Metadata_SimpleIterator *it; + FLAC__uint64 max_area_seen = 0; + FLAC__uint64 max_depth_seen = 0; + + *picture = 0; + + it = FLAC__metadata_simple_iterator_new(); + if(0 == it) + return false; + if(!FLAC__metadata_simple_iterator_init(it, filename, /*read_only=*/true, /*preserve_file_stats=*/true)) { + FLAC__metadata_simple_iterator_delete(it); + return false; + } + do { + if(FLAC__metadata_simple_iterator_get_block_type(it) == FLAC__METADATA_TYPE_PICTURE) { + FLAC__StreamMetadata *obj = FLAC__metadata_simple_iterator_get_block(it); + FLAC__uint64 area = (FLAC__uint64)obj->data.picture.width * (FLAC__uint64)obj->data.picture.height; + /* check constraints */ + if( + (type == (FLAC__StreamMetadata_Picture_Type)(-1) || type == obj->data.picture.type) && + (mime_type == 0 || !strcmp(mime_type, obj->data.picture.mime_type)) && + (description == 0 || !strcmp((const char *)description, (const char *)obj->data.picture.description)) && + obj->data.picture.width <= max_width && + obj->data.picture.height <= max_height && + obj->data.picture.depth <= max_depth && + obj->data.picture.colors <= max_colors && + (area > max_area_seen || (area == max_area_seen && obj->data.picture.depth > max_depth_seen)) + ) { + if(*picture) + FLAC__metadata_object_delete(*picture); + *picture = obj; + max_area_seen = area; + max_depth_seen = obj->data.picture.depth; + } + else { + FLAC__metadata_object_delete(obj); + } + } + } while(FLAC__metadata_simple_iterator_next(it)); + + FLAC__metadata_simple_iterator_delete(it); + + return (0 != *picture); +} + + +/**************************************************************************** + * + * Level 1 implementation + * + ***************************************************************************/ + +#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4) +/* 1 for initial offset, +4 for our own personal use */ + +struct FLAC__Metadata_SimpleIterator { + FILE *file; + char *filename, *tempfile_path_prefix; + struct flac_stat_s stats; + FLAC__bool has_stats; + FLAC__bool is_writable; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__off_t offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH]; + FLAC__off_t first_offset; /* this is the offset to the STREAMINFO block */ + uint32_t depth; + /* this is the metadata block header of the current block we are pointing to: */ + FLAC__bool is_last; + FLAC__MetadataType type; + uint32_t length; +}; + +FLAC_API const char * const FLAC__Metadata_SimpleIteratorStatusString[] = { + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR" +}; + + +FLAC_API FLAC__Metadata_SimpleIterator *FLAC__metadata_simple_iterator_new(void) +{ + FLAC__Metadata_SimpleIterator *iterator = calloc(1, sizeof(FLAC__Metadata_SimpleIterator)); + + if(0 != iterator) { + iterator->file = 0; + iterator->filename = 0; + iterator->tempfile_path_prefix = 0; + iterator->has_stats = false; + iterator->is_writable = false; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + iterator->first_offset = iterator->offset[0] = -1; + iterator->depth = 0; + } + + return iterator; +} + +static void simple_iterator_free_guts_(FLAC__Metadata_SimpleIterator *iterator) +{ + if(0 != iterator->file) { + fclose(iterator->file); + iterator->file = 0; + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + } + if(0 != iterator->filename) { + free(iterator->filename); + iterator->filename = 0; + } + if(0 != iterator->tempfile_path_prefix) { + free(iterator->tempfile_path_prefix); + iterator->tempfile_path_prefix = 0; + } +} + +FLAC_API void FLAC__metadata_simple_iterator_delete(FLAC__Metadata_SimpleIterator *iterator) +{ + simple_iterator_free_guts_(iterator); + free(iterator); +} + +FLAC_API FLAC__Metadata_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__Metadata_SimpleIteratorStatus status; + + status = iterator->status; + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return status; +} + +static FLAC__bool simple_iterator_prime_input_(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool read_only) +{ + uint32_t ret; + + if(read_only || 0 == (iterator->file = flac_fopen(iterator->filename, "r+b"))) { + iterator->is_writable = false; + if(read_only || errno == EACCES) { + if(0 == (iterator->file = flac_fopen(iterator->filename, "rb"))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + } + else { + iterator->is_writable = true; + } + + ret = seek_to_first_metadata_block_(iterator->file); + switch(ret) { + case 0: + iterator->depth = 0; + iterator->first_offset = iterator->offset[iterator->depth] = ftello(iterator->file); + return read_metadata_block_header_(iterator); + case 1: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + case 2: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + case 3: + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE; + return false; + default: + return false; + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__Metadata_SimpleIterator *iterator, const char *filename, FLAC__bool read_only, FLAC__bool preserve_file_stats) +{ + const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'flac_rename(...)' for what it will take to finish implementing this */ + + simple_iterator_free_guts_(iterator); + + if(!read_only && preserve_file_stats) + iterator->has_stats = get_file_stats_(filename, &iterator->stats); + + if(0 == (iterator->filename = strdup(filename))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + return simple_iterator_prime_input_(iterator, read_only); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__Metadata_SimpleIterator *iterator) +{ + return iterator->is_writable; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__Metadata_SimpleIterator *iterator) +{ + if(iterator->is_last) + return false; + + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + iterator->offset[iterator->depth] = ftello(iterator->file); + + return read_metadata_block_header_(iterator); +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__off_t this_offset; + + if(iterator->offset[iterator->depth] == iterator->first_offset) + return false; + + if(0 != fseeko(iterator->file, iterator->first_offset, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = iterator->first_offset; + if(!read_metadata_block_header_(iterator)) + return false; + + /* we ignore any error from ftello() and catch it in fseeko() */ + while(ftello(iterator->file) + (FLAC__off_t)iterator->length < iterator->offset[iterator->depth]) { + if(0 != fseeko(iterator->file, iterator->length, SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + this_offset = ftello(iterator->file); + if(!read_metadata_block_header_(iterator)) + return false; + } + + iterator->offset[iterator->depth] = this_offset; + + return true; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_SimpleIterator *iterator) +{ + return iterator->is_last; +} + +/*@@@@add to tests*/ +FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator) +{ + return (off_t)iterator->offset[iterator->depth]; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__Metadata_SimpleIterator *iterator) +{ + return iterator->type; +} + +/*@@@@add to tests*/ +FLAC_API uint32_t FLAC__metadata_simple_iterator_get_block_length(const FLAC__Metadata_SimpleIterator *iterator) +{ + return iterator->length; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_get_application_id(FLAC__Metadata_SimpleIterator *iterator, FLAC__byte *id) +{ + const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(iterator->type != FLAC__METADATA_TYPE_APPLICATION) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(fread(id, 1, id_bytes, iterator->file) != id_bytes) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + /* back up */ + if(0 != fseeko(iterator->file, -((int)id_bytes), SEEK_CUR)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return true; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_simple_iterator_get_block(FLAC__Metadata_SimpleIterator *iterator) +{ + FLAC__StreamMetadata *block = FLAC__metadata_object_new(iterator->type); + + if(0 != block) { + block->is_last = iterator->is_last; + block->length = iterator->length; + + if(!read_metadata_block_data_(iterator, block)) { + FLAC__metadata_object_delete(block); + return 0; + } + + /* back up to the beginning of the block data to stay consistent */ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + FLAC__metadata_object_delete(block); + return 0; + } + } + else + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + return block; +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + FLAC__bool ret; + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) { + if(iterator->type != block->type) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + } + + block->is_last = iterator->is_last; + + if(iterator->length == block->length) + return write_metadata_block_stationary_(iterator, block); + else if(iterator->length > block->length) { + if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) { + ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last); + return ret; + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + return ret; + } + } + else /* iterator->length < block->length */ { + uint32_t padding_leftover = 0; + FLAC__bool padding_is_last = false; + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + const uint32_t extra_padding_bytes_required = block->length - iterator->length; + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < extra_padding_bytes_required) + use_padding = false; + else { + padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + return ret; + } + else { + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/false); + return ret; + } + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool use_padding) +{ + uint32_t padding_leftover = 0; + FLAC__bool padding_is_last = false; + + FLAC__bool ret; + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + block->is_last = iterator->is_last; + + if(use_padding) { + /* first see if we can even use padding */ + if(iterator->is_last) { + use_padding = false; + } + else { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_next(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + if(iterator->type != FLAC__METADATA_TYPE_PADDING) { + use_padding = false; + } + else { + if(iterator->length == block->length) { + padding_leftover = 0; + block->is_last = iterator->is_last; + } + else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) + use_padding = false; + else { + padding_leftover = iterator->length - block->length; + padding_is_last = iterator->is_last; + block->is_last = false; + } + } + if(!simple_iterator_pop_(iterator)) + return false; + } + } + if(use_padding) { + /* move to the next block, which is suitable padding */ + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + if(padding_leftover == 0) { + ret = write_metadata_block_stationary_(iterator, block); + return ret; + } + else { + ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last); + return ret; + } + } + else { + ret = rewrite_whole_file_(iterator, block, /*append=*/true); + return ret; + } +} + +FLAC_API FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__Metadata_SimpleIterator *iterator, FLAC__bool use_padding) +{ + FLAC__bool ret; + + if(!iterator->is_writable) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE; + return false; + } + + if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT; + return false; + } + + if(use_padding) { + FLAC__StreamMetadata *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if(0 == padding) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + padding->length = iterator->length; + if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) { + FLAC__metadata_object_delete(padding); + return false; + } + FLAC__metadata_object_delete(padding); + if(!FLAC__metadata_simple_iterator_prev(iterator)) + return false; + return true; + } + else { + ret = rewrite_whole_file_(iterator, 0, /*append=*/false); + return ret; + } +} + + + +/**************************************************************************** + * + * Level 2 implementation + * + ***************************************************************************/ + + +typedef struct FLAC__Metadata_Node { + FLAC__StreamMetadata *data; + struct FLAC__Metadata_Node *prev, *next; +} FLAC__Metadata_Node; + +struct FLAC__Metadata_Chain { + char *filename; /* will be NULL if using callbacks */ + FLAC__bool is_ogg; + FLAC__Metadata_Node *head; + FLAC__Metadata_Node *tail; + uint32_t nodes; + FLAC__Metadata_ChainStatus status; + FLAC__off_t first_offset, last_offset; + /* + * This is the length of the chain initially read from the FLAC file. + * it is used to compare against the current length to decide whether + * or not the whole file has to be rewritten. + */ + FLAC__off_t initial_length; + /* @@@ hacky, these are currently only needed by ogg reader */ + FLAC__IOHandle handle; + FLAC__IOCallback_Read read_cb; +}; + +struct FLAC__Metadata_Iterator { + FLAC__Metadata_Chain *chain; + FLAC__Metadata_Node *current; +}; + +FLAC_API const char * const FLAC__Metadata_ChainStatusString[] = { + "FLAC__METADATA_CHAIN_STATUS_OK", + "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT", + "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE", + "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE", + "FLAC__METADATA_CHAIN_STATUS_BAD_METADATA", + "FLAC__METADATA_CHAIN_STATUS_READ_ERROR", + "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR", + "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR", + "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR", + "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR", + "FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS", + "FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH", + "FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL" +}; + + +static FLAC__Metadata_Node *node_new_(void) +{ + return calloc(1, sizeof(FLAC__Metadata_Node)); +} + +static void node_delete_(FLAC__Metadata_Node *node) +{ + if(0 != node->data) + FLAC__metadata_object_delete(node->data); + free(node); +} + +static void chain_init_(FLAC__Metadata_Chain *chain) +{ + chain->filename = 0; + chain->is_ogg = false; + chain->head = chain->tail = 0; + chain->nodes = 0; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + chain->initial_length = 0; + chain->read_cb = 0; +} + +static void chain_clear_(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *next; + + for(node = chain->head; node; ) { + next = node->next; + node_delete_(node); + node = next; + } + + if(0 != chain->filename) + free(chain->filename); + + chain_init_(chain); +} + +static void chain_append_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + node->next = node->prev = 0; + node->data->is_last = true; + if(0 != chain->tail) + chain->tail->data->is_last = false; + + if(0 == chain->head) + chain->head = node; + else { + chain->tail->next = node; + node->prev = chain->tail; + } + chain->tail = node; + chain->nodes++; +} + +static void chain_remove_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + if(node == chain->head) + chain->head = node->next; + else + node->prev->next = node->next; + + if(node == chain->tail) + chain->tail = node->prev; + else + node->next->prev = node->prev; + + if(0 != chain->tail) + chain->tail->data->is_last = true; + + chain->nodes--; +} + +static void chain_delete_node_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + chain_remove_node_(chain, node); + node_delete_(node); +} + +static FLAC__off_t chain_calculate_length_(FLAC__Metadata_Chain *chain) +{ + const FLAC__Metadata_Node *node; + FLAC__off_t length = 0; + for(node = chain->head; node; node = node->next) + length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + return length; +} + +static void iterator_insert_node_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + node->data->is_last = false; + + node->prev = iterator->current->prev; + node->next = iterator->current; + + if(0 == node->prev) + iterator->chain->head = node; + else + node->prev->next = node; + + iterator->current->prev = node; + + iterator->chain->nodes++; +} + +static void iterator_insert_node_after_(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Node *node) +{ + iterator->current->data->is_last = false; + + node->prev = iterator->current; + node->next = iterator->current->next; + + if(0 == node->next) + iterator->chain->tail = node; + else + node->next->prev = node; + + node->prev->next = node; + + iterator->chain->tail->data->is_last = true; + + iterator->chain->nodes++; +} + +/* return true iff node and node->next are both padding */ +static FLAC__bool chain_merge_adjacent_padding_(FLAC__Metadata_Chain *chain, FLAC__Metadata_Node *node) +{ + if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) { + const uint32_t growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length; + node->data->length += growth; /* new block size can be greater than max metadata block size, but it'll be fixed later in chain_prepare_for_write_() */ + + chain_delete_node_(chain, node->next); + return true; + } + else + return false; +} + +/* Returns the new length of the chain, or 0 if there was an error. */ +/* WATCHOUT: This can get called multiple times before a write, so + * it should still work when this happens. + */ +/* WATCHOUT: Make sure to also update the logic in + * FLAC__metadata_chain_check_if_tempfile_needed() if the logic here changes. + */ +static FLAC__off_t chain_prepare_for_write_(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + FLAC__off_t current_length = chain_calculate_length_(chain); + + if(use_padding) { + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + const FLAC__off_t delta = chain->initial_length - current_length; + chain->tail->data->length += (uint32_t)delta; + current_length += delta; + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + FLAC__StreamMetadata *padding; + FLAC__Metadata_Node *node; + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + padding->length = (uint32_t)(chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + current_length)); + if(0 == (node = node_new_())) { + FLAC__metadata_object_delete(padding); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return 0; + } + node->data = padding; + chain_append_node_(chain, node); + current_length = chain_calculate_length_(chain); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const FLAC__off_t delta = current_length - chain->initial_length; + if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((FLAC__off_t)chain->tail->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + chain_delete_node_(chain, chain->tail); + current_length = chain_calculate_length_(chain); + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((FLAC__off_t)chain->tail->data->length >= delta) { + chain->tail->data->length -= (uint32_t)delta; + current_length -= delta; + } + } + } + } + + /* check sizes of all metadata blocks; reduce padding size if necessary */ + { + FLAC__Metadata_Node *node; + for (node = chain->head; node; node = node->next) { + if(node->data->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + node->data->length = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + current_length = chain_calculate_length_(chain); + } else { + chain->status = FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + return 0; + } + } + } + } + + return current_length; +} + +static FLAC__bool chain_read_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Tell tell_cb) +{ + FLAC__Metadata_Node *node; + + /* we assume we're already at the beginning of the file */ + + switch(seek_to_first_metadata_block_cb_(handle, read_cb, seek_cb)) { + case 0: + break; + case 1: + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + case 2: + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + case 3: + chain->status = FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + return false; + default: + return false; + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->first_offset = (FLAC__off_t)pos; + } + + { + FLAC__bool is_last; + FLAC__MetadataType type; + uint32_t length; + + do { + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + if(!read_metadata_block_header_cb_(handle, read_cb, &is_last, &type, &length)) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + + node->data = FLAC__metadata_object_new(type); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + node->data->is_last = is_last; + node->data->length = length; + + chain->status = get_equivalent_status_(read_metadata_block_data_cb_(handle, read_cb, seek_cb, node->data)); + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + node_delete_(node); + return false; + } + chain_append_node_(chain, node); + } while(!is_last); + } + + { + FLAC__int64 pos = tell_cb(handle); + if(pos < 0) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + return false; + } + chain->last_offset = (FLAC__off_t)pos; + } + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__StreamDecoderReadStatus chain_read_ogg_read_cb_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder; + if(*bytes > 0 && chain->status == FLAC__METADATA_CHAIN_STATUS_OK) { + *bytes = chain->read_cb(buffer, sizeof(FLAC__byte), *bytes, chain->handle); + if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; +} + +static FLAC__StreamDecoderWriteStatus chain_read_ogg_write_cb_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + (void)decoder, (void)frame, (void)buffer, (void)client_data; + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; +} + +static void chain_read_ogg_metadata_cb_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + FLAC__Metadata_Node *node; + + (void)decoder; + + node = node_new_(); + if(0 == node) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + node->data = FLAC__metadata_object_clone(metadata); + if(0 == node->data) { + node_delete_(node); + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return; + } + + chain_append_node_(chain, node); +} + +static void chain_read_ogg_error_cb_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + FLAC__Metadata_Chain *chain = (FLAC__Metadata_Chain*)client_data; + (void)decoder, (void)status; + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ +} + +static FLAC__bool chain_read_ogg_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb) +{ + FLAC__StreamDecoder *decoder; + + /* we assume we're already at the beginning of the file */ + + chain->handle = handle; + chain->read_cb = read_cb; + if(0 == (decoder = FLAC__stream_decoder_new())) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + FLAC__stream_decoder_set_metadata_respond_all(decoder); + if(FLAC__stream_decoder_init_ogg_stream(decoder, chain_read_ogg_read_cb_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, chain_read_ogg_write_cb_, chain_read_ogg_metadata_cb_, chain_read_ogg_error_cb_, chain) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + return false; + } + + chain->first_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; /*@@@ maybe needs better error code */ + if(chain->status != FLAC__METADATA_CHAIN_STATUS_OK) { + FLAC__stream_decoder_delete(decoder); + return false; + } + + FLAC__stream_decoder_delete(decoder); + + chain->last_offset = 0; /*@@@ wrong; will need to be set correctly to implement metadata writing for Ogg FLAC */ + + chain->initial_length = chain_calculate_length_(chain); + + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__Metadata_Node *node; + + if(0 != seek_cb(handle, chain->first_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(handle, write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return true; +} + +static FLAC__bool chain_rewrite_metadata_in_place_(FLAC__Metadata_Chain *chain) +{ + FILE *file; + FLAC__bool ret; + + if(0 == (file = flac_fopen(chain->filename, "r+b"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* chain_rewrite_metadata_in_place_cb_() sets chain->status for us */ + ret = chain_rewrite_metadata_in_place_cb_(chain, (FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, fseek_wrapper_); + + fclose(file); + + return ret; +} + +static FLAC__bool chain_rewrite_file_(FLAC__Metadata_Chain *chain, const char *tempfile_path_prefix) +{ + FILE *f, *tempfile = NULL; + char *tempfilename; + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + /* copy the file prefix (data up to first metadata block */ + if(0 == (f = flac_fopen(chain->filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + goto err; + } + if(!write_metadata_block_data_(tempfile, &status, node->data)) { + chain->status = get_equivalent_status_(status); + goto err; + } + } + /*FLAC__ASSERT(fflush(), ftello() == chain->last_offset);*/ + + /* copy the file postfix (everything after the metadata) */ + if(0 != fseeko(f, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + goto err; + } + if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) { + chain->status = get_equivalent_status_(status); + goto err; + } + + /* move the tempfile on top of the original */ + (void)fclose(f); + if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status)) + return false; + + return true; + +err: + (void)fclose(f); + cleanup_tempfile_(&tempfile, &tempfilename); + return false; +} + +/* assumes 'handle' is already at beginning of file */ +static FLAC__bool chain_rewrite_file_cb_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb) +{ + FLAC__Metadata_SimpleIteratorStatus status; + const FLAC__Metadata_Node *node; + + /* copy the file prefix (data up to first metadata block */ + if(!copy_n_bytes_from_file_cb_(handle, read_cb, temp_handle, temp_write_cb, chain->first_offset, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + /* write the metadata */ + for(node = chain->head; node; node = node->next) { + if(!write_metadata_block_header_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + if(!write_metadata_block_data_cb_(temp_handle, temp_write_cb, node->data)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + return false; + } + } + + /* copy the file postfix (everything after the metadata) */ + if(0 != seek_cb(handle, chain->last_offset, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_cb_(handle, read_cb, eof_cb, temp_handle, temp_write_cb, &status)) { + chain->status = get_equivalent_status_(status); + return false; + } + + return true; +} + +FLAC_API FLAC__Metadata_Chain *FLAC__metadata_chain_new(void) +{ + FLAC__Metadata_Chain *chain = calloc(1, sizeof(FLAC__Metadata_Chain)); + + if(0 != chain) + chain_init_(chain); + + return chain; +} + +FLAC_API void FLAC__metadata_chain_delete(FLAC__Metadata_Chain *chain) +{ + chain_clear_(chain); + + free(chain); +} + +FLAC_API FLAC__Metadata_ChainStatus FLAC__metadata_chain_status(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_ChainStatus status; + + status = chain->status; + chain->status = FLAC__METADATA_CHAIN_STATUS_OK; + return status; +} + +static FLAC__bool chain_read_(FLAC__Metadata_Chain *chain, const char *filename, FLAC__bool is_ogg) +{ + FILE *file; + FLAC__bool ret; + + chain_clear_(chain); + + if(0 == (chain->filename = strdup(filename))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + + chain->is_ogg = is_ogg; + + if(0 == (file = flac_fopen(filename, "rb"))) { + chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, file, (FLAC__IOCallback_Read)fread) : + chain_read_cb_(chain, file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, ftell_wrapper_) + ; + + fclose(file); + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg(FLAC__Metadata_Chain *chain, const char *filename) +{ + return chain_read_(chain, filename, /*is_ogg=*/true); +} + +static FLAC__bool chain_read_with_callbacks_(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__bool is_ogg) +{ + FLAC__bool ret; + + chain_clear_(chain); + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.tell) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + chain->is_ogg = is_ogg; + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + /* the function also sets chain->status for us */ + ret = is_ogg? + chain_read_ogg_cb_(chain, handle, callbacks.read) : + chain_read_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.tell) + ; + + return ret; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_read_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/false); +} + +/*@@@@add to tests*/ +FLAC_API FLAC__bool FLAC__metadata_chain_read_ogg_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + return chain_read_with_callbacks_(chain, handle, callbacks, /*is_ogg=*/true); +} + +typedef enum { + LBS_NONE = 0, + LBS_SIZE_CHANGED, + LBS_BLOCK_ADDED, + LBS_BLOCK_REMOVED +} LastBlockState; + +FLAC_API FLAC__bool FLAC__metadata_chain_check_if_tempfile_needed(FLAC__Metadata_Chain *chain, FLAC__bool use_padding) +{ + /* This does all the same checks that are in chain_prepare_for_write_() + * but doesn't actually alter the chain. Make sure to update the logic + * here if chain_prepare_for_write_() changes. + */ + FLAC__off_t current_length; + LastBlockState lbs_state = LBS_NONE; + uint32_t lbs_size = 0; + + current_length = chain_calculate_length_(chain); + + if(use_padding) { + const FLAC__Metadata_Node * const node = chain->tail; + /* if the metadata shrank and the last block is padding, we just extend the last padding block */ + if(current_length < chain->initial_length && node->data->type == FLAC__METADATA_TYPE_PADDING) { + lbs_state = LBS_SIZE_CHANGED; + lbs_size = (uint32_t)(node->data->length + (chain->initial_length - current_length)); + } + /* if the metadata shrank more than 4 bytes then there's room to add another padding block */ + else if(current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) { + lbs_state = LBS_BLOCK_ADDED; + lbs_size = (uint32_t)(chain->initial_length - (current_length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH)); + } + /* if the metadata grew but the last block is padding, try cutting the padding to restore the original length so we don't have to rewrite the whole file */ + else if(current_length > chain->initial_length) { + const FLAC__off_t delta = current_length - chain->initial_length; + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + /* if the delta is exactly the size of the last padding block, remove the padding block */ + if((FLAC__off_t)node->data->length + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH == delta) { + lbs_state = LBS_BLOCK_REMOVED; + lbs_size = 0; + } + /* if there is at least 'delta' bytes of padding, trim the padding down */ + else if((FLAC__off_t)node->data->length >= delta) { + lbs_state = LBS_SIZE_CHANGED; + lbs_size = (uint32_t)(node->data->length - delta); + } + } + } + } + + current_length = 0; + /* check sizes of all metadata blocks; reduce padding size if necessary */ + { + const FLAC__Metadata_Node *node; + for(node = chain->head; node; node = node->next) { + uint32_t block_len = node->data->length; + if(node == chain->tail) { + if(lbs_state == LBS_BLOCK_REMOVED) + continue; + else if(lbs_state == LBS_SIZE_CHANGED) + block_len = lbs_size; + } + if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) + block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + else + return false /* the return value doesn't matter */; + } + current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len); + } + + if(lbs_state == LBS_BLOCK_ADDED) { + /* test added padding block */ + uint32_t block_len = lbs_size; + if(block_len >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + block_len = (1u << FLAC__STREAM_METADATA_LENGTH_LEN) - 1; + current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + block_len); + } + } + + return (current_length != chain->initial_length); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats) +{ + struct flac_stat_s stats; + const char *tempfile_path_prefix = 0; + FLAC__off_t current_length; + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 == chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + if(preserve_file_stats) + get_file_stats_(chain->filename, &stats); + + if(current_length == chain->initial_length) { + if(!chain_rewrite_metadata_in_place_(chain)) + return false; + } + else { + if(!chain_rewrite_file_(chain, tempfile_path_prefix)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + } + + if(preserve_file_stats) + set_file_stats_(chain->filename, &stats); + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks) +{ + FLAC__off_t current_length; + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.write || 0 == callbacks.seek) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + return chain_rewrite_metadata_in_place_cb_(chain, handle, callbacks.write, callbacks.seek); +} + +FLAC_API FLAC__bool FLAC__metadata_chain_write_with_callbacks_and_tempfile(FLAC__Metadata_Chain *chain, FLAC__bool use_padding, FLAC__IOHandle handle, FLAC__IOCallbacks callbacks, FLAC__IOHandle temp_handle, FLAC__IOCallbacks temp_callbacks) +{ + FLAC__off_t current_length; + + if (chain->is_ogg) { /* cannot write back to Ogg FLAC yet */ + chain->status = FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + return false; + } + + if (0 != chain->filename) { + chain->status = FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH; + return false; + } + + if (0 == callbacks.read || 0 == callbacks.seek || 0 == callbacks.eof) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + if (0 == temp_callbacks.write) { + chain->status = FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS; + return false; + } + + if (!FLAC__metadata_chain_check_if_tempfile_needed(chain, use_padding)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL; + return false; + } + + current_length = chain_prepare_for_write_(chain, use_padding); + + /* a return value of 0 means there was an error; chain->status is already set */ + if (0 == current_length) + return false; + + /* rewind */ + if(0 != callbacks.seek(handle, 0, SEEK_SET)) { + chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + return false; + } + + if(!chain_rewrite_file_cb_(chain, handle, callbacks.read, callbacks.seek, callbacks.eof, temp_handle, temp_callbacks.write)) + return false; + + /* recompute lengths and offsets */ + { + const FLAC__Metadata_Node *node; + chain->initial_length = current_length; + chain->last_offset = chain->first_offset; + for(node = chain->head; node; node = node->next) + chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length); + } + + return true; +} + +FLAC_API void FLAC__metadata_chain_merge_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node; + + for(node = chain->head; node; ) { + if(!chain_merge_adjacent_padding_(chain, node)) + node = node->next; + } +} + +FLAC_API void FLAC__metadata_chain_sort_padding(FLAC__Metadata_Chain *chain) +{ + FLAC__Metadata_Node *node, *save; + uint32_t i; + + /* + * Don't try and be too smart... this simple algo is good enough for + * the small number of nodes that we deal with. + */ + for(i = 0, node = chain->head; i < chain->nodes; i++) { + if(node->data->type == FLAC__METADATA_TYPE_PADDING) { + save = node->next; + chain_remove_node_(chain, node); + chain_append_node_(chain, node); + node = save; + } + else { + node = node->next; + } + } + + FLAC__metadata_chain_merge_padding(chain); +} + + +FLAC_API FLAC__Metadata_Iterator *FLAC__metadata_iterator_new(void) +{ + FLAC__Metadata_Iterator *iterator = calloc(1, sizeof(FLAC__Metadata_Iterator)); + + /* calloc() implies: + iterator->current = 0; + iterator->chain = 0; + */ + + return iterator; +} + +FLAC_API void FLAC__metadata_iterator_delete(FLAC__Metadata_Iterator *iterator) +{ + free(iterator); +} + +FLAC_API void FLAC__metadata_iterator_init(FLAC__Metadata_Iterator *iterator, FLAC__Metadata_Chain *chain) +{ + iterator->chain = chain; + iterator->current = chain->head; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_next(FLAC__Metadata_Iterator *iterator) +{ + if(0 == iterator->current || 0 == iterator->current->next) + return false; + + iterator->current = iterator->current->next; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_prev(FLAC__Metadata_Iterator *iterator) +{ + if(0 == iterator->current || 0 == iterator->current->prev) + return false; + + iterator->current = iterator->current->prev; + return true; +} + +FLAC_API FLAC__MetadataType FLAC__metadata_iterator_get_block_type(const FLAC__Metadata_Iterator *iterator) +{ + return iterator->current->data->type; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_iterator_get_block(FLAC__Metadata_Iterator *iterator) +{ + return iterator->current->data; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_set_block(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block); +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__Metadata_Iterator *iterator, FLAC__bool replace_with_padding) +{ + FLAC__Metadata_Node *save; + + if(0 == iterator->current->prev) { + return false; + } + + save = iterator->current->prev; + + if(replace_with_padding) { + FLAC__metadata_object_delete_data(iterator->current->data); + iterator->current->data->type = FLAC__METADATA_TYPE_PADDING; + } + else { + chain_delete_node_(iterator->chain, iterator->current); + } + + iterator->current = save; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == iterator->current->prev) { + return false; + } + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_(iterator, node); + iterator->current = node; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__Metadata_Iterator *iterator, FLAC__StreamMetadata *block) +{ + FLAC__Metadata_Node *node; + + if(block->type == FLAC__METADATA_TYPE_STREAMINFO) + return false; + + if(0 == (node = node_new_())) + return false; + + node->data = block; + iterator_insert_node_after_(iterator, node); + iterator->current = node; + return true; +} + + +/**************************************************************************** + * + * Local function definitions + * + ***************************************************************************/ + +void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes) +{ + uint32_t i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, uint32_t bytes) +{ + uint32_t i; + + for(i = 0; i < bytes; i++) { + *(b++) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, uint32_t bytes) +{ + uint32_t i; + + b += bytes; + + for(i = 0; i < bytes; i++) { + *(--b) = (FLAC__byte)(val & 0xff); + val >>= 8; + } +} + +FLAC__uint32 unpack_uint32_(FLAC__byte *b, uint32_t bytes) +{ + FLAC__uint32 ret = 0; + uint32_t i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*b++); + + return ret; +} + +FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, uint32_t bytes) +{ + FLAC__uint32 ret = 0; + uint32_t i; + + b += bytes; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint32)(*--b); + + return ret; +} + +FLAC__uint64 unpack_uint64_(FLAC__byte *b, uint32_t bytes) +{ + FLAC__uint64 ret = 0; + uint32_t i; + + for(i = 0; i < bytes; i++) + ret = (ret << 8) | (FLAC__uint64)(*b++); + + return ret; +} + +FLAC__bool read_metadata_block_header_(FLAC__Metadata_SimpleIterator *iterator) +{ + if(!read_metadata_block_header_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, &iterator->is_last, &iterator->type, &iterator->length)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + + return true; +} + +FLAC__bool read_metadata_block_data_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block) +{ + iterator->status = read_metadata_block_data_cb_((FLAC__IOHandle)iterator->file, (FLAC__IOCallback_Read)fread, fseek_wrapper_, block); + + return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK); +} + +FLAC__bool read_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__bool *is_last, FLAC__MetadataType *type, uint32_t *length) +{ + FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + if(read_cb(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + *is_last = raw_header[0] & 0x80? true : false; + *type = (FLAC__MetadataType)(raw_header[0] & 0x7f); + *length = unpack_uint32_(raw_header + 1, 3); + + /* Note that we don't check: + * if(iterator->type >= FLAC__METADATA_TYPE_UNDEFINED) + * we just will read in an opaque block + */ + + return true; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata *block) +{ + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return read_metadata_block_data_streaminfo_cb_(handle, read_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return read_metadata_block_data_padding_cb_(handle, seek_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return read_metadata_block_data_application_cb_(handle, read_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return read_metadata_block_data_seektable_cb_(handle, read_cb, &block->data.seek_table, block->length); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return read_metadata_block_data_vorbis_comment_cb_(handle, read_cb, seek_cb, &block->data.vorbis_comment, block->length); + case FLAC__METADATA_TYPE_CUESHEET: + return read_metadata_block_data_cuesheet_cb_(handle, read_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return read_metadata_block_data_picture_cb_(handle, read_cb, &block->data.picture); + default: + return read_metadata_block_data_unknown_cb_(handle, read_cb, &block->data.unknown, block->length); + } +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b; + + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + b = buffer; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level unpacker and use the + * _STREAMINFO_ constants. + */ + block->min_blocksize = unpack_uint32_(b, 2); b += 2; + block->max_blocksize = unpack_uint32_(b, 2); b += 2; + block->min_framesize = unpack_uint32_(b, 3); b += 3; + block->max_framesize = unpack_uint32_(b, 3); b += 3; + block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((uint32_t)(b[2] & 0xf0) >> 4); + block->channels = (uint32_t)((b[2] & 0x0e) >> 1) + 1; + block->bits_per_sample = ((((uint32_t)(b[2] & 0x01)) << 4) | (((uint32_t)(b[3] & 0xf0)) >> 4)) + 1; + block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4); + memcpy(block->md5sum, b+8, 16); + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_Padding *block, uint32_t block_length) +{ + (void)block; /* nothing to do; we don't care about reading the padding bytes */ + + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Application *block, uint32_t block_length) +{ + const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(read_cb(block->id, 1, id_bytes, handle) != id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + if(block_length < id_bytes) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + block_length -= id_bytes; + + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_SeekTable *block, uint32_t block_length) +{ + uint32_t i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + if(block->num_points == 0) + block->points = 0; + else if(0 == (block->points = safe_malloc_mul_2op_p(block->num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_points; i++) { + if(read_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + /* some MAGIC NUMBERs here */ + block->points[i].sample_number = unpack_uint64_(buffer, 8); + block->points[i].stream_offset = unpack_uint64_(buffer+8, 8); + block->points[i].frame_samples = unpack_uint32_(buffer+16, 2); + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_VorbisComment_Entry *entry, uint32_t max_length) +{ + const uint32_t entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + if(max_length < entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA; + + max_length -= entry_length_len; + if(read_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + entry->length = unpack_uint32_little_endian_(buffer, entry_length_len); + if(max_length < entry->length) { + entry->length = 0; + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA; + } else max_length -= entry->length; + + if(0 != entry->entry) + free(entry->entry); + + if(entry->length == 0) { + entry->entry = 0; + } + else { + if(0 == (entry->entry = safe_malloc_add_2op_(entry->length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(entry->entry, 1, entry->length, handle) != entry->length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + entry->entry[entry->length] = '\0'; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb, FLAC__StreamMetadata_VorbisComment *block, uint32_t block_length) +{ + uint32_t i; + FLAC__Metadata_SimpleIteratorStatus status; + const uint32_t num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, &(block->vendor_string), block_length); + if(block_length >= 4) + block_length -= 4; + if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA) + goto skip; + else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + block_length -= block->vendor_string.length; + + if(block_length < num_comments_len) goto skip; else block_length -= num_comments_len; + if(read_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len); + + if(block->num_comments == 0) { + block->comments = 0; + } + else if(0 == (block->comments = calloc(block->num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + block->num_comments = 0; + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + } + + for(i = 0; i < block->num_comments; i++) { + status = read_metadata_block_data_vorbis_comment_entry_cb_(handle, read_cb, block->comments + i, block_length); + if(block_length >= 4) block_length -= 4; + if(status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA) { + block->num_comments = i; + goto skip; + } + else if(status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) return status; + block_length -= block->comments[i].length; + } + + skip: + if(block_length > 0) { + /* bad metadata */ + if(0 != seek_cb(handle, block_length, SEEK_CUR)) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_track_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet_Track *track) +{ + uint32_t i, len; + FLAC__byte buffer[32]; /* asserted below that this is big enough */ + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->offset = unpack_uint64_(buffer, len); + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->number = (FLAC__byte)unpack_uint32_(buffer, len); + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(read_cb(track->isrc, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->type = buffer[0] >> 7; + track->pre_emphasis = (buffer[0] >> 6) & 1; + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->num_indices = (FLAC__byte)unpack_uint32_(buffer, len); + + if(track->num_indices == 0) { + track->indices = 0; + } + else if(0 == (track->indices = calloc(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < track->num_indices; i++) { + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].offset = unpack_uint64_(buffer, len); + + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + track->indices[i].number = (FLAC__byte)unpack_uint32_(buffer, len); + + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_CueSheet *block) +{ + uint32_t i, len; + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[1024]; /* MSVC needs a constant expression so we put a magic number and assert */ + + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(read_cb(block->media_catalog_number, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->lead_in = unpack_uint64_(buffer, len); + + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->is_cd = buffer[0]&0x80? true : false; + + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->num_tracks = unpack_uint32_(buffer, len); + + if(block->num_tracks == 0) { + block->tracks = 0; + } + else if(0 == (block->tracks = calloc(block->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + for(i = 0; i < block->num_tracks; i++) { + if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_cuesheet_track_cb_(handle, read_cb, block->tracks + i))) + return status; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +static FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cstring_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__byte **data, FLAC__uint32 *length, FLAC__uint32 length_len) +{ + FLAC__byte buffer[sizeof(FLAC__uint32)]; + + length_len /= 8; /* convert to bytes */ + + if(read_cb(buffer, 1, length_len, handle) != length_len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + *length = unpack_uint32_(buffer, length_len); + + if(0 != *data) + free(*data); + + if(0 == (*data = safe_malloc_add_2op_(*length, /*+*/1))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(*length > 0) { + if(read_cb(*data, 1, *length, handle) != *length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + (*data)[*length] = '\0'; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Picture *block) +{ + FLAC__Metadata_SimpleIteratorStatus status; + FLAC__byte buffer[4]; /* asserted below that this is big enough */ + FLAC__uint32 len; + + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->type = (FLAC__StreamMetadata_Picture_Type)unpack_uint32_(buffer, len); + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, (FLAC__byte**)(&(block->mime_type)), &len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->description), &len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->width = unpack_uint32_(buffer, len); + + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->height = unpack_uint32_(buffer, len); + + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->depth = unpack_uint32_(buffer, len); + + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN / 8; + if(read_cb(buffer, 1, len, handle) != len) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + block->colors = unpack_uint32_(buffer, len); + + /* for convenience we use read_metadata_block_data_picture_cstring_cb_() even though it adds an extra terminating NUL we don't use */ + if((status = read_metadata_block_data_picture_cstring_cb_(handle, read_cb, &(block->data), &(block->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) + return status; + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__Metadata_SimpleIteratorStatus read_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__StreamMetadata_Unknown *block, uint32_t block_length) +{ + if(block_length == 0) { + block->data = 0; + } + else { + if(0 == (block->data = malloc(block_length))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + if(read_cb(block->data, 1, block_length, handle) != block_length) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + } + + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; +} + +FLAC__bool write_metadata_block_header_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + if(!write_metadata_block_header_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_(FILE *file, FLAC__Metadata_SimpleIteratorStatus *status, const FLAC__StreamMetadata *block) +{ + if (write_metadata_block_data_cb_((FLAC__IOHandle)file, (FLAC__IOCallback_Write)fwrite, block)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; + return true; + } + else { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } +} + +FLAC__bool write_metadata_block_header_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH]; + + /* double protection */ + if(block->length >= (1u << FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; + + buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type; + pack_uint32_(block->length, buffer + 1, 3); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, handle) != FLAC__STREAM_METADATA_HEADER_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata *block) +{ + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return write_metadata_block_data_streaminfo_cb_(handle, write_cb, &block->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return write_metadata_block_data_padding_cb_(handle, write_cb, &block->data.padding, block->length); + case FLAC__METADATA_TYPE_APPLICATION: + return write_metadata_block_data_application_cb_(handle, write_cb, &block->data.application, block->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return write_metadata_block_data_seektable_cb_(handle, write_cb, &block->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return write_metadata_block_data_vorbis_comment_cb_(handle, write_cb, &block->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return write_metadata_block_data_cuesheet_cb_(handle, write_cb, &block->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return write_metadata_block_data_picture_cb_(handle, write_cb, &block->data.picture); + default: + return write_metadata_block_data_unknown_cb_(handle, write_cb, &block->data.unknown, block->length); + } +} + +FLAC__bool write_metadata_block_data_streaminfo_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_StreamInfo *block) +{ + FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; + const uint32_t channels1 = block->channels - 1; + const uint32_t bps1 = block->bits_per_sample - 1; + + /* we are using hardcoded numbers for simplicity but we should + * probably eventually write a bit-level packer and use the + * _STREAMINFO_ constants. + */ + pack_uint32_(block->min_blocksize, buffer, 2); + pack_uint32_(block->max_blocksize, buffer+2, 2); + pack_uint32_(block->min_framesize, buffer+4, 3); + pack_uint32_(block->max_framesize, buffer+7, 3); + buffer[10] = (block->sample_rate >> 12) & 0xff; + buffer[11] = (block->sample_rate >> 4) & 0xff; + buffer[12] = (FLAC__byte)(((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4)); + buffer[13] = (FLAC__byte)(((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f)); + pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4); + memcpy(buffer+18, block->md5sum, 16); + + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, handle) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_padding_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Padding *block, uint32_t block_length) +{ + uint32_t i, n = block_length; + FLAC__byte buffer[1024]; + + (void)block; + + memset(buffer, 0, 1024); + + for(i = 0; i < n/1024; i++) + if(write_cb(buffer, 1, 1024, handle) != 1024) + return false; + + n %= 1024; + + if(write_cb(buffer, 1, n, handle) != n) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_application_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Application *block, uint32_t block_length) +{ + const uint32_t id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + + if(write_cb(block->id, 1, id_bytes, handle) != id_bytes) + return false; + + block_length -= id_bytes; + + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_seektable_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_SeekTable *block) +{ + uint32_t i; + FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; + + for(i = 0; i < block->num_points; i++) { + /* some MAGIC NUMBERs here */ + pack_uint64_(block->points[i].sample_number, buffer, 8); + pack_uint64_(block->points[i].stream_offset, buffer+8, 8); + pack_uint32_(block->points[i].frame_samples, buffer+16, 2); + if(write_cb(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, handle) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_vorbis_comment_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_VorbisComment *block) +{ + uint32_t i; + const uint32_t entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8; + const uint32_t num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->vendor_string.entry, 1, block->vendor_string.length, handle) != block->vendor_string.length) + return false; + + pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len); + if(write_cb(buffer, 1, num_comments_len, handle) != num_comments_len) + return false; + + for(i = 0; i < block->num_comments; i++) { + pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len); + if(write_cb(buffer, 1, entry_length_len, handle) != entry_length_len) + return false; + if(write_cb(block->comments[i].entry, 1, block->comments[i].length, handle) != block->comments[i].length) + return false; + } + + return true; +} + +FLAC__bool write_metadata_block_data_cuesheet_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_CueSheet *block) +{ + uint32_t i, j, len; + FLAC__byte buffer[1024]; /* asserted below that this is big enough */ + + len = FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN / 8; + if(write_cb(block->media_catalog_number, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN / 8; + pack_uint64_(block->lead_in, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = (FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN) / 8; + memset(buffer, 0, len); + if(block->is_cd) + buffer[0] |= 0x80; + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN / 8; + pack_uint32_(block->num_tracks, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(i = 0; i < block->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = block->tracks + i; + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN / 8; + pack_uint64_(track->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN / 8; + pack_uint32_(track->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN / 8; + if(write_cb(track->isrc, 1, len, handle) != len) + return false; + + len = (FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN) / 8; + memset(buffer, 0, len); + buffer[0] = (FLAC__byte)((track->type << 7) | (track->pre_emphasis << 6)); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN / 8; + pack_uint32_(track->num_indices, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + j; + + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN / 8; + pack_uint64_(indx->offset, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN / 8; + pack_uint32_(indx->number, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN / 8; + memset(buffer, 0, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + } + } + + return true; +} + +FLAC__bool write_metadata_block_data_picture_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Picture *block) +{ + uint32_t len; + size_t slen; + FLAC__byte buffer[4]; /* magic number is asserted below */ + + len = FLAC__STREAM_METADATA_PICTURE_TYPE_LEN/8; + pack_uint32_(block->type, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN/8; + slen = strlen(block->mime_type); + pack_uint32_((FLAC__uint32)slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->mime_type, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN/8; + slen = strlen((const char *)block->description); + pack_uint32_((FLAC__uint32)slen, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->description, 1, slen, handle) != slen) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN/8; + pack_uint32_(block->width, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN/8; + pack_uint32_(block->height, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN/8; + pack_uint32_(block->depth, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_COLORS_LEN/8; + pack_uint32_(block->colors, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + + len = FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN/8; + pack_uint32_(block->data_length, buffer, len); + if(write_cb(buffer, 1, len, handle) != len) + return false; + if(write_cb(block->data, 1, block->data_length, handle) != block->data_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_data_unknown_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Write write_cb, const FLAC__StreamMetadata_Unknown *block, uint32_t block_length) +{ + if(write_cb(block->data, 1, block_length, handle) != block_length) + return false; + + return true; +} + +FLAC__bool write_metadata_block_stationary_(FLAC__Metadata_SimpleIterator *iterator, const FLAC__StreamMetadata *block) +{ + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, uint32_t padding_length, FLAC__bool padding_is_last) +{ + FLAC__StreamMetadata *padding; + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + block->is_last = false; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, block)) + return false; + + if(!write_metadata_block_data_(iterator->file, &iterator->status, block)) + return false; + + if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) + return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + + padding->is_last = padding_is_last; + padding->length = padding_length; + + if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) { + FLAC__metadata_object_delete(padding); + return false; + } + + FLAC__metadata_object_delete(padding); + + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +FLAC__bool rewrite_whole_file_(FLAC__Metadata_SimpleIterator *iterator, FLAC__StreamMetadata *block, FLAC__bool append) +{ + FILE *tempfile = NULL; + char *tempfilename = NULL; + int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */ + FLAC__off_t fixup_is_last_flag_offset = -1; + + if(iterator->is_last) { + if(append) { + fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + } + else if(0 == block) { + simple_iterator_push_(iterator); + if(!FLAC__metadata_simple_iterator_prev(iterator)) { + (void)simple_iterator_pop_(iterator); + return false; + } + fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */ + fixup_is_last_flag_offset = iterator->offset[iterator->depth]; + if(!simple_iterator_pop_(iterator)) + return false; + } + } + + if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append)) + return false; + + if(0 != block) { + if(!write_metadata_block_header_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + + if(!write_metadata_block_data_(tempfile, &iterator->status, block)) { + cleanup_tempfile_(&tempfile, &tempfilename); + return false; + } + } + + if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0)) + return false; + + if(append) + return FLAC__metadata_simple_iterator_next(iterator); + + return true; +} + +void simple_iterator_push_(FLAC__Metadata_SimpleIterator *iterator) +{ + iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth]; + iterator->depth++; +} + +FLAC__bool simple_iterator_pop_(FLAC__Metadata_SimpleIterator *iterator) +{ + iterator->depth--; + if(0 != fseeko(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + + return read_metadata_block_header_(iterator); +} + +/* return meanings: + * 0: ok + * 1: read error + * 2: seek error + * 3: not a FLAC file + */ +uint32_t seek_to_first_metadata_block_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Seek seek_cb) +{ + FLAC__byte buffer[4]; + size_t n; + uint32_t i; + + /* skip any id3v2 tag */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + else if(0 == memcmp(buffer, "ID3", 3)) { + uint32_t tag_length = 0; + + /* skip to the tag length */ + if(seek_cb(handle, 2, SEEK_CUR) < 0) + return 2; + + /* read the length */ + for(i = 0; i < 4; i++) { + if(read_cb(buffer, 1, 1, handle) < 1 || buffer[0] & 0x80) + return 1; + tag_length <<= 7; + tag_length |= (buffer[0] & 0x7f); + } + + /* skip the rest of the tag */ + if(seek_cb(handle, tag_length, SEEK_CUR) < 0) + return 2; + + /* read the stream sync code */ + errno = 0; + n = read_cb(buffer, 1, 4, handle); + if(errno) + return 1; + else if(n != 4) + return 3; + } + + /* check for the fLaC signature */ + if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH)) + return 0; + else + return 3; +} + +uint32_t seek_to_first_metadata_block_(FILE *f) +{ + return seek_to_first_metadata_block_cb_((FLAC__IOHandle)f, (FLAC__IOCallback_Read)fread, fseek_wrapper_); +} + +FLAC__bool simple_iterator_copy_file_prefix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append) +{ + const FLAC__off_t offset_end = append? iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length : iterator->offset[iterator->depth]; + + if(0 != fseeko(iterator->file, 0, SEEK_SET)) { + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + return true; +} + +FLAC__bool simple_iterator_copy_file_postfix_(FLAC__Metadata_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, FLAC__off_t fixup_is_last_flag_offset, FLAC__bool backup) +{ + FLAC__off_t save_offset = iterator->offset[iterator->depth]; + + if(0 != fseeko(iterator->file, save_offset + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) { + cleanup_tempfile_(tempfile, tempfilename); + return false; + } + + if(fixup_is_last_code != 0) { + /* + * if code == 1, it means a block was appended to the end so + * we have to clear the is_last flag of the previous block + * if code == -1, it means the last block was deleted so + * we have to set the is_last flag of the previous block + */ + /* MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */ + FLAC__byte x; + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(fread(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(fixup_is_last_code > 0) { + x &= 0x7f; + } + else { + x |= 0x80; + } + if(0 != fseeko(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR; + return false; + } + if(local__fwrite(&x, 1, 1, *tempfile) != 1) { + cleanup_tempfile_(tempfile, tempfilename); + iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + (void)fclose(iterator->file); + + if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status)) + return false; + + if(iterator->has_stats) + set_file_stats_(iterator->filename, &iterator->stats); + + if(!simple_iterator_prime_input_(iterator, !iterator->is_writable)) + return false; + if(backup) { + while(iterator->offset[iterator->depth] + (FLAC__off_t)FLAC__STREAM_METADATA_HEADER_LENGTH + (FLAC__off_t)iterator->length < save_offset) + if(!FLAC__metadata_simple_iterator_next(iterator)) + return false; + return true; + } + else { + /* move the iterator to it's original block faster by faking a push, then doing a pop_ */ + iterator->offset[0] = save_offset; + iterator->depth++; + return simple_iterator_pop_(iterator); + } +} + +FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(bytes > 0) { + n = flac_min(sizeof(buffer), (size_t)bytes); + if(fread(buffer, 1, n, file) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_n_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__off_t bytes, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(bytes > 0) { + n = flac_min(sizeof(buffer), (size_t)bytes); + if(read_cb(buffer, 1, n, handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + bytes -= n; + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!feof(file)) { + n = fread(buffer, 1, sizeof(buffer), file); + if(n == 0 && !feof(file)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && local__fwrite(buffer, 1, n, tempfile) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +FLAC__bool copy_remaining_bytes_from_file_cb_(FLAC__IOHandle handle, FLAC__IOCallback_Read read_cb, FLAC__IOCallback_Eof eof_cb, FLAC__IOHandle temp_handle, FLAC__IOCallback_Write temp_write_cb, FLAC__Metadata_SimpleIteratorStatus *status) +{ + FLAC__byte buffer[8192]; + size_t n; + + while(!eof_cb(handle)) { + n = read_cb(buffer, 1, sizeof(buffer), handle); + if(n == 0 && !eof_cb(handle)) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR; + return false; + } + if(n > 0 && temp_write_cb(buffer, 1, n, temp_handle) != n) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR; + return false; + } + } + + return true; +} + +static int +local_snprintf(char *str, size_t size, const char *fmt, ...) +{ + va_list va; + int rc; + +#if defined _MSC_VER + if (size == 0) + return 1024; +#endif + + va_start (va, fmt); + +#if defined _MSC_VER + rc = vsnprintf_s (str, size, _TRUNCATE, fmt, va); + if (rc < 0) + rc = (int)(size - 1); +#elif defined __MINGW32__ + rc = __mingw_vsnprintf (str, size, fmt, va); +#else + rc = vsnprintf (str, size, fmt, va); +#endif + va_end (va); + + return rc; +} + +FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + static const char *tempfile_suffix = ".metadata_edit"; + if(0 == tempfile_path_prefix) { + size_t dest_len = strlen(filename) + strlen(tempfile_suffix) + 1; + if(0 == (*tempfilename = safe_malloc_(dest_len))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + local_snprintf(*tempfilename, dest_len, "%s%s", filename, tempfile_suffix); + } + else { + const char *p = strrchr(filename, '/'); + size_t dest_len; + if(0 == p) + p = filename; + else + p++; + + dest_len = strlen(tempfile_path_prefix) + strlen(p) + strlen(tempfile_suffix) + 2; + + if(0 == (*tempfilename = safe_malloc_(dest_len))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR; + return false; + } + local_snprintf(*tempfilename, dest_len, "%s/%s%s", tempfile_path_prefix, p, tempfile_suffix); + } + + if(0 == (*tempfile = flac_fopen(*tempfilename, "w+b"))) { + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE; + return false; + } + + return true; +} + +FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__Metadata_SimpleIteratorStatus *status) +{ + (void)fclose(*tempfile); + *tempfile = 0; + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ || defined __EMX__ + /* on some flavors of windows, flac_rename() will fail if the destination already exists */ + if(flac_unlink(filename) < 0) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR; + return false; + } +#endif + + /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just flac_rename(): */ + if(0 != flac_rename(*tempfilename, filename)) { + cleanup_tempfile_(tempfile, tempfilename); + *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR; + return false; + } + + cleanup_tempfile_(tempfile, tempfilename); + + return true; +} + +void cleanup_tempfile_(FILE **tempfile, char **tempfilename) +{ + if(0 != *tempfile) { + (void)fclose(*tempfile); + *tempfile = 0; + } + + if(0 != *tempfilename) { + (void)flac_unlink(*tempfilename); + free(*tempfilename); + *tempfilename = 0; + } +} + +FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats) +{ + return (0 == flac_stat(filename, stats)); +} + +void set_file_stats_(const char *filename, struct flac_stat_s *stats) +{ + struct utimbuf srctime; + + srctime.actime = stats->st_atime; + srctime.modtime = stats->st_mtime; + (void)flac_chmod(filename, stats->st_mode); + (void)flac_utime(filename, &srctime); +#if !defined _MSC_VER && !defined __BORLANDC__ && !defined __MINGW32__ + FLAC_CHECK_RETURN(chown(filename, stats->st_uid, -1)); + FLAC_CHECK_RETURN(chown(filename, -1, stats->st_gid)); +#endif +} + +int fseek_wrapper_(FLAC__IOHandle handle, FLAC__int64 offset, int whence) +{ + return fseeko((FILE*)handle, (FLAC__off_t)offset, whence); +} + +FLAC__int64 ftell_wrapper_(FLAC__IOHandle handle) +{ + return ftello((FILE*)handle); +} + +FLAC__Metadata_ChainStatus get_equivalent_status_(FLAC__Metadata_SimpleIteratorStatus status) +{ + switch(status) { + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK: + return FLAC__METADATA_CHAIN_STATUS_OK; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT: + return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE: + return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE: + return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE: + return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA: + return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR: + return FLAC__METADATA_CHAIN_STATUS_READ_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR: + return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR: + return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR: + return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR: + return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR; + case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR: + default: + return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR; + } +} diff --git a/src/libflac/metadata_object.c b/src/libflac/metadata_object.c @@ -0,0 +1,1588 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> +#include <string.h> + +#include "private/metadata.h" +#include "private/memory.h" + +#include "share/alloc.h" +#include "share/compat.h" + +// 8bb: hide POSIX warnings +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +/* Alias the first (in share/alloc.h) to the second (in src/libFLAC/memory.c). */ +#define safe_malloc_mul_2op_ safe_malloc_mul_2op_p + + +/**************************************************************************** + * + * Local routines + * + ***************************************************************************/ + +/* copy bytes: + * from = NULL && bytes = 0 + * to <- NULL + * from != NULL && bytes > 0 + * to <- copy of from + * else ASSERT + * malloc error leaves 'to' unchanged + */ +static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, uint32_t bytes) +{ + if (bytes > 0 && from != NULL) { + FLAC__byte *x; + if ((x = safe_malloc_(bytes)) == NULL) + return false; + memcpy(x, from, bytes); + *to = x; + } + else { + *to = 0; + } + return true; +} + +/* reallocate entry to 1 byte larger and add a terminating NUL */ +/* realloc() failure leaves entry unchanged */ +static FLAC__bool ensure_null_terminated_(FLAC__byte **entry, uint32_t length) +{ + FLAC__byte *x = safe_realloc_add_2op_(*entry, length, /*+*/1); + if (x != NULL) { + x[length] = '\0'; + *entry = x; + return true; + } + else + return false; +} + +/* copies the NUL-terminated C-string 'from' to '*to', leaving '*to' + * unchanged if malloc fails, free()ing the original '*to' if it + * succeeds and the original '*to' was not NULL + */ +static FLAC__bool copy_cstring_(char **to, const char *from) +{ + char *copy = strdup(from); + if (copy) { + free(*to); + *to = copy; + return true; + } + else + return false; +} + +static FLAC__bool copy_vcentry_(FLAC__StreamMetadata_VorbisComment_Entry *to, const FLAC__StreamMetadata_VorbisComment_Entry *from) +{ + to->length = from->length; + if (from->entry == 0) { + to->entry = 0; + } + else { + FLAC__byte *x; + if ((x = safe_malloc_add_2op_(from->length, /*+*/1)) == NULL) + return false; + memcpy(x, from->entry, from->length); + x[from->length] = '\0'; + to->entry = x; + } + return true; +} + +static FLAC__bool copy_track_(FLAC__StreamMetadata_CueSheet_Track *to, const FLAC__StreamMetadata_CueSheet_Track *from) +{ + memcpy(to, from, sizeof(FLAC__StreamMetadata_CueSheet_Track)); + if (from->indices == 0) { + } + else { + FLAC__StreamMetadata_CueSheet_Index *x; + if ((x = safe_malloc_mul_2op_p(from->num_indices, /*times*/sizeof(FLAC__StreamMetadata_CueSheet_Index))) == NULL) + return false; + memcpy(x, from->indices, from->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index)); + to->indices = x; + } + return true; +} + +static void seektable_calculate_length_(FLAC__StreamMetadata *object) +{ + object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; +} + +static FLAC__StreamMetadata_SeekPoint *seekpoint_array_new_(uint32_t num_points) +{ + FLAC__StreamMetadata_SeekPoint *object_array; + + object_array = safe_malloc_mul_2op_p(num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)); + + if (object_array != NULL) { + uint32_t i; + for (i = 0; i < num_points; i++) { + object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object_array[i].stream_offset = 0; + object_array[i].frame_samples = 0; + } + } + + return object_array; +} + +static void vorbiscomment_calculate_length_(FLAC__StreamMetadata *object) +{ + uint32_t i; + + object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8; + object->length += object->data.vorbis_comment.vendor_string.length; + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8; + for (i = 0; i < object->data.vorbis_comment.num_comments; i++) { + object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8); + object->length += object->data.vorbis_comment.comments[i].length; + } +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_new_(uint32_t num_comments) +{ + return safe_calloc_(num_comments, sizeof(FLAC__StreamMetadata_VorbisComment_Entry)); +} + +static void vorbiscomment_entry_array_delete_(FLAC__StreamMetadata_VorbisComment_Entry *object_array, uint32_t num_comments) +{ + uint32_t i; + + for (i = 0; i < num_comments; i++) + free(object_array[i].entry); + + free(object_array); +} + +static FLAC__StreamMetadata_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetadata_VorbisComment_Entry *object_array, uint32_t num_comments) +{ + FLAC__StreamMetadata_VorbisComment_Entry *return_array; + + return_array = vorbiscomment_entry_array_new_(num_comments); + + if (return_array != NULL) { + uint32_t i; + + for (i = 0; i < num_comments; i++) { + if (!copy_vcentry_(return_array+i, object_array+i)) { + vorbiscomment_entry_array_delete_(return_array, num_comments); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry *dest, const FLAC__StreamMetadata_VorbisComment_Entry *src, FLAC__bool copy) +{ + FLAC__byte *save; + + save = dest->entry; + + if (src->entry != NULL) { + if (copy) { + /* do the copy first so that if we fail we leave the dest object untouched */ + if (!copy_vcentry_(dest, src)) + return false; + } + else { + /* we have to make sure that the string we're taking over is null-terminated */ + + /* + * Stripping the const from src->entry is OK since we're taking + * ownership of the pointer. This is a hack around a deficiency + * in the API where the same function is used for 'copy' and + * 'own', but the source entry is a const pointer. If we were + * precise, the 'own' flavor would be a separate function with a + * non-const source pointer. But it's not, so we hack away. + */ + if (!ensure_null_terminated_((FLAC__byte**)(&src->entry), src->length)) + return false; + *dest = *src; + } + } + else { + /* the src is null */ + *dest = *src; + } + + free(save); + + vorbiscomment_calculate_length_(object); + return true; +} + +static int vorbiscomment_find_entry_from_(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name, uint32_t field_name_length) +{ + uint32_t i; + + for (i = offset; i < object->data.vorbis_comment.num_comments; i++) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) + return (int)i; + } + + return -1; +} + +static void cuesheet_calculate_length_(FLAC__StreamMetadata *object) +{ + uint32_t i; + + object->length = ( + FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN + + FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN + + FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN + ) / 8; + + object->length += object->data.cue_sheet.num_tracks * ( + FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN + + FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN + ) / 8; + + for (i = 0; i < object->data.cue_sheet.num_tracks; i++) { + object->length += object->data.cue_sheet.tracks[i].num_indices * ( + FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN + + FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN + ) / 8; + } +} + +static FLAC__StreamMetadata_CueSheet_Index *cuesheet_track_index_array_new_(uint32_t num_indices) +{ + return safe_calloc_(num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_new_(uint32_t num_tracks) +{ + return safe_calloc_(num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +static void cuesheet_track_array_delete_(FLAC__StreamMetadata_CueSheet_Track *object_array, uint32_t num_tracks) +{ + uint32_t i; + + for (i = 0; i < num_tracks; i++) { + if (object_array[i].indices != 0) { + free(object_array[i].indices); + } + } + + free(object_array); +} + +static FLAC__StreamMetadata_CueSheet_Track *cuesheet_track_array_copy_(const FLAC__StreamMetadata_CueSheet_Track *object_array, uint32_t num_tracks) +{ + FLAC__StreamMetadata_CueSheet_Track *return_array; + + return_array = cuesheet_track_array_new_(num_tracks); + + if (return_array != NULL) { + uint32_t i; + + for (i = 0; i < num_tracks; i++) { + if (!copy_track_(return_array+i, object_array+i)) { + cuesheet_track_array_delete_(return_array, num_tracks); + return 0; + } + } + } + + return return_array; +} + +static FLAC__bool cuesheet_set_track_(FLAC__StreamMetadata *object, FLAC__StreamMetadata_CueSheet_Track *dest, const FLAC__StreamMetadata_CueSheet_Track *src, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet_Index *save; + + save = dest->indices; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_track_(dest, src)) + return false; + } + else { + *dest = *src; + } + + free(save); + + cuesheet_calculate_length_(object); + return true; +} + + +/**************************************************************************** + * + * Metadata object routines + * + ***************************************************************************/ + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_new(FLAC__MetadataType type) +{ + FLAC__StreamMetadata *object; + + if (type > FLAC__MAX_METADATA_TYPE) + return 0; + + object = calloc(1, sizeof(FLAC__StreamMetadata)); + if (object != NULL) { + object->is_last = false; + object->type = type; + switch(type) { + case FLAC__METADATA_TYPE_STREAMINFO: + object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH; + break; + case FLAC__METADATA_TYPE_PADDING: + /* calloc() took care of this for us: + object->length = 0; + */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8; + /* calloc() took care of this for us: + object->data.application.data = 0; + */ + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + /* calloc() took care of this for us: + object->length = 0; + object->data.seek_table.num_points = 0; + object->data.seek_table.points = 0; + */ + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + object->data.vorbis_comment.vendor_string.length = (uint32_t)strlen(FLAC__VENDOR_STRING); + if (!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, (const FLAC__byte*)FLAC__VENDOR_STRING, object->data.vorbis_comment.vendor_string.length+1)) { + free(object); + return 0; + } + vorbiscomment_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_CUESHEET: + cuesheet_calculate_length_(object); + break; + case FLAC__METADATA_TYPE_PICTURE: + object->length = ( + FLAC__STREAM_METADATA_PICTURE_TYPE_LEN + + FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN + /* empty mime_type string */ + FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN + /* empty description string */ + FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN + + FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN + + FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN + + FLAC__STREAM_METADATA_PICTURE_COLORS_LEN + + FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN + + 0 /* no data */ + ) / 8; + object->data.picture.type = FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER; + object->data.picture.mime_type = 0; + object->data.picture.description = 0; + /* calloc() took care of this for us: + object->data.picture.width = 0; + object->data.picture.height = 0; + object->data.picture.depth = 0; + object->data.picture.colors = 0; + object->data.picture.data_length = 0; + object->data.picture.data = 0; + */ + /* now initialize mime_type and description with empty strings to make things easier on the client */ + if (!copy_cstring_(&object->data.picture.mime_type, "")) { + free(object); + return 0; + } + if (!copy_cstring_((char**)(&object->data.picture.description), "")) { + free(object->data.picture.mime_type); + free(object); + return 0; + } + break; + default: + /* calloc() took care of this for us: + object->length = 0; + object->data.unknown.data = 0; + */ + break; + } + } + + return object; +} + +FLAC_API FLAC__StreamMetadata *FLAC__metadata_object_clone(const FLAC__StreamMetadata *object) +{ + FLAC__StreamMetadata *to; + + if ((to = FLAC__metadata_object_new(object->type)) != NULL) { + to->is_last = object->is_last; + to->type = object->type; + to->length = object->length; + switch(to->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetadata_StreamInfo)); + break; + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if (to->length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8) { /* underflow check */ + FLAC__metadata_object_delete(to); + return 0; + } + memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8); + if (!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + to->data.seek_table.num_points = object->data.seek_table.num_points; + if (to->data.seek_table.num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) { /* overflow check */ + FLAC__metadata_object_delete(to); + return 0; + } + if (!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint))) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if (to->data.vorbis_comment.vendor_string.entry != NULL) { + free(to->data.vorbis_comment.vendor_string.entry); + to->data.vorbis_comment.vendor_string.entry = 0; + } + if (!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) { + FLAC__metadata_object_delete(to); + return 0; + } + if (object->data.vorbis_comment.num_comments == 0) { + to->data.vorbis_comment.comments = 0; + } + else { + to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + if (to->data.vorbis_comment.comments == NULL) { + to->data.vorbis_comment.num_comments = 0; + FLAC__metadata_object_delete(to); + return 0; + } + } + to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments; + break; + case FLAC__METADATA_TYPE_CUESHEET: + memcpy(&to->data.cue_sheet, &object->data.cue_sheet, sizeof(FLAC__StreamMetadata_CueSheet)); + if (object->data.cue_sheet.num_tracks == 0) { + } + else { + to->data.cue_sheet.tracks = cuesheet_track_array_copy_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + if (to->data.cue_sheet.tracks == NULL) { + FLAC__metadata_object_delete(to); + return 0; + } + } + break; + case FLAC__METADATA_TYPE_PICTURE: + to->data.picture.type = object->data.picture.type; + if (!copy_cstring_(&to->data.picture.mime_type, object->data.picture.mime_type)) { + FLAC__metadata_object_delete(to); + return 0; + } + if (!copy_cstring_((char**)(&to->data.picture.description), (const char*)object->data.picture.description)) { + FLAC__metadata_object_delete(to); + return 0; + } + to->data.picture.width = object->data.picture.width; + to->data.picture.height = object->data.picture.height; + to->data.picture.depth = object->data.picture.depth; + to->data.picture.colors = object->data.picture.colors; + to->data.picture.data_length = object->data.picture.data_length; + if (!copy_bytes_((&to->data.picture.data), object->data.picture.data, object->data.picture.data_length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + default: + if (!copy_bytes_(&to->data.unknown.data, object->data.unknown.data, object->length)) { + FLAC__metadata_object_delete(to); + return 0; + } + break; + } + } + + return to; +} + +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object) +{ + switch(object->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if (object->data.application.data != NULL) { + free(object->data.application.data); + object->data.application.data = NULL; + } + break; + case FLAC__METADATA_TYPE_SEEKTABLE: + if (object->data.seek_table.points != NULL) { + free(object->data.seek_table.points); + object->data.seek_table.points = NULL; + } + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if (object->data.vorbis_comment.vendor_string.entry != NULL) { + free(object->data.vorbis_comment.vendor_string.entry); + object->data.vorbis_comment.vendor_string.entry = 0; + } + if (object->data.vorbis_comment.comments != NULL) { + vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments); + object->data.vorbis_comment.comments = NULL; + object->data.vorbis_comment.num_comments = 0; + } + break; + case FLAC__METADATA_TYPE_CUESHEET: + if (object->data.cue_sheet.tracks != NULL) { + cuesheet_track_array_delete_(object->data.cue_sheet.tracks, object->data.cue_sheet.num_tracks); + object->data.cue_sheet.tracks = NULL; + object->data.cue_sheet.num_tracks = 0; + } + break; + case FLAC__METADATA_TYPE_PICTURE: + if (object->data.picture.mime_type != NULL) { + free(object->data.picture.mime_type); + object->data.picture.mime_type = NULL; + } + if (object->data.picture.description != NULL) { + free(object->data.picture.description); + object->data.picture.description = NULL; + } + if (object->data.picture.data != NULL) { + free(object->data.picture.data); + object->data.picture.data = NULL; + } + break; + default: + if (object->data.unknown.data != NULL) { + free(object->data.unknown.data); + object->data.unknown.data = NULL; + } + break; + } +} + +FLAC_API void FLAC__metadata_object_delete(FLAC__StreamMetadata *object) +{ + FLAC__metadata_object_delete_data(object); + free(object); +} + +static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetadata_StreamInfo *block1, const FLAC__StreamMetadata_StreamInfo *block2) +{ + if (block1->min_blocksize != block2->min_blocksize) + return false; + if (block1->max_blocksize != block2->max_blocksize) + return false; + if (block1->min_framesize != block2->min_framesize) + return false; + if (block1->max_framesize != block2->max_framesize) + return false; + if (block1->sample_rate != block2->sample_rate) + return false; + if (block1->channels != block2->channels) + return false; + if (block1->bits_per_sample != block2->bits_per_sample) + return false; + if (block1->total_samples != block2->total_samples) + return false; + if (memcmp(block1->md5sum, block2->md5sum, 16) != 0) + return false; + return true; +} + +static FLAC__bool compare_block_data_application_(const FLAC__StreamMetadata_Application *block1, const FLAC__StreamMetadata_Application *block2, uint32_t block_length) +{ + if (memcmp(block1->id, block2->id, sizeof(block1->id)) != 0) + return false; + if (block1->data != NULL && block2->data != NULL) + return memcmp(block1->data, block2->data, block_length - sizeof(block1->id)) == 0; + else + return block1->data == block2->data; +} + +static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetadata_SeekTable *block1, const FLAC__StreamMetadata_SeekTable *block2) +{ + uint32_t i; + + if (block1->num_points != block2->num_points) + return false; + + if (block1->points != NULL && block2->points != NULL) { + for (i = 0; i < block1->num_points; i++) { + if (block1->points[i].sample_number != block2->points[i].sample_number) + return false; + if (block1->points[i].stream_offset != block2->points[i].stream_offset) + return false; + if (block1->points[i].frame_samples != block2->points[i].frame_samples) + return false; + } + return true; + } + else + return block1->points == block2->points; +} + +static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetadata_VorbisComment *block1, const FLAC__StreamMetadata_VorbisComment *block2) +{ + uint32_t i; + + if (block1->vendor_string.length != block2->vendor_string.length) + return false; + + if (block1->vendor_string.entry != NULL && block2->vendor_string.entry != NULL) { + if (memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length) != 0) + return false; + } + else if (block1->vendor_string.entry != block2->vendor_string.entry) + return false; + + if (block1->num_comments != block2->num_comments) + return false; + + for (i = 0; i < block1->num_comments; i++) { + if (block1->comments[i].entry != NULL && block2->comments[i].entry != NULL) { + if (memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length) != 0) + return false; + } + else if (block1->comments[i].entry != block2->comments[i].entry) + return false; + } + return true; +} + +static FLAC__bool compare_block_data_cuesheet_(const FLAC__StreamMetadata_CueSheet *block1, const FLAC__StreamMetadata_CueSheet *block2) +{ + uint32_t i, j; + + if (strcmp(block1->media_catalog_number, block2->media_catalog_number) != 0) + return false; + + if (block1->lead_in != block2->lead_in) + return false; + + if (block1->is_cd != block2->is_cd) + return false; + + if (block1->num_tracks != block2->num_tracks) + return false; + + if (block1->tracks != NULL && block2->tracks != NULL) { + for (i = 0; i < block1->num_tracks; i++) { + if (block1->tracks[i].offset != block2->tracks[i].offset) + return false; + if (block1->tracks[i].number != block2->tracks[i].number) + return false; + if (memcmp(block1->tracks[i].isrc, block2->tracks[i].isrc, sizeof(block1->tracks[i].isrc)) != 0) + return false; + if (block1->tracks[i].type != block2->tracks[i].type) + return false; + if (block1->tracks[i].pre_emphasis != block2->tracks[i].pre_emphasis) + return false; + if (block1->tracks[i].num_indices != block2->tracks[i].num_indices) + return false; + if (block1->tracks[i].indices != NULL && block2->tracks[i].indices != NULL) { + for (j = 0; j < block1->tracks[i].num_indices; j++) { + if (block1->tracks[i].indices[j].offset != block2->tracks[i].indices[j].offset) + return false; + if (block1->tracks[i].indices[j].number != block2->tracks[i].indices[j].number) + return false; + } + } + else if (block1->tracks[i].indices != block2->tracks[i].indices) + return false; + } + } + else if (block1->tracks != block2->tracks) + return false; + return true; +} + +static FLAC__bool compare_block_data_picture_(const FLAC__StreamMetadata_Picture *block1, const FLAC__StreamMetadata_Picture *block2) +{ + if (block1->type != block2->type) + return false; + if (block1->mime_type != block2->mime_type && (block1->mime_type == 0 || block2->mime_type == 0 || strcmp(block1->mime_type, block2->mime_type))) + return false; + if (block1->description != block2->description && (block1->description == 0 || block2->description == 0 || strcmp((const char *)block1->description, (const char *)block2->description))) + return false; + if (block1->width != block2->width) + return false; + if (block1->height != block2->height) + return false; + if (block1->depth != block2->depth) + return false; + if (block1->colors != block2->colors) + return false; + if (block1->data_length != block2->data_length) + return false; + if (block1->data != block2->data && (block1->data == NULL || block2->data == NULL || memcmp(block1->data, block2->data, block1->data_length))) + return false; + return true; +} + +static FLAC__bool compare_block_data_unknown_(const FLAC__StreamMetadata_Unknown *block1, const FLAC__StreamMetadata_Unknown *block2, uint32_t block_length) +{ + if (block1->data != NULL && block2->data != NULL) + return memcmp(block1->data, block2->data, block_length) == 0; + else + return block1->data == block2->data; +} + +FLAC_API FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetadata *block1, const FLAC__StreamMetadata *block2) +{ + if (block1->type != block2->type) { + return false; + } + if (block1->is_last != block2->is_last) { + return false; + } + if (block1->length != block2->length) { + return false; + } + switch(block1->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info); + case FLAC__METADATA_TYPE_PADDING: + return true; /* we don't compare the padding guts */ + case FLAC__METADATA_TYPE_APPLICATION: + return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length); + case FLAC__METADATA_TYPE_SEEKTABLE: + return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table); + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment); + case FLAC__METADATA_TYPE_CUESHEET: + return compare_block_data_cuesheet_(&block1->data.cue_sheet, &block2->data.cue_sheet); + case FLAC__METADATA_TYPE_PICTURE: + return compare_block_data_picture_(&block1->data.picture, &block2->data.picture); + default: + return compare_block_data_unknown_(&block1->data.unknown, &block2->data.unknown, block1->length); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, uint32_t length, FLAC__bool copy) +{ + FLAC__byte *save; + + save = object->data.application.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_bytes_(&object->data.application.data, data, length)) + return false; + } + else { + object->data.application.data = data; + } + + free(save); + + object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetadata *object, uint32_t new_num_points) +{ + if (object->data.seek_table.points == 0) { + if (new_num_points == 0) + return true; + else if ((object->data.seek_table.points = seekpoint_array_new_(new_num_points)) == 0) + return false; + } + else { + const size_t old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + const size_t new_size = new_num_points * sizeof(FLAC__StreamMetadata_SeekPoint); + + /* overflow check */ + if (new_num_points > UINT32_MAX / sizeof(FLAC__StreamMetadata_SeekPoint)) + return false; + + if (new_size == 0) { + free(object->data.seek_table.points); + object->data.seek_table.points = 0; + } + else if ((object->data.seek_table.points = safe_realloc_(object->data.seek_table.points, new_size)) == NULL) + return false; + + /* if growing, set new elements to placeholders */ + if (new_size > old_size) { + uint32_t i; + for (i = object->data.seek_table.num_points; i < new_num_points; i++) { + object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; + object->data.seek_table.points[i].stream_offset = 0; + object->data.seek_table.points[i].frame_samples = 0; + } + } + } + + object->data.seek_table.num_points = new_num_points; + + seektable_calculate_length_(object); + return true; +} + +FLAC_API void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point) +{ + object->data.seek_table.points[point_num] = point; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetadata *object, uint32_t point_num, FLAC__StreamMetadata_SeekPoint point) +{ + int i; + + if (!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1)) + return false; + + /* move all points >= point_num forward one space */ + for (i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--) + object->data.seek_table.points[i] = object->data.seek_table.points[i-1]; + + FLAC__metadata_object_seektable_set_point(object, point_num, point); + seektable_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetadata *object, uint32_t point_num) +{ + uint32_t i; + + /* move all points > point_num backward one space */ + for (i = point_num; i < object->data.seek_table.num_points-1; i++) + object->data.seek_table.points[i] = object->data.seek_table.points[i+1]; + + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_is_legal(const FLAC__StreamMetadata *object) +{ + return FLAC__format_seektable_is_legal(&object->data.seek_table); +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_placeholders(FLAC__StreamMetadata *object, uint32_t num) +{ + if (num > 0) + /* WATCHOUT: we rely on the fact that growing the array adds PLACEHOLDERS at the end */ + return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points + num); + else + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_point(FLAC__StreamMetadata *object, FLAC__uint64 sample_number) +{ + FLAC__StreamMetadata_SeekTable *seek_table; + + seek_table = &object->data.seek_table; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + 1)) + return false; + + seek_table->points[seek_table->num_points - 1].sample_number = sample_number; + seek_table->points[seek_table->num_points - 1].stream_offset = 0; + seek_table->points[seek_table->num_points - 1].frame_samples = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_points(FLAC__StreamMetadata *object, FLAC__uint64 sample_numbers[], uint32_t num) +{ + if (num > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + uint32_t i, j; + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for (j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = sample_numbers[j]; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points(FLAC__StreamMetadata *object, uint32_t num, FLAC__uint64 total_samples) +{ + if (num > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + uint32_t i, j; + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + num)) + return false; + + for (j = 0; j < num; i++, j++) { + seek_table->points[i].sample_number = total_samples * (FLAC__uint64)j / (FLAC__uint64)num; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(FLAC__StreamMetadata *object, uint32_t samples, FLAC__uint64 total_samples) +{ + if (samples > 0 && total_samples > 0) { + FLAC__StreamMetadata_SeekTable *seek_table = &object->data.seek_table; + uint32_t i, j; + FLAC__uint64 num, sample; + + num = 1 + total_samples / samples; /* 1+ for the first sample at 0 */ + /* now account for the fact that we don't place a seekpoint at "total_samples" since samples are number from 0: */ + if (total_samples % samples == 0) + num--; + + /* Put a strict upper bound on the number of allowed seek points. */ + if (num > 32768) { + /* Set the bound and recalculate samples accordingly. */ + num = 32768; + samples = (uint32_t)(total_samples / num); + } + + i = seek_table->num_points; + + if (!FLAC__metadata_object_seektable_resize_points(object, seek_table->num_points + (uint32_t)num)) + return false; + + sample = 0; + for (j = 0; j < num; i++, j++, sample += samples) { + seek_table->points[i].sample_number = sample; + seek_table->points[i].stream_offset = 0; + seek_table->points[i].frame_samples = 0; + } + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_seektable_template_sort(FLAC__StreamMetadata *object, FLAC__bool compact) +{ + uint32_t unique; + + unique = FLAC__format_seektable_sort(&object->data.seek_table); + + return !compact || FLAC__metadata_object_seektable_resize_points(object, unique); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + if (!FLAC__format_vorbiscomment_entry_value_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetadata *object, uint32_t new_num_comments) +{ + if (object->data.vorbis_comment.comments == NULL) { + if (new_num_comments == 0) + return true; + else if ((object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)) == NULL) + return false; + } + else { + const size_t old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + const size_t new_size = new_num_comments * sizeof(FLAC__StreamMetadata_VorbisComment_Entry); + + /* overflow check */ + if (new_num_comments > UINT32_MAX / sizeof(FLAC__StreamMetadata_VorbisComment_Entry)) + return false; + + /* if shrinking, free the truncated entries */ + if (new_num_comments < object->data.vorbis_comment.num_comments) { + uint32_t i; + for (i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++) + if (object->data.vorbis_comment.comments[i].entry != NULL) + free(object->data.vorbis_comment.comments[i].entry); + } + + if (new_size == 0) { + free(object->data.vorbis_comment.comments); + object->data.vorbis_comment.comments = 0; + } + else { + FLAC__StreamMetadata_VorbisComment_Entry *oldptr = object->data.vorbis_comment.comments; + if ((object->data.vorbis_comment.comments = realloc(object->data.vorbis_comment.comments, new_size)) == NULL) { + vorbiscomment_entry_array_delete_(oldptr, object->data.vorbis_comment.num_comments); + object->data.vorbis_comment.num_comments = 0; + return false; + } + } + + /* if growing, zero all the length/pointers of new elements */ + if (new_size > old_size) + memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size); + } + + object->data.vorbis_comment.num_comments = new_num_comments; + + vorbiscomment_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], &entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetadata *object, uint32_t comment_num, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + vc = &object->data.vorbis_comment; + + if (!FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments+1)) + return false; + + /* move all comments >= comment_num forward one space */ + memmove(&vc->comments[comment_num+1], &vc->comments[comment_num], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-1-comment_num)); + vc->comments[comment_num].length = 0; + vc->comments[comment_num].entry = 0; + + return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_append_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool copy) +{ + return FLAC__metadata_object_vorbiscomment_insert_comment(object, object->data.vorbis_comment.num_comments, entry, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_replace_comment(FLAC__StreamMetadata *object, FLAC__StreamMetadata_VorbisComment_Entry entry, FLAC__bool all, FLAC__bool copy) +{ + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + int i; + size_t field_name_length; + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + + if (eq == NULL) + return false; /* double protection */ + + field_name_length = eq-entry.entry; + + i = vorbiscomment_find_entry_from_(object, 0, (const char *)entry.entry, (uint32_t)field_name_length); + if (i >= 0) { + uint32_t indx = (uint32_t)i; + if (!FLAC__metadata_object_vorbiscomment_set_comment(object, indx, entry, copy)) + return false; + entry = object->data.vorbis_comment.comments[indx]; + indx++; /* skip over replaced comment */ + if (all && indx < object->data.vorbis_comment.num_comments) { + i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, (uint32_t)field_name_length); + while (i >= 0) { + indx = (uint32_t)i; + if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, indx)) + return false; + if (indx < object->data.vorbis_comment.num_comments) + i = vorbiscomment_find_entry_from_(object, indx, (const char *)entry.entry, (uint32_t)field_name_length); + else + i = -1; + } + } + return true; + } + else + return FLAC__metadata_object_vorbiscomment_append_comment(object, entry, copy); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetadata *object, uint32_t comment_num) +{ + FLAC__StreamMetadata_VorbisComment *vc; + + vc = &object->data.vorbis_comment; + + /* free the comment at comment_num */ + free(vc->comments[comment_num].entry); + + /* move all comments > comment_num backward one space */ + memmove(&vc->comments[comment_num], &vc->comments[comment_num+1], sizeof(FLAC__StreamMetadata_VorbisComment_Entry)*(vc->num_comments-comment_num-1)); + vc->comments[vc->num_comments-1].length = 0; + vc->comments[vc->num_comments-1].entry = 0; + + return FLAC__metadata_object_vorbiscomment_resize_comments(object, vc->num_comments-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(FLAC__StreamMetadata_VorbisComment_Entry *entry, const char *field_name, const char *field_value) +{ + if (!FLAC__format_vorbiscomment_entry_name_is_legal(field_name)) + return false; + if (!FLAC__format_vorbiscomment_entry_value_is_legal((const FLAC__byte *)field_value, (uint32_t)(-1))) + return false; + + { + const size_t nn = strlen(field_name); + const size_t nv = strlen(field_value); + entry->length = (FLAC__uint32)(nn + 1 /*=*/ + nv); + if ((entry->entry = safe_malloc_add_4op_(nn, /*+*/1, /*+*/nv, /*+*/1)) == NULL) + return false; + memcpy(entry->entry, field_name, nn); + entry->entry[nn] = '='; + memcpy(entry->entry+nn+1, field_value, nv); + entry->entry[entry->length] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(const FLAC__StreamMetadata_VorbisComment_Entry entry, char **field_name, char **field_value) +{ + if (!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) + return false; + + { + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + const size_t nn = eq-entry.entry; + const size_t nv = entry.length-nn-1; /* -1 for the '=' */ + + if (eq == NULL) + return false; /* double protection */ + if ((*field_name = safe_malloc_add_2op_(nn, /*+*/1)) == NULL) + return false; + if ((*field_value = safe_malloc_add_2op_(nv, /*+*/1)) == NULL) { + free(*field_name); + return false; + } + memcpy(*field_name, entry.entry, nn); + memcpy(*field_value, entry.entry+nn+1, nv); + (*field_name)[nn] = '\0'; + (*field_value)[nv] = '\0'; + } + + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_vorbiscomment_entry_matches(const FLAC__StreamMetadata_VorbisComment_Entry entry, const char *field_name, uint32_t field_name_length) +{ + const FLAC__byte *eq = (FLAC__byte*)memchr(entry.entry, '=', entry.length); + return (eq != NULL && (uint32_t)(eq-entry.entry) == field_name_length && FLAC__STRNCASECMP(field_name, (const char *)entry.entry, field_name_length) == 0); +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_find_entry_from(const FLAC__StreamMetadata *object, uint32_t offset, const char *field_name) +{ + return vorbiscomment_find_entry_from_(object, offset, field_name, (uint32_t)strlen(field_name)); +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entry_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + const uint32_t field_name_length = (const uint32_t)strlen(field_name); + uint32_t i; + + for (i = 0; i < object->data.vorbis_comment.num_comments; i++) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + if (!FLAC__metadata_object_vorbiscomment_delete_comment(object, i)) + return -1; + else + return 1; + } + } + + return 0; +} + +FLAC_API int FLAC__metadata_object_vorbiscomment_remove_entries_matching(FLAC__StreamMetadata *object, const char *field_name) +{ + FLAC__bool ok = true; + uint32_t matching = 0; + const uint32_t field_name_length = (const uint32_t)strlen(field_name); + int i; + + /* must delete from end to start otherwise it will interfere with our iteration */ + for (i = (int)object->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) { + if (FLAC__metadata_object_vorbiscomment_entry_matches(object->data.vorbis_comment.comments[i], field_name, field_name_length)) { + matching++; + ok &= FLAC__metadata_object_vorbiscomment_delete_comment(object, (uint32_t)i); + } + } + + return ok? (int)matching : -1; +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_new(void) +{ + return calloc(1, sizeof(FLAC__StreamMetadata_CueSheet_Track)); +} + +FLAC_API FLAC__StreamMetadata_CueSheet_Track *FLAC__metadata_object_cuesheet_track_clone(const FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__StreamMetadata_CueSheet_Track *to; + + if ((to = FLAC__metadata_object_cuesheet_track_new()) != NULL) { + if (!copy_track_(to, object)) { + FLAC__metadata_object_cuesheet_track_delete(to); + return 0; + } + } + + return to; +} + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object) +{ + if (object->indices != NULL) { + free(object->indices); + } +} + +FLAC_API void FLAC__metadata_object_cuesheet_track_delete(FLAC__StreamMetadata_CueSheet_Track *object) +{ + FLAC__metadata_object_cuesheet_track_delete_data(object); + free(object); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_resize_indices(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t new_num_indices) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + track = &object->data.cue_sheet.tracks[track_num]; + + if (track->indices == NULL) { + if (new_num_indices == 0) + return true; + else if ((track->indices = cuesheet_track_index_array_new_(new_num_indices)) == NULL) + return false; + } + else { + const size_t old_size = track->num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + const size_t new_size = new_num_indices * sizeof(FLAC__StreamMetadata_CueSheet_Index); + + /* overflow check */ + if (new_num_indices > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Index)) + return false; + + if (new_size == 0) { + free(track->indices); + track->indices = 0; + } + else if ((track->indices = safe_realloc_(track->indices, new_size)) == NULL) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if (new_size > old_size) + memset(track->indices + track->num_indices, 0, new_size - old_size); + } + + track->num_indices = (FLAC__byte)new_num_indices; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num, FLAC__StreamMetadata_CueSheet_Index indx) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + track = &object->data.cue_sheet.tracks[track_num]; + + if (!FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices+1)) + return false; + + /* move all indices >= index_num forward one space */ + memmove(&track->indices[index_num+1], &track->indices[index_num], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-1-index_num)); + + track->indices[index_num] = indx; + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_insert_blank_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num) +{ + FLAC__StreamMetadata_CueSheet_Index indx; + memset(&indx, 0, sizeof(indx)); + return FLAC__metadata_object_cuesheet_track_insert_index(object, track_num, index_num, indx); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_track_delete_index(FLAC__StreamMetadata *object, uint32_t track_num, uint32_t index_num) +{ + FLAC__StreamMetadata_CueSheet_Track *track; + + track = &object->data.cue_sheet.tracks[track_num]; + + /* move all indices > index_num backward one space */ + memmove(&track->indices[index_num], &track->indices[index_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Index)*(track->num_indices-index_num-1)); + + FLAC__metadata_object_cuesheet_track_resize_indices(object, track_num, track->num_indices-1); + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMetadata *object, uint32_t new_num_tracks) +{ + if (object->data.cue_sheet.tracks == NULL) { + if (new_num_tracks == 0) + return true; + else if ((object->data.cue_sheet.tracks = cuesheet_track_array_new_(new_num_tracks)) == NULL) + return false; + } + else { + const size_t old_size = object->data.cue_sheet.num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + const size_t new_size = new_num_tracks * sizeof(FLAC__StreamMetadata_CueSheet_Track); + + /* overflow check */ + if (new_num_tracks > UINT32_MAX / sizeof(FLAC__StreamMetadata_CueSheet_Track)) + return false; + + /* if shrinking, free the truncated entries */ + if (new_num_tracks < object->data.cue_sheet.num_tracks) { + uint32_t i; + for (i = new_num_tracks; i < object->data.cue_sheet.num_tracks; i++) + free(object->data.cue_sheet.tracks[i].indices); + } + + if (new_size == 0) { + free(object->data.cue_sheet.tracks); + object->data.cue_sheet.tracks = 0; + } + else if ((object->data.cue_sheet.tracks = safe_realloc_(object->data.cue_sheet.tracks, new_size)) == NULL) + return false; + + /* if growing, zero all the lengths/pointers of new elements */ + if (new_size > old_size) + memset(object->data.cue_sheet.tracks + object->data.cue_sheet.num_tracks, 0, new_size - old_size); + } + + object->data.cue_sheet.num_tracks = new_num_tracks; + + cuesheet_calculate_length_(object); + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_set_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + return cuesheet_set_track_(object, object->data.cue_sheet.tracks + track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_track(FLAC__StreamMetadata *object, uint32_t track_num, FLAC__StreamMetadata_CueSheet_Track *track, FLAC__bool copy) +{ + FLAC__StreamMetadata_CueSheet *cs; + + cs = &object->data.cue_sheet; + + if (!FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks+1)) + return false; + + /* move all tracks >= track_num forward one space */ + memmove(&cs->tracks[track_num+1], &cs->tracks[track_num], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-1-track_num)); + cs->tracks[track_num].num_indices = 0; + cs->tracks[track_num].indices = 0; + + return FLAC__metadata_object_cuesheet_set_track(object, track_num, track, copy); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_insert_blank_track(FLAC__StreamMetadata *object, uint32_t track_num) +{ + FLAC__StreamMetadata_CueSheet_Track track; + memset(&track, 0, sizeof(track)); + return FLAC__metadata_object_cuesheet_insert_track(object, track_num, &track, /*copy=*/false); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_delete_track(FLAC__StreamMetadata *object, uint32_t track_num) +{ + FLAC__StreamMetadata_CueSheet *cs; + + cs = &object->data.cue_sheet; + + /* free the track at track_num */ + free(cs->tracks[track_num].indices); + + /* move all tracks > track_num backward one space */ + memmove(&cs->tracks[track_num], &cs->tracks[track_num+1], sizeof(FLAC__StreamMetadata_CueSheet_Track)*(cs->num_tracks-track_num-1)); + cs->tracks[cs->num_tracks-1].num_indices = 0; + cs->tracks[cs->num_tracks-1].indices = 0; + + return FLAC__metadata_object_cuesheet_resize_tracks(object, cs->num_tracks-1); +} + +FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_is_legal(const FLAC__StreamMetadata *object, FLAC__bool check_cd_da_subset, const char **violation) +{ + return FLAC__format_cuesheet_is_legal(&object->data.cue_sheet, check_cd_da_subset, violation); +} + +static FLAC__uint64 get_index_01_offset_(const FLAC__StreamMetadata_CueSheet *cs, uint32_t track) +{ + if (track >= (cs->num_tracks-1) || cs->tracks[track].num_indices < 1) + return 0; + else if (cs->tracks[track].indices[0].number == 1) + return cs->tracks[track].indices[0].offset + cs->tracks[track].offset + cs->lead_in; + else if (cs->tracks[track].num_indices < 2) + return 0; + else if (cs->tracks[track].indices[1].number == 1) + return cs->tracks[track].indices[1].offset + cs->tracks[track].offset + cs->lead_in; + else + return 0; +} + +static FLAC__uint32 cddb_add_digits_(FLAC__uint32 x) +{ + FLAC__uint32 n = 0; + while (x) { + n += (x%10); + x /= 10; + } + return n; +} + +/*@@@@add to tests*/ +FLAC_API FLAC__uint32 FLAC__metadata_object_cuesheet_calculate_cddb_id(const FLAC__StreamMetadata *object) +{ + const FLAC__StreamMetadata_CueSheet *cs; + + cs = &object->data.cue_sheet; + + if (cs->num_tracks < 2) /* need at least one real track and the lead-out track */ + return 0; + + { + FLAC__uint32 i, length, sum = 0; + for (i = 0; i < (cs->num_tracks-1); i++) /* -1 to avoid counting the lead-out */ + sum += cddb_add_digits_((FLAC__uint32)(get_index_01_offset_(cs, i) / 44100)); + length = (FLAC__uint32)((cs->tracks[cs->num_tracks-1].offset+cs->lead_in) / 44100) - (FLAC__uint32)(get_index_01_offset_(cs, 0) / 44100); + + return (sum % 0xFF) << 24 | length << 8 | (FLAC__uint32)(cs->num_tracks-1); + } +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_mime_type(FLAC__StreamMetadata *object, char *mime_type, FLAC__bool copy) +{ + char *old; + size_t old_length, new_length; + + old = object->data.picture.mime_type; + old_length = old? strlen(old) : 0; + new_length = strlen(mime_type); + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (new_length >= SIZE_MAX) /* overflow check */ + return false; + if (!copy_bytes_((FLAC__byte**)(&object->data.picture.mime_type), (FLAC__byte*)mime_type, (uint32_t)(new_length+1))) + return false; + } + else { + object->data.picture.mime_type = mime_type; + } + + free(old); + + object->length -= (uint32_t)old_length; + object->length += (uint32_t)new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_description(FLAC__StreamMetadata *object, FLAC__byte *description, FLAC__bool copy) +{ + FLAC__byte *old; + size_t old_length, new_length; + + old = object->data.picture.description; + old_length = old? strlen((const char *)old) : 0; + new_length = strlen((const char *)description); + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (new_length >= SIZE_MAX) /* overflow check */ + return false; + if (!copy_bytes_(&object->data.picture.description, description, (uint32_t)(new_length+1))) + return false; + } + else { + object->data.picture.description = description; + } + + free(old); + + object->length -= (uint32_t)old_length; + object->length += (uint32_t)new_length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_set_data(FLAC__StreamMetadata *object, FLAC__byte *data, FLAC__uint32 length, FLAC__bool copy) +{ + FLAC__byte *old; + + old = object->data.picture.data; + + /* do the copy first so that if we fail we leave the object untouched */ + if (copy) { + if (!copy_bytes_(&object->data.picture.data, data, length)) + return false; + } + else { + object->data.picture.data = data; + } + + free(old); + + object->length -= object->data.picture.data_length; + object->data.picture.data_length = length; + object->length += length; + return true; +} + +FLAC_API FLAC__bool FLAC__metadata_object_picture_is_legal(const FLAC__StreamMetadata *object, const char **violation) +{ + return FLAC__format_picture_is_legal(&object->data.picture, violation); +} diff --git a/src/libflac/private/bitmath.h b/src/libflac/private/bitmath.h @@ -0,0 +1,205 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITMATH_H +#define FLAC__PRIVATE__BITMATH_H + +#include "../FLAC/ordinals.h" + +#include "../share/compat.h" + +#if defined(_MSC_VER) +#include <intrin.h> /* for _BitScanReverse* */ +#endif + +/* Will never be emitted for MSVC, GCC, Intel compilers */ +static inline uint32_t FLAC__clz_soft_uint32(FLAC__uint32 word) +{ + static const uint8_t byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + return word > 0xffffff ? byte_to_unary_table[word >> 24] : + word > 0xffff ? byte_to_unary_table[word >> 16] + 8 : + word > 0xff ? byte_to_unary_table[word >> 8] + 16 : + byte_to_unary_table[word] + 24; +} + +static inline uint32_t FLAC__clz_uint32(FLAC__uint32 v) +{ +/* Never used with input 0 */ +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v) ^ 31U; +#elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +/* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on + * -march= setting or to a software routine in exotic machines. */ + return __builtin_clz(v); +#elif defined(_MSC_VER) + { + DWORD idx; + _BitScanReverse(&idx, v); + return (uint32_t)(idx ^ 31U); + } +#else + return FLAC__clz_soft_uint32(v); +#endif +} + +/* Used when 64-bit bsr/clz is unavailable; can use 32-bit bsr/clz when possible */ +static inline uint32_t FLAC__clz_soft_uint64(FLAC__uint64 word) +{ + return (FLAC__uint32)(word>>32) ? FLAC__clz_uint32((FLAC__uint32)(word>>32)) : + FLAC__clz_uint32((FLAC__uint32)word) + 32; +} + +static inline uint32_t FLAC__clz_uint64(FLAC__uint64 v) +{ + /* Never used with input 0 */ +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return __builtin_clzll(v); +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + DWORD idx; + _BitScanReverse64(&idx, v); + return (uint32_t)(idx ^ 63U); + } +#else + return FLAC__clz_soft_uint64(v); +#endif +} + +/* These two functions work with input 0 */ +static inline uint32_t FLAC__clz2_uint32(FLAC__uint32 v) +{ + if (!v) + return 32; + return FLAC__clz_uint32(v); +} + +static inline uint32_t FLAC__clz2_uint64(FLAC__uint64 v) +{ + if (!v) + return 64; + return FLAC__clz_uint64(v); +} + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = assertion failure + * ilog2( 1) = 0 + * ilog2( 2) = 1 + * ilog2( 3) = 1 + * ilog2( 4) = 2 + * ilog2( 5) = 2 + * ilog2( 6) = 2 + * ilog2( 7) = 2 + * ilog2( 8) = 3 + * ilog2( 9) = 3 + * ilog2(10) = 3 + * ilog2(11) = 3 + * ilog2(12) = 3 + * ilog2(13) = 3 + * ilog2(14) = 3 + * ilog2(15) = 3 + * ilog2(16) = 4 + * ilog2(17) = 4 + * ilog2(18) = 4 + */ + +static inline uint32_t FLAC__bitmath_ilog2(FLAC__uint32 v) +{ +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v); +#elif defined(_MSC_VER) + { + DWORD idx; + _BitScanReverse(&idx, v); + return (uint32_t)idx; + } +#else + return FLAC__clz_uint32(v) ^ 31U; +#endif +} + +static inline uint32_t FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return __builtin_clzll(v) ^ 63U; +/* Sorry, only supported in x64/Itanium.. and both have fast FPU which makes integer-only encoder pointless */ +#elif (defined(__INTEL_COMPILER) || defined(_MSC_VER)) && (defined(_M_IA64) || defined(_M_X64)) + { + DWORD idx; + _BitScanReverse64(&idx, v); + return (uint32_t)idx; + } +#else +/* Brain-damaged compilers will use the fastest possible way that is, + de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) + (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 CC0 (Public domain). +*/ + { + static const uint8_t DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + v|= v>>1; + v|= v>>2; + v|= v>>4; + v|= v>>8; + v|= v>>16; + v|= v>>32; + v= (v>>1)+1; + return DEBRUIJN_IDX64[v*FLAC__U64L(0x218A392CD3D5DBF)>>58&0x3F]; + } +#endif +} + +uint32_t FLAC__bitmath_silog2(FLAC__int64 v); + +#endif diff --git a/src/libflac/private/bitreader.h b/src/libflac/private/bitreader.h @@ -0,0 +1,90 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__BITREADER_H +#define FLAC__PRIVATE__BITREADER_H + +#include <stdio.h> /* for FILE */ +#include "../FLAC/ordinals.h" + +/* + * opaque structure definition + */ +struct FLAC__BitReader; +typedef struct FLAC__BitReader FLAC__BitReader; + +typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data); + +/* + * construction, deletion, initialization, etc functions + */ +FLAC__BitReader *FLAC__bitreader_new(void); +void FLAC__bitreader_delete(FLAC__BitReader *br); +FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__BitReaderReadCallback rcb, void *cd); +void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ +FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); +void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); + +/* + * CRC functions + */ +void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); +FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); + +/* + * info functions + */ +FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +uint32_t FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +uint32_t FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); + +/* + * read functions + */ + +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, uint32_t bits); +FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ +FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, uint32_t bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ +FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, uint32_t nvals); /* WATCHOUT: does not CRC the read data! */ +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, uint32_t *val); +FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, uint32_t parameter); +FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], uint32_t nvals, uint32_t parameter); +#if 0 /* UNUSED */ +FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, uint32_t parameter); +FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, uint32_t *val, uint32_t parameter); +#endif +FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, uint32_t *rawlen); +FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, uint32_t *rawlen); +#endif diff --git a/src/libflac/private/crc.h b/src/libflac/private/crc.h @@ -0,0 +1,60 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2018 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__CRC_H +#define FLAC__PRIVATE__CRC_H + +#include "../FLAC/ordinals.h" + +/* 8 bit CRC generator, MSB shifted first +** polynomial = x^8 + x^2 + x^1 + x^0 +** init = 0 +*/ +FLAC__uint8 FLAC__crc8(const FLAC__byte *data, uint32_t len); + +/* 16 bit CRC generator, MSB shifted first +** polynomial = x^16 + x^15 + x^2 + x^0 +** init = 0 +*/ +extern FLAC__uint16 const FLAC__crc16_table[8][256]; + +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)]) +/* this alternate may be faster on some systems/compilers */ +#if 0 +#define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[0][((crc)>>8) ^ (data)]) & 0xffff) +#endif + +FLAC__uint16 FLAC__crc16(const FLAC__byte *data, uint32_t len); +FLAC__uint16 FLAC__crc16_update_words32(const FLAC__uint32 *words, uint32_t len, FLAC__uint16 crc); +FLAC__uint16 FLAC__crc16_update_words64(const FLAC__uint64 *words, uint32_t len, FLAC__uint16 crc); + +#endif diff --git a/src/libflac/private/fixed.h b/src/libflac/private/fixed.h @@ -0,0 +1,82 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FIXED_H +#define FLAC__PRIVATE__FIXED_H + +#include "../private/float.h" +#include "../FLAC/format.h" + +/* + * FLAC__fixed_compute_best_predictor() + * -------------------------------------------------------------------- + * Compute the best fixed predictor and the expected bits-per-sample + * of the residual signal for each order. The _wide() version uses + * 64-bit integers which is statistically necessary when bits-per- + * sample + log2(blocksize) > 30 + * + * IN data[0,data_len-1] + * IN data_len + * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] + */ +uint32_t FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); +uint32_t FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], uint32_t data_len, float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); + +/* + * FLAC__fixed_compute_residual() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__fixed_compute_residual(const FLAC__int32 data[], uint32_t data_len, uint32_t order, FLAC__int32 residual[]); + +/* + * FLAC__fixed_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__fixed_restore_signal(const FLAC__int32 residual[], uint32_t data_len, uint32_t order, FLAC__int32 data[]); + +#endif diff --git a/src/libflac/private/float.h b/src/libflac/private/float.h @@ -0,0 +1,46 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FLOAT_H +#define FLAC__PRIVATE__FLOAT_H + +#include "../FLAC/ordinals.h" + +/* + * FLAC__real is the basic floating point type used in LPC analysis. + * + * WATCHOUT: changing FLAC__real will change the signatures of many + * functions that have assembly language equivalents and break them. + */ +typedef float FLAC__real; + +#endif diff --git a/src/libflac/private/format.h b/src/libflac/private/format.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__FORMAT_H +#define FLAC__PRIVATE__FORMAT_H + +#include "../FLAC/format.h" + +uint32_t FLAC__format_get_max_rice_partition_order(uint32_t blocksize, uint32_t predictor_order); +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize(uint32_t blocksize); +uint32_t FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(uint32_t limit, uint32_t blocksize, uint32_t predictor_order); +void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object); +void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object); +FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, uint32_t max_partition_order); + +#endif diff --git a/src/libflac/private/lpc.h b/src/libflac/private/lpc.h @@ -0,0 +1,258 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__LPC_H +#define FLAC__PRIVATE__LPC_H + +#include "../private/float.h" +#include "../FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_window_data() + * -------------------------------------------------------------------- + * Applies the given window to the data. + * OPT: asm implementation + * + * IN in[0,data_len-1] + * IN window[0,data_len-1] + * OUT out[0,lag-1] + * IN data_len + */ +void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], uint32_t data_len); + +/* + * FLAC__lpc_compute_autocorrelation() + * -------------------------------------------------------------------- + * Compute the autocorrelation for lags between 0 and lag-1. + * Assumes data[] outside of [0,data_len-1] == 0. + * Asserts that lag > 0. + * + * IN data[0,data_len-1] + * IN data_len + * IN 0 < lag <= data_len + * OUT autoc[0,lag-1] + */ +void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_16_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE_SUPPORTED +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_old(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_4_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_8_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_12_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_sse_lag_16_new(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +# endif +# endif +#if defined(FLAC__CPU_PPC64) && defined(FLAC__USE_VSX) +#ifdef FLAC__HAS_TARGET_POWER9 +void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_4(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_8(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_12(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power9_vsx_lag_16(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +#endif +#ifdef FLAC__HAS_TARGET_POWER8 +void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_4(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_8(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_12(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +void FLAC__lpc_compute_autocorrelation_intrin_power8_vsx_lag_16(const FLAC__real data[], uint32_t data_len, uint32_t lag, FLAC__real autoc[]); +#endif +#endif +#endif + +/* + * FLAC__lpc_compute_lp_coefficients() + * -------------------------------------------------------------------- + * Computes LP coefficients for orders 1..max_order. + * Do not call if autoc[0] == 0.0. This means the signal is zero + * and there is no point in calculating a predictor. + * + * IN autoc[0,max_order] autocorrelation values + * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute + * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order + * *** IMPORTANT: + * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched + * OUT error[0,max_order-1] error for each order (more + * specifically, the variance of + * the error signal times # of + * samples in the signal) + * + * Example: if max_order is 9, the LP coefficients for order 9 will be + * in lp_coeff[8][0,8], the LP coefficients for order 8 will be + * in lp_coeff[7][0,7], etc. + */ +void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], uint32_t *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], double error[]); + +/* + * FLAC__lpc_quantize_coefficients() + * -------------------------------------------------------------------- + * Quantizes the LP coefficients. NOTE: precision + bits_per_sample + * must be less than 32 (sizeof(FLAC__int32)*8). + * + * IN lp_coeff[0,order-1] LP coefficients + * IN order LP order + * IN FLAC__MIN_QLP_COEFF_PRECISION < precision + * desired precision (in bits, including sign + * bit) of largest coefficient + * OUT qlp_coeff[0,order-1] quantized coefficients + * OUT shift # of bits to shift right to get approximated + * LP coefficients. NOTE: could be negative. + * RETURN 0 => quantization OK + * 1 => coefficients require too much shifting for *shift to + * fit in the LPC subframe header. 'shift' is unset. + * 2 => coefficients are all zero, which is bad. 'shift' is + * unset. + */ +int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], uint32_t order, uint32_t precision, FLAC__int32 qlp_coeff[], int *shift); + +/* + * FLAC__lpc_compute_residual_from_qlp_coefficients() + * -------------------------------------------------------------------- + * Compute the residual signal obtained from sutracting the predicted + * signal from the original. + * + * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * OUT residual[0,data_len-1] residual signal + */ +void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_asm_ia32(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_sse41(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +# endif +# ifdef FLAC__AVX2_SUPPORTED +void FLAC__lpc_compute_residual_from_qlp_coefficients_16_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +void FLAC__lpc_compute_residual_from_qlp_coefficients_wide_intrin_avx2(const FLAC__int32 *data, uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 residual[]); +# endif +# endif +#endif + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +/* + * FLAC__lpc_restore_signal() + * -------------------------------------------------------------------- + * Restore the original signal by summing the residual and the + * predictor. + * + * IN residual[0,data_len-1] residual signal + * IN data_len length of original signal + * IN qlp_coeff[0,order-1] quantized LP coefficients + * IN order > 0 LP order + * IN lp_quantization quantization of LP coefficients in bits + * *** IMPORTANT: the caller must pass in the historical samples: + * IN data[-order,-1] previously-reconstructed historical samples + * OUT data[0,data_len-1] original signal + */ +void FLAC__lpc_restore_signal(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +#ifndef FLAC__NO_ASM +# ifdef FLAC__CPU_IA32 +# ifdef FLAC__HAS_NASM +void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide_asm_ia32(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +# endif /* FLAC__HAS_NASM */ +# endif /* FLAC__CPU_IA32 */ +# if (defined FLAC__CPU_IA32 || defined FLAC__CPU_X86_64) && FLAC__HAS_X86INTRIN +# ifdef FLAC__SSE4_1_SUPPORTED +void FLAC__lpc_restore_signal_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_16_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +void FLAC__lpc_restore_signal_wide_intrin_sse41(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); +# endif +# endif +#endif /* FLAC__NO_ASM */ + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__lpc_compute_expected_bits_per_residual_sample() + * -------------------------------------------------------------------- + * Compute the expected number of bits per residual signal sample + * based on the LP error (which is related to the residual variance). + * + * IN lpc_error >= 0.0 error returned from calculating LP coefficients + * IN total_samples > 0 # of samples in residual signal + * RETURN expected bits per sample + */ +double FLAC__lpc_compute_expected_bits_per_residual_sample(double lpc_error, uint32_t total_samples); +double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(double lpc_error, double error_scale); + +/* + * FLAC__lpc_compute_best_order() + * -------------------------------------------------------------------- + * Compute the best order from the array of signal errors returned + * during coefficient computation. + * + * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients + * IN max_order > 0 max LP order + * IN total_samples > 0 # of samples in residual signal + * IN overhead_bits_per_order # of bits overhead for each increased LP order + * (includes warmup sample size and quantized LP coefficient) + * RETURN [1,max_order] best order + */ +uint32_t FLAC__lpc_compute_best_order(const double lpc_error[], uint32_t max_order, uint32_t total_samples, uint32_t overhead_bits_per_order); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/src/libflac/private/macros.h b/src/libflac/private/macros.h @@ -0,0 +1,72 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__MACROS_H +#define FLAC__PRIVATE__MACROS_H + +#if defined(__GNUC__) && (__GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + +#define flac_max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN_PASTE(A,B) A##B +#define MIN_IMPL(A,B,L) ({ \ + __typeof__(A) MIN_PASTE(__a,L) = (A); \ + __typeof__(B) MIN_PASTE(__b,L) = (B); \ + MIN_PASTE(__a,L) < MIN_PASTE(__b,L) ? MIN_PASTE(__a,L) : MIN_PASTE(__b,L); \ + }) + +#define flac_min(A,B) MIN_IMPL(A,B,__COUNTER__) + +/* Whatever other unix that has sys/param.h */ +#elif defined(HAVE_SYS_PARAM_H) +#include <sys/param.h> +#define flac_max(a,b) MAX(a,b) +#define flac_min(a,b) MIN(a,b) + +/* Windows VS has them in stdlib.h.. XXX:Untested */ +#elif defined(_MSC_VER) +#include <stdlib.h> +#define flac_max(a,b) __max(a,b) +#define flac_min(a,b) __min(a,b) +#endif + +#ifndef flac_min +#define flac_min(x,y) ((x) <= (y) ? (x) : (y)) +#endif + +#ifndef flac_max +#define flac_max(x,y) ((x) >= (y) ? (x) : (y)) +#endif + +#endif diff --git a/src/libflac/private/md5.h b/src/libflac/private/md5.h @@ -0,0 +1,50 @@ +#ifndef FLAC__PRIVATE__MD5_H +#define FLAC__PRIVATE__MD5_H + +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson <ijackson@nyx.cs.du.edu>. + * Still in the public domain. + * + * Josh Coalson: made some changes to integrate with libFLAC. + * Still in the public domain, with no warranty. + */ + +#include "../FLAC/ordinals.h" + +typedef union { + FLAC__byte *p8; + FLAC__int16 *p16; + FLAC__int32 *p32; +} FLAC__multibyte; + +typedef struct { + FLAC__uint32 in[16]; + FLAC__uint32 buf[4]; + FLAC__uint32 bytes[2]; + FLAC__multibyte internal_buf; + size_t capacity; +} FLAC__MD5Context; + +void FLAC__MD5Init(FLAC__MD5Context *context); +void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); + +FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], uint32_t channels, uint32_t samples, uint32_t bytes_per_sample); + +#endif diff --git a/src/libflac/private/memory.h b/src/libflac/private/memory.h @@ -0,0 +1,55 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__MEMORY_H +#define FLAC__PRIVATE__MEMORY_H + + +#include <stdlib.h> /* for size_t */ + +#include "../private/float.h" +#include "../FLAC/ordinals.h" /* for FLAC__bool */ + +/* Returns the unaligned address returned by malloc. + * Use free() on this address to deallocate. + */ +void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, uint32_t **unaligned_pointer, uint32_t **aligned_pointer); +#ifndef FLAC__INTEGER_ONLY_LIBRARY +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +#endif +void *safe_malloc_mul_2op_p(size_t size1, size_t size2); + +#endif diff --git a/src/libflac/private/metadata.h b/src/libflac/private/metadata.h @@ -0,0 +1,46 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2002-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__METADATA_H +#define FLAC__PRIVATE__METADATA_H + +#include "../FLAC/metadata.h" + +/* WATCHOUT: all malloc()ed data in the block is free()ed; this may not + * be a consistent state (e.g. PICTURE) or equivalent to the initial + * state after FLAC__metadata_object_new() + */ +void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); + +void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); + +#endif diff --git a/src/libflac/private/window.h b/src/libflac/private/window.h @@ -0,0 +1,70 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PRIVATE__WINDOW_H +#define FLAC__PRIVATE__WINDOW_H + +#include "../private/float.h" +#include "../FLAC/format.h" + +#ifndef FLAC__INTEGER_ONLY_LIBRARY + +/* + * FLAC__window_*() + * -------------------------------------------------------------------- + * Calculates window coefficients according to different apodization + * functions. + * + * OUT window[0,L-1] + * IN L (number of points in window) + */ +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end); +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); + +#endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ + +#endif diff --git a/src/libflac/protected/stream_decoder.h b/src/libflac/protected/stream_decoder.h @@ -0,0 +1,60 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__PROTECTED__STREAM_DECODER_H +#define FLAC__PROTECTED__STREAM_DECODER_H + +#include "../FLAC/stream_decoder.h" + +typedef struct FLAC__StreamDecoderProtected { + FLAC__StreamDecoderState state; + FLAC__StreamDecoderInitStatus initstate; + uint32_t channels; + FLAC__ChannelAssignment channel_assignment; + uint32_t bits_per_sample; + uint32_t sample_rate; /* in Hz */ + uint32_t blocksize; /* in samples (per channel) */ + FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ + +} FLAC__StreamDecoderProtected; + +/* + * return the number of input bytes consumed + */ +uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); + +/* + * return client_data from decoder + */ +FLAC_API void *get_client_data_from_decoder(FLAC__StreamDecoder *decoder); + +#endif diff --git a/src/libflac/share/alloc.h b/src/libflac/share/alloc.h @@ -0,0 +1,213 @@ +/* alloc - Convenience routines for safely allocating memory + * Copyright (C) 2007-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FLAC__SHARE__ALLOC_H +#define FLAC__SHARE__ALLOC_H + +/* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early + * before #including this file, otherwise SIZE_MAX might not be defined + */ + +#include <limits.h> /* for SIZE_MAX */ +#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */ +#include <stdlib.h> /* for size_t, malloc(), etc */ +#include "../share/compat.h" + +#ifndef SIZE_MAX +# ifndef SIZE_T_MAX +# ifdef _MSC_VER +# ifdef _WIN64 +# define SIZE_T_MAX FLAC__U64L(0xffffffffffffffff) +# else +# define SIZE_T_MAX 0xffffffff +# endif +# else +# error +# endif +# endif +# define SIZE_MAX SIZE_T_MAX +#endif + +/* avoid malloc()ing 0 bytes, see: + * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 +*/ +static inline void *safe_malloc_(size_t size) +{ + /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(!size) + size++; + return malloc(size); +} + +static inline void *safe_calloc_(size_t nmemb, size_t size) +{ + if(!nmemb || !size) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + return calloc(nmemb, size); +} + +/*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ + +static inline void *safe_malloc_add_2op_(size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) + return 0; + return safe_malloc_(size2); +} + +static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return safe_malloc_(size3); +} + +static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return safe_malloc_(size4); +} + +void *safe_malloc_mul_2op_(size_t size1, size_t size2) ; + +static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2 || !size3) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + size1 *= size2; + if(size1 > SIZE_MAX / size3) + return 0; + return malloc(size1*size3); +} + +/* size1*size2 + size3 */ +static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || !size2) + return safe_malloc_(size3); + if(size1 > SIZE_MAX / size2) + return 0; + return safe_malloc_add_2op_(size1*size2, size3); +} + +/* size1 * (size2 + size3) */ +static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + size2 += size3; + if(size2 < size3) + return 0; + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} + +static inline void *safe_realloc_(void *ptr, size_t size) +{ + void *oldptr = ptr; + void *newptr = realloc(ptr, size); + if(size > 0 && newptr == 0) + free(oldptr); + return newptr; +} +static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +{ + size2 += size1; + if(size2 < size1) { + free(ptr); + return 0; + } + return realloc(ptr, size2); +} + +static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + return realloc(ptr, size3); +} + +static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +{ + size2 += size1; + if(size2 < size1) + return 0; + size3 += size2; + if(size3 < size2) + return 0; + size4 += size3; + if(size4 < size3) + return 0; + return realloc(ptr, size4); +} + +static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +{ + if(!size1 || !size2) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + if(size1 > SIZE_MAX / size2) + return 0; + return safe_realloc_(ptr, size1*size2); +} + +/* size1 * (size2 + size3) */ +static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +{ + if(!size1 || (!size2 && !size3)) + return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ + size2 += size3; + if(size2 < size3) + return 0; + return safe_realloc_mul_2op_(ptr, size1, size2); +} + +#endif diff --git a/src/libflac/share/compat.h b/src/libflac/share/compat.h @@ -0,0 +1,205 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This is the preferred location of all CPP hackery to make $random_compiler + * work like something approaching a C99 (or maybe more accurately GNU99) + * compiler. + * + * It is assumed that this header will be included after "config.h". + */ + +#ifndef FLAC__SHARE__COMPAT_H +#define FLAC__SHARE__COMPAT_H + +#if defined _WIN32 && !defined __CYGWIN__ +/* where MSVC puts unlink() */ +# include <io.h> +#else +# include <unistd.h> +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#include <sys/types.h> /* for off_t */ +#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */ +#if !defined __MINGW32__ +#define fseeko _fseeki64 +#define ftello _ftelli64 +#else /* MinGW */ +#if !defined(HAVE_FSEEKO) +#define fseeko fseeko64 +#define ftello ftello64 +#endif +#endif +#else +#define FLAC__off_t off_t +#endif + +#if HAVE_INTTYPES_H +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +#endif + +#if defined(_MSC_VER) +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline __inline +#endif + +#if defined __INTEL_COMPILER || (defined _MSC_VER && defined _WIN64) +/* MSVS generates VERY slow 32-bit code with __restrict */ +#define flac_restrict __restrict +#elif defined __GNUC__ +#define flac_restrict __restrict__ +#else +#define flac_restrict +#endif + +#define FLAC__U64L(x) x##ULL + +#if defined _MSC_VER || defined __MINGW32__ +#define FLAC__STRCASECMP _stricmp +#define FLAC__STRNCASECMP _strnicmp +#elif defined __BORLANDC__ +#define FLAC__STRCASECMP stricmp +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRCASECMP strcasecmp +#define FLAC__STRNCASECMP strncasecmp +#endif + +#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__ +#include <io.h> /* for _setmode(), chmod() */ +#include <fcntl.h> /* for _O_BINARY */ +#else +#include <unistd.h> /* for chown(), unlink() */ +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include <utime.h> /* for utime() */ +#else +#include <sys/utime.h> /* for utime() */ +#endif +#else +#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */ +#include <utime.h> /* for utime() */ +#endif + +#if defined _MSC_VER +# if _MSC_VER >= 1800 +# include <inttypes.h> +# elif _MSC_VER >= 1600 +/* Visual Studio 2010 has decent C99 support */ +# include <stdint.h> +# define PRIu64 "llu" +# define PRId64 "lld" +# define PRIx64 "llx" +# else +# include <limits.h> +# ifndef UINT32_MAX +# define UINT32_MAX _UI32_MAX +# endif +# define PRIu64 "I64u" +# define PRId64 "I64d" +# define PRIx64 "I64x" +# endif +#endif /* defined _MSC_VER */ + +#ifdef _WIN32 +/* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */ + +#include "../share/win_utf8_io.h" +#define flac_printf printf_utf8 +#define flac_fprintf fprintf_utf8 +#define flac_vfprintf vfprintf_utf8 + +#include "../share/windows_unicode_filenames.h" +#define flac_fopen flac_internal_fopen_utf8 +#define flac_chmod flac_internal_chmod_utf8 +#define flac_utime flac_internal_utime_utf8 +#define flac_unlink flac_internal_unlink_utf8 +#define flac_rename flac_internal_rename_utf8 +#define flac_stat flac_internal_stat64_utf8 + +#else + +#define flac_printf printf +#define flac_fprintf fprintf +#define flac_vfprintf vfprintf + +#define flac_fopen fopen +#define flac_chmod chmod +#define flac_utime utime +#define flac_unlink unlink +#define flac_rename rename +#define flac_stat stat + +#endif + +#ifdef _WIN32 +#define flac_stat_s __stat64 /* stat struct */ +#define flac_fstat _fstat64 +#else +#define flac_stat_s stat /* stat struct */ +#define flac_fstat fstat +#endif + +#ifdef ANDROID +#include <limits.h> +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/* FLAC needs to compile and work correctly on systems with a normal ISO C99 + * snprintf as well as Microsoft Visual Studio which has an non-standards + * conformant snprint_s function. + * + * This function wraps the MS version to behave more like the ISO version. + */ +#include <stdarg.h> +#ifdef __cplusplus +extern "C" { +#endif +int flac_snprintf(char *str, size_t size, const char *fmt, ...); +int flac_vsnprintf(char *str, size_t size, const char *fmt, va_list va); +#ifdef __cplusplus +}; +#endif + +#endif /* FLAC__SHARE__COMPAT_H */ diff --git a/src/libflac/share/endswap.h b/src/libflac/share/endswap.h @@ -0,0 +1,84 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* It is assumed that this header will be included after "config.h". */ + +#if HAVE_BSWAP32 /* GCC and Clang */ + +/* GCC prior to 4.8 didn't provide bswap16 on x86_64 */ +#if ! HAVE_BSWAP16 +static inline unsigned short __builtin_bswap16(unsigned short a) +{ + return (a<<8)|(a>>8); +} +#endif + +#define ENDSWAP_16(x) (__builtin_bswap16 (x)) +#define ENDSWAP_32(x) (__builtin_bswap32 (x)) +#define ENDSWAP_64(x) (__builtin_bswap64 (x)) + +#elif defined _MSC_VER /* Windows */ + +#include <stdlib.h> + +#define ENDSWAP_16(x) (_byteswap_ushort (x)) +#define ENDSWAP_32(x) (_byteswap_ulong (x)) +#define ENDSWAP_64(x) (_byteswap_uint64 (x)) + +#elif defined HAVE_BYTESWAP_H /* Linux */ + +#include <byteswap.h> + +#define ENDSWAP_16(x) (bswap_16 (x)) +#define ENDSWAP_32(x) (bswap_32 (x)) +#define ENDSWAP_64(x) (bswap_64 (x)) + +#else + +#define ENDSWAP_16(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) +#define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) | (((x) >> 8) & 0xFF00) | (((x) & 0xFF00) << 8) | (((x) & 0xFF) << 24)) +#define ENDSWAP_64(x) ((ENDSWAP_32(((x) >> 32) & 0xFFFFFFFF)) | (ENDSWAP_32((x) & 0xFFFFFFFF) << 32)) + +#endif + + +/* Host to little-endian byte swapping (for MD5 calculation) */ +#if CPU_IS_BIG_ENDIAN + +#define H2LE_16(x) ENDSWAP_16 (x) +#define H2LE_32(x) ENDSWAP_32 (x) + +#else + +#define H2LE_16(x) (x) +#define H2LE_32(x) (x) + +#endif diff --git a/src/libflac/share/macros.h b/src/libflac/share/macros.h @@ -0,0 +1,45 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <errno.h> + +/* FLAC_CHECK_RETURN : Check the return value of the provided function and + * print an error message if it fails (ie returns a value < 0). + * + * Ideally, a library should not print anything, but this macro is only used + * for things that extremely unlikely to fail, like `chown` to a previoulsy + * saved `uid`. + */ + +#define FLAC_CHECK_RETURN(x) \ + { if ((x) < 0) \ + fprintf (stderr, "%s : %s\n", #x, strerror (errno)) ; \ + } diff --git a/src/libflac/share/safe_str.h b/src/libflac/share/safe_str.h @@ -0,0 +1,69 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Safe string handling functions to replace things like strcpy, strncpy, + * strcat, strncat etc. + * All of these functions guarantee a correctly NUL terminated string but + * the string may be truncated if the destination buffer was too short. + */ + +#ifndef FLAC__SHARE_SAFE_STR_H +#define FLAC__SHARE_SAFE_STR_H + +static inline char * +safe_strncat(char *dest, const char *src, size_t dest_size) +{ + char * ret; + + if (dest_size < 1) + return dest; + + ret = strncat(dest, src, dest_size - strlen (dest)); + dest [dest_size - 1] = 0; + + return ret; +} + +static inline char * +safe_strncpy(char *dest, const char *src, size_t dest_size) +{ + char * ret; + + if (dest_size < 1) + return dest; + + ret = strncpy(dest, src, dest_size); + dest [dest_size - 1] = 0; + + return ret; +} + +#endif /* FLAC__SHARE_SAFE_STR_H */ diff --git a/src/libflac/share/utf8.h b/src/libflac/share/utf8.h @@ -0,0 +1,25 @@ +#ifndef SHARE__UTF8_H +#define SHARE__UTF8_H + +/* + * Convert a string between UTF-8 and the locale's charset. + * Invalid bytes are replaced by '#', and characters that are + * not available in the target encoding are replaced by '?'. + * + * If the locale's charset is not set explicitly then it is + * obtained using nl_langinfo(CODESET), where available, the + * environment variable CHARSET, or assumed to be US-ASCII. + * + * Return value of conversion functions: + * + * -1 : memory allocation failed + * 0 : data was converted exactly + * 1 : valid data was converted approximately (using '?') + * 2 : input was invalid (but still converted, using '#') + * 3 : unknown encoding (but still converted, using '?') + */ + +int utf8_encode(const char *from, char **to); +int utf8_decode(const char *from, char **to); + +#endif diff --git a/src/libflac/share/win_utf8_io.h b/src/libflac/share/win_utf8_io.h @@ -0,0 +1,62 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#ifndef flac__win_utf8_io_h +#define flac__win_utf8_io_h + +#include <stdio.h> +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +size_t strlen_utf8(const char *str); +int win_get_console_width(void); + +int get_utf8_argv(int *argc, char ***argv); + +int printf_utf8(const char *format, ...); +int fprintf_utf8(FILE *stream, const char *format, ...); +int vfprintf_utf8(FILE *stream, const char *format, va_list argptr); + +#define WIN32_MEAN_AND_LEAN +#include <windows.h> +HANDLE WINAPI CreateFile_utf8(const char *lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/src/libflac/share/windows_unicode_filenames.h b/src/libflac/share/windows_unicode_filenames.h @@ -0,0 +1,63 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#ifndef flac__windows_unicode_filenames_h +#define flac__windows_unicode_filenames_h + +#include <stdio.h> +#include <sys/stat.h> +#include <sys/utime.h> +#include "../FLAC/ordinals.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void flac_internal_set_utf8_filenames(FLAC__bool flag); +FLAC__bool flac_internal_get_utf8_filenames(void); +#define flac_set_utf8_filenames flac_internal_set_utf8_filenames +#define flac_get_utf8_filenames flac_internal_get_utf8_filenames + +FILE* flac_internal_fopen_utf8(const char *filename, const char *mode); +int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer); +int flac_internal_chmod_utf8(const char *filename, int pmode); +int flac_internal_utime_utf8(const char *filename, struct utimbuf *times); +int flac_internal_unlink_utf8(const char *filename); +int flac_internal_rename_utf8(const char *oldname, const char *newname); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif +#endif diff --git a/src/libflac/stream_decoder.c b/src/libflac/stream_decoder.c @@ -0,0 +1,2951 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// 8bb: hide POSIX warnings +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +#include <stdio.h> +#include <stdlib.h> /* for malloc() */ +#include <string.h> /* for memset/memcpy() */ +#include <sys/stat.h> /* for stat() */ +#include <sys/types.h> /* for off_t */ +#include "share/compat.h" +#include "share/alloc.h" +#include "protected/stream_decoder.h" +#include "private/bitreader.h" +#include "private/bitmath.h" +#include "private/crc.h" +#include "private/fixed.h" +#include "private/format.h" +#include "private/lpc.h" +#include "private/md5.h" +#include "private/memory.h" +#include "private/macros.h" + +/*********************************************************************** + * + * Private static data + * + ***********************************************************************/ + +static const FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; + +/*********************************************************************** + * + * Private class method prototypes + * + ***********************************************************************/ + +static void set_defaults_(FLAC__StreamDecoder *decoder); +static FILE *get_binary_stdin_(void); +static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels); +static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); +static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length); +static FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length); +static FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length); +static FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj); +static FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj); +static FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder); +static FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode); +static FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode); +static FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode); +static FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended); +static FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder); +static FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]); +static void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status); +static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); +static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); + +/*********************************************************************** + * + * Private class data + * + ***********************************************************************/ + +typedef struct FLAC__StreamDecoderPrivate { + FLAC__bool is_ogg; + FLAC__StreamDecoderReadCallback read_callback; + FLAC__StreamDecoderSeekCallback seek_callback; + FLAC__StreamDecoderTellCallback tell_callback; + FLAC__StreamDecoderLengthCallback length_callback; + FLAC__StreamDecoderEofCallback eof_callback; + FLAC__StreamDecoderWriteCallback write_callback; + FLAC__StreamDecoderMetadataCallback metadata_callback; + FLAC__StreamDecoderErrorCallback error_callback; + /* generic 32-bit datapath: */ + void (*local_lpc_restore_signal)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); + /* generic 64-bit datapath: */ + void (*local_lpc_restore_signal_64bit)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); + /* for use when the signal is <= 16 bits-per-sample, or <= 15 bits-per-sample on a side channel (which requires 1 extra bit): */ + void (*local_lpc_restore_signal_16bit)(const FLAC__int32 residual[], uint32_t data_len, const FLAC__int32 qlp_coeff[], uint32_t order, int lp_quantization, FLAC__int32 data[]); + void *client_data; + FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ + FLAC__BitReader *input; + FLAC__int32 *output[FLAC__MAX_CHANNELS]; + FLAC__int32 *residual[FLAC__MAX_CHANNELS]; /* WATCHOUT: these are the aligned pointers; the real pointers that should be free()'d are residual_unaligned[] below */ + FLAC__EntropyCodingMethod_PartitionedRiceContents partitioned_rice_contents[FLAC__MAX_CHANNELS]; + uint32_t output_capacity, output_channels; + FLAC__uint32 fixed_block_size, next_fixed_block_size; + FLAC__uint64 samples_decoded; + FLAC__bool has_stream_info, has_seek_table; + FLAC__StreamMetadata stream_info; + FLAC__StreamMetadata seek_table; + FLAC__bool metadata_filter[128]; /* MAGIC number 128 == total number of metadata block types == 1 << 7 */ + FLAC__byte *metadata_filter_ids; + size_t metadata_filter_ids_count, metadata_filter_ids_capacity; /* units for both are IDs, not bytes */ + FLAC__Frame frame; + FLAC__bool cached; /* true if there is a byte in lookahead */ + FLAC__byte header_warmup[2]; /* contains the sync code and reserved bits */ + FLAC__byte lookahead; /* temp storage when we need to look ahead one byte in the stream */ + /* unaligned (original) pointers to allocated data */ + FLAC__int32 *residual_unaligned[FLAC__MAX_CHANNELS]; + FLAC__bool do_md5_checking; /* initially gets protected_->md5_checking but is turned off after a seek or if the metadata has a zero MD5 */ + FLAC__bool internal_reset_hack; /* used only during init() so we can call reset to set up the decoder without rewinding the input */ + FLAC__bool is_seeking; + FLAC__MD5Context md5context; + FLAC__byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ + /* (the rest of these are only used for seeking) */ + FLAC__Frame last_frame; /* holds the info of the last frame we seeked to */ + FLAC__uint64 first_frame_offset; /* hint to the seek routine of where in the stream the first audio frame starts */ + FLAC__uint64 target_sample; + uint32_t unparseable_frame_count; /* used to tell whether we're decoding a future version of FLAC or just got a bad sync */ + FLAC__bool got_a_frame; /* hack needed in Ogg FLAC seek routine to check when process_single() actually writes a frame */ +} FLAC__StreamDecoderPrivate; + +/*********************************************************************** + * + * Public static class data + * + ***********************************************************************/ + +FLAC_API const char * const FLAC__StreamDecoderStateString[] = { + "FLAC__STREAM_DECODER_SEARCH_FOR_METADATA", + "FLAC__STREAM_DECODER_READ_METADATA", + "FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC", + "FLAC__STREAM_DECODER_READ_FRAME", + "FLAC__STREAM_DECODER_END_OF_STREAM", + "FLAC__STREAM_DECODER_OGG_ERROR", + "FLAC__STREAM_DECODER_SEEK_ERROR", + "FLAC__STREAM_DECODER_ABORTED", + "FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_UNINITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderInitStatusString[] = { + "FLAC__STREAM_DECODER_INIT_STATUS_OK", + "FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER", + "FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS", + "FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR", + "FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE", + "FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED" +}; + +FLAC_API const char * const FLAC__StreamDecoderReadStatusString[] = { + "FLAC__STREAM_DECODER_READ_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM", + "FLAC__STREAM_DECODER_READ_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderSeekStatusString[] = { + "FLAC__STREAM_DECODER_SEEK_STATUS_OK", + "FLAC__STREAM_DECODER_SEEK_STATUS_ERROR", + "FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderTellStatusString[] = { + "FLAC__STREAM_DECODER_TELL_STATUS_OK", + "FLAC__STREAM_DECODER_TELL_STATUS_ERROR", + "FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderLengthStatusString[] = { + "FLAC__STREAM_DECODER_LENGTH_STATUS_OK", + "FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR", + "FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED" +}; + +FLAC_API const char * const FLAC__StreamDecoderWriteStatusString[] = { + "FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE", + "FLAC__STREAM_DECODER_WRITE_STATUS_ABORT" +}; + +FLAC_API const char * const FLAC__StreamDecoderErrorStatusString[] = { + "FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC", + "FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER", + "FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH", + "FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM" +}; + +/*********************************************************************** + * + * Class constructor/destructor + * + ***********************************************************************/ +FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) +{ + FLAC__StreamDecoder *decoder; + uint32_t i; + + decoder = calloc(1, sizeof(FLAC__StreamDecoder)); + if(decoder == 0) { + return 0; + } + + decoder->protected_ = calloc(1, sizeof(FLAC__StreamDecoderProtected)); + if(decoder->protected_ == 0) { + free(decoder); + return 0; + } + + decoder->private_ = calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + if(decoder->private_ == 0) { + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->input = FLAC__bitreader_new(); + if(decoder->private_->input == 0) { + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + decoder->private_->metadata_filter_ids_capacity = 16; + if(0 == (decoder->private_->metadata_filter_ids = malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + FLAC__bitreader_delete(decoder->private_->input); + free(decoder->private_); + free(decoder->protected_); + free(decoder); + return 0; + } + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + decoder->private_->output[i] = 0; + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + decoder->private_->has_seek_table = false; + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_init(&decoder->private_->partitioned_rice_contents[i]); + + decoder->private_->file = 0; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return decoder; +} + +FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) +{ + uint32_t i; + + if (decoder == NULL) + return ; + + (void)FLAC__stream_decoder_finish(decoder); + + if(0 != decoder->private_->metadata_filter_ids) + free(decoder->private_->metadata_filter_ids); + + FLAC__bitreader_delete(decoder->private_->input); + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) + FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(&decoder->private_->partitioned_rice_contents[i]); + + free(decoder->private_); + free(decoder->protected_); + free(decoder); +} + +/*********************************************************************** + * + * Public class methods + * + ***********************************************************************/ + +static FLAC__StreamDecoderInitStatus init_stream_internal_( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(is_ogg) + return FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER; + + if( + 0 == read_callback || + 0 == write_callback || + 0 == error_callback || + (seek_callback && (0 == tell_callback || 0 == length_callback || 0 == eof_callback)) + ) + return FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + decoder->private_->local_lpc_restore_signal = FLAC__lpc_restore_signal; + decoder->private_->local_lpc_restore_signal_64bit = FLAC__lpc_restore_signal_wide; + decoder->private_->local_lpc_restore_signal_16bit = FLAC__lpc_restore_signal; + + /* from here on, errors are fatal */ + + if(!FLAC__bitreader_init(decoder->private_->input, read_callback_, decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + decoder->private_->read_callback = read_callback; + decoder->private_->seek_callback = seek_callback; + decoder->private_->tell_callback = tell_callback; + decoder->private_->length_callback = length_callback; + decoder->private_->eof_callback = eof_callback; + decoder->private_->write_callback = write_callback; + decoder->private_->metadata_callback = metadata_callback; + decoder->private_->error_callback = error_callback; + decoder->private_->client_data = client_data; + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + decoder->private_->samples_decoded = 0; + decoder->private_->has_stream_info = false; + decoder->private_->cached = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + decoder->private_->is_seeking = false; + + decoder->private_->internal_reset_hack = true; /* so the following reset does not try to rewind the input */ + if(!FLAC__stream_decoder_reset(decoder)) { + /* above call sets the state for us */ + return FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR; + } + + return FLAC__STREAM_DECODER_INIT_STATUS_OK; +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/false + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( + FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderReadCallback read_callback, + FLAC__StreamDecoderSeekCallback seek_callback, + FLAC__StreamDecoderTellCallback tell_callback, + FLAC__StreamDecoderLengthCallback length_callback, + FLAC__StreamDecoderEofCallback eof_callback, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_stream_internal_( + decoder, + read_callback, + seek_callback, + tell_callback, + length_callback, + eof_callback, + write_callback, + metadata_callback, + error_callback, + client_data, + /*is_ogg=*/true + ); +} + +static FLAC__StreamDecoderInitStatus init_FILE_internal_( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + /* + * To make sure that our file does not go unclosed after an error, we + * must assign the FILE pointer before any further error can occur in + * this routine. + */ + if(file == stdin) + file = get_binary_stdin_(); /* just to be safe */ + + decoder->private_->file = file; + + return init_stream_internal_( + decoder, + file_read_callback_, + decoder->private_->file == stdin? 0: file_seek_callback_, + decoder->private_->file == stdin? 0: file_tell_callback_, + decoder->private_->file == stdin? 0: file_length_callback_, + file_eof_callback_, + write_callback, + metadata_callback, + error_callback, + client_data, + is_ogg + ); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_FILE( + FLAC__StreamDecoder *decoder, + FILE *file, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +static FLAC__StreamDecoderInitStatus init_file_internal_( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data, + FLAC__bool is_ogg +) +{ + FILE *file; + + /* + * To make sure that our file does not go unclosed after an error, we + * have to do the same entrance checks here that are later performed + * in FLAC__stream_decoder_init_FILE() before the FILE* is assigned. + */ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED; + + if(0 == write_callback || 0 == error_callback) + return decoder->protected_->initstate = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS; + + file = filename? flac_fopen(filename, "rb") : stdin; + + if(0 == file) + return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; + + return init_FILE_internal_(decoder, file, write_callback, metadata_callback, error_callback, client_data, is_ogg); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/false); +} + +FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( + FLAC__StreamDecoder *decoder, + const char *filename, + FLAC__StreamDecoderWriteCallback write_callback, + FLAC__StreamDecoderMetadataCallback metadata_callback, + FLAC__StreamDecoderErrorCallback error_callback, + void *client_data +) +{ + return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); +} + +FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) +{ + FLAC__bool md5_failed = false; + uint32_t i; + + if(decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return true; + + /* see the comment in FLAC__stream_decoder_reset() as to why we + * always call FLAC__MD5Final() + */ + FLAC__MD5Final(decoder->private_->computed_md5sum, &decoder->private_->md5context); + + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + + FLAC__bitreader_free(decoder->private_->input); + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN() + * require that the output arrays have a buffer of up to 3 zeroes + * in front (at negative indices) for alignment purposes; + * we use 4 to keep the data well-aligned. + */ + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + decoder->private_->output_capacity = 0; + decoder->private_->output_channels = 0; + + if(0 != decoder->private_->file) { + if(decoder->private_->file != stdin) + fclose(decoder->private_->file); + decoder->private_->file = 0; + } + + if(decoder->private_->do_md5_checking) { + if(memcmp(decoder->private_->stream_info.data.stream_info.md5sum, decoder->private_->computed_md5sum, 16)) + md5_failed = true; + } + decoder->private_->is_seeking = false; + + set_defaults_(decoder); + + decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; + + return !md5_failed; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long value) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + (void)value; + return false; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_md5_checking(FLAC__StreamDecoder *decoder, FLAC__bool value) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->protected_->md5_checking = value; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + /* double protection */ + if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = true; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_all(FLAC__StreamDecoder *decoder) +{ + uint32_t i; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + for(i = 0; i < sizeof(decoder->private_->metadata_filter) / sizeof(decoder->private_->metadata_filter[0]); i++) + decoder->private_->metadata_filter[i] = true; + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore(FLAC__StreamDecoder *decoder, FLAC__MetadataType type) +{ + /* double protection */ + if((uint32_t)type > FLAC__MAX_METADATA_TYPE_CODE) + return false; + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + decoder->private_->metadata_filter[type] = false; + if(type == FLAC__METADATA_TYPE_APPLICATION) + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__StreamDecoder *decoder, const FLAC__byte id[4]) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + if(!decoder->private_->metadata_filter[FLAC__METADATA_TYPE_APPLICATION]) + return true; + + if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { + if(0 == (decoder->private_->metadata_filter_ids = safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->private_->metadata_filter_ids_capacity *= 2; + } + + memcpy(decoder->private_->metadata_filter_ids + decoder->private_->metadata_filter_ids_count * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)); + decoder->private_->metadata_filter_ids_count++; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_all(FLAC__StreamDecoder *decoder) +{ + if(decoder->protected_->state != FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter_ids_count = 0; + return true; +} + +FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->state; +} + +FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder) +{ + return FLAC__StreamDecoderStateString[decoder->protected_->state]; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_md5_checking(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->md5_checking; +} + +FLAC_API FLAC__uint64 FLAC__stream_decoder_get_total_samples(const FLAC__StreamDecoder *decoder) +{ + return decoder->private_->has_stream_info? decoder->private_->stream_info.data.stream_info.total_samples : 0; +} + +FLAC_API uint32_t FLAC__stream_decoder_get_channels(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->channels; +} + +FLAC_API FLAC__ChannelAssignment FLAC__stream_decoder_get_channel_assignment(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->channel_assignment; +} + +FLAC_API uint32_t FLAC__stream_decoder_get_bits_per_sample(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->bits_per_sample; +} + +FLAC_API uint32_t FLAC__stream_decoder_get_sample_rate(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->sample_rate; +} + +FLAC_API uint32_t FLAC__stream_decoder_get_blocksize(const FLAC__StreamDecoder *decoder) +{ + return decoder->protected_->blocksize; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_get_decode_position(const FLAC__StreamDecoder *decoder, FLAC__uint64 *position) +{ + if(0 == decoder->private_->tell_callback) + return false; + if(decoder->private_->tell_callback(decoder, position, decoder->private_->client_data) != FLAC__STREAM_DECODER_TELL_STATUS_OK) + return false; + /* should never happen since all FLAC frames and metadata blocks are byte aligned, but check just in case */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) + return false; + *position -= FLAC__stream_decoder_get_input_bytes_unconsumed(decoder); + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_flush(FLAC__StreamDecoder *decoder) +{ + if(!decoder->private_->internal_reset_hack && decoder->protected_->state == FLAC__STREAM_DECODER_UNINITIALIZED) + return false; + + decoder->private_->samples_decoded = 0; + decoder->private_->do_md5_checking = false; + + if(!FLAC__bitreader_clear(decoder->private_->input)) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_reset(FLAC__StreamDecoder *decoder) +{ + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + + /* Rewind if necessary. If FLAC__stream_decoder_init() is calling us, + * (internal_reset_hack) don't try to rewind since we are already at + * the beginning of the stream and don't want to fail if the input is + * not seekable. + */ + if(!decoder->private_->internal_reset_hack) { + if(decoder->private_->file == stdin) + return false; /* can't rewind stdin, reset fails */ + if(decoder->private_->seek_callback && decoder->private_->seek_callback(decoder, 0, decoder->private_->client_data) == FLAC__STREAM_DECODER_SEEK_STATUS_ERROR) + return false; /* seekable and seek fails, reset fails */ + } + else + decoder->private_->internal_reset_hack = false; + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; + + decoder->private_->has_stream_info = false; + + free(decoder->private_->seek_table.data.seek_table.points); + decoder->private_->seek_table.data.seek_table.points = 0; + decoder->private_->has_seek_table = false; + + decoder->private_->do_md5_checking = decoder->protected_->md5_checking; + /* + * This goes in reset() and not flush() because according to the spec, a + * fixed-blocksize stream must stay that way through the whole stream. + */ + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size = 0; + + /* We initialize the FLAC__MD5Context even though we may never use it. This + * is because md5 checking may be turned on to start and then turned off if + * a seek occurs. So we init the context here and finalize it in + * FLAC__stream_decoder_finish() to make sure things are always cleaned up + * properly. + */ + FLAC__MD5Init(&decoder->private_->md5context); + + decoder->private_->first_frame_offset = 0; + decoder->private_->unparseable_frame_count = 0; + + return true; +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_single(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + else + return true; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_metadata(FLAC__StreamDecoder *decoder) +{ + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_process_until_end_of_stream(FLAC__StreamDecoder *decoder) +{ + FLAC__bool dummy; + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + if(!find_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_METADATA: + if(!read_metadata_(decoder)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &dummy, /*do_full_decode=*/true)) + return false; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_skip_single_frame(FLAC__StreamDecoder *decoder) +{ + FLAC__bool got_a_frame; + + while(1) { + switch(decoder->protected_->state) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + return false; /* above function sets the status for us */ + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + if(!frame_sync_(decoder)) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_READ_FRAME: + if(!read_frame_(decoder, &got_a_frame, /*do_full_decode=*/false)) + return false; /* above function sets the status for us */ + if(got_a_frame) + return true; /* above function sets the status for us */ + break; + case FLAC__STREAM_DECODER_END_OF_STREAM: + case FLAC__STREAM_DECODER_ABORTED: + return true; + default: + return false; + } + } +} + +FLAC_API FLAC__bool FLAC__stream_decoder_seek_absolute(FLAC__StreamDecoder *decoder, FLAC__uint64 sample) +{ + FLAC__uint64 length; + + if( + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_METADATA && + decoder->protected_->state != FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC && + decoder->protected_->state != FLAC__STREAM_DECODER_READ_FRAME && + decoder->protected_->state != FLAC__STREAM_DECODER_END_OF_STREAM + ) + return false; + + if(0 == decoder->private_->seek_callback) + return false; + + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) + return false; + + decoder->private_->is_seeking = true; + + /* turn off md5 checking if a seek is attempted */ + decoder->private_->do_md5_checking = false; + + /* get the file length (currently our algorithm needs to know the length so it's also an error to get FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED) */ + if(decoder->private_->length_callback(decoder, &length, decoder->private_->client_data) != FLAC__STREAM_DECODER_LENGTH_STATUS_OK) { + decoder->private_->is_seeking = false; + return false; + } + + /* if we haven't finished processing the metadata yet, do that so we have the STREAMINFO, SEEK_TABLE, and first_frame_offset */ + if( + decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA || + decoder->protected_->state == FLAC__STREAM_DECODER_READ_METADATA + ) { + if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { + /* above call sets the state for us */ + decoder->private_->is_seeking = false; + return false; + } + /* check this again in case we didn't know total_samples the first time */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0 && sample >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->private_->is_seeking = false; + return false; + } + } + + { + const FLAC__bool ok = seek_to_absolute_sample_(decoder, length, sample); + decoder->private_->is_seeking = false; + return ok; + } +} + +/*********************************************************************** + * + * Protected class methods + * + ***********************************************************************/ + +uint32_t FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder) +{ + return FLAC__bitreader_get_input_bits_unconsumed(decoder->private_->input) / 8; +} + +/*********************************************************************** + * + * Private class methods + * + ***********************************************************************/ + +void set_defaults_(FLAC__StreamDecoder *decoder) +{ + decoder->private_->is_ogg = false; + decoder->private_->read_callback = 0; + decoder->private_->seek_callback = 0; + decoder->private_->tell_callback = 0; + decoder->private_->length_callback = 0; + decoder->private_->eof_callback = 0; + decoder->private_->write_callback = 0; + decoder->private_->metadata_callback = 0; + decoder->private_->error_callback = 0; + decoder->private_->client_data = 0; + + memset(decoder->private_->metadata_filter, 0, sizeof(decoder->private_->metadata_filter)); + decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] = true; + decoder->private_->metadata_filter_ids_count = 0; + + decoder->protected_->md5_checking = false; +} + +/* + * This will forcibly set stdin to binary mode (for OSes that require it) + */ +FILE *get_binary_stdin_(void) +{ + /* if something breaks here it is probably due to the presence or + * absence of an underscore before the identifiers 'setmode', + * 'fileno', and/or 'O_BINARY'; check your system header files. + */ +#if defined _MSC_VER || defined __MINGW32__ + _setmode(_fileno(stdin), _O_BINARY); +#elif defined __EMX__ + setmode(fileno(stdin), O_BINARY); +#endif + + return stdin; +} + +FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, uint32_t size, uint32_t channels) +{ + uint32_t i; + FLAC__int32 *tmp; + + if(size <= decoder->private_->output_capacity && channels <= decoder->private_->output_channels) + return true; + + /* simply using realloc() is not practical because the number of channels may change mid-stream */ + + for(i = 0; i < FLAC__MAX_CHANNELS; i++) { + if(0 != decoder->private_->output[i]) { + free(decoder->private_->output[i]-4); + decoder->private_->output[i] = 0; + } + if(0 != decoder->private_->residual_unaligned[i]) { + free(decoder->private_->residual_unaligned[i]); + decoder->private_->residual_unaligned[i] = decoder->private_->residual[i] = 0; + } + } + + for(i = 0; i < channels; i++) { + /* WATCHOUT: + * FLAC__lpc_restore_signal_asm_ia32_mmx() and ..._intrin_sseN() + * require that the output arrays have a buffer of up to 3 zeroes + * in front (at negative indices) for alignment purposes; + * we use 4 to keep the data well-aligned. + */ + tmp = safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); + if(tmp == 0) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + memset(tmp, 0, sizeof(FLAC__int32)*4); + decoder->private_->output[i] = tmp + 4; + + if(!FLAC__memory_alloc_aligned_int32_array(size, &decoder->private_->residual_unaligned[i], &decoder->private_->residual[i])) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + } + + decoder->private_->output_capacity = size; + decoder->private_->output_channels = channels; + + return true; +} + +FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) +{ + size_t i; + + for(i = 0; i < decoder->private_->metadata_filter_ids_count; i++) + if(0 == memcmp(decoder->private_->metadata_filter_ids + i * (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8), id, (FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))) + return true; + + return false; +} + +FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + uint32_t i, id; + FLAC__bool first = true; + + for(i = id = 0; i < 4; ) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == FLAC__STREAM_SYNC_STRING[i]) { + first = true; + i++; + id = 0; + continue; + } + + if(id >= 3) + return false; + + if(x == ID3V2_TAG_[id]) { + id++; + i = 0; + if(id == 3) { + if(!skip_id3v2_tag_(decoder)) + return false; /* skip_id3v2_tag_ sets the state for us */ + } + continue; + } + id = 0; + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + i = 0; + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_READ_METADATA; + return true; +} + +FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) +{ + FLAC__bool is_last; + FLAC__uint32 i, x, type, length; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_IS_LAST_LEN)) + return false; /* read_callback_ sets the state for us */ + is_last = x? true : false; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &type, FLAC__STREAM_METADATA_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &length, FLAC__STREAM_METADATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(type == FLAC__METADATA_TYPE_STREAMINFO) { + if(!read_metadata_streaminfo_(decoder, is_last, length)) + return false; + + decoder->private_->has_stream_info = true; + if(0 == memcmp(decoder->private_->stream_info.data.stream_info.md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + decoder->private_->do_md5_checking = false; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_STREAMINFO] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->stream_info, decoder->private_->client_data); + } + else if(type == FLAC__METADATA_TYPE_SEEKTABLE) { + /* just in case we already have a seek table, and reading the next one fails: */ + decoder->private_->has_seek_table = false; + + if(!read_metadata_seektable_(decoder, is_last, length)) + return false; + + decoder->private_->has_seek_table = true; + if(!decoder->private_->is_seeking && decoder->private_->metadata_filter[FLAC__METADATA_TYPE_SEEKTABLE] && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &decoder->private_->seek_table, decoder->private_->client_data); + } + else { + FLAC__bool skip_it = !decoder->private_->metadata_filter[type]; + uint32_t real_length = length; + FLAC__StreamMetadata block; + + memset(&block, 0, sizeof(block)); + block.is_last = is_last; + block.type = (FLAC__MetadataType)type; + block.length = length; + + if(type == FLAC__METADATA_TYPE_APPLICATION) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(real_length < FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) { /* underflow check */ + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR;/*@@@@@@ maybe wrong error? need to resync?*/ + return false; + } + + real_length -= FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8; + + if(decoder->private_->metadata_filter_ids_count > 0 && has_id_filtered_(decoder, block.data.application.id)) + skip_it = !skip_it; + } + + if(skip_it) { + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + return false; /* read_callback_ sets the state for us */ + } + else { + FLAC__bool ok = true; + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + /* skip the padding bytes */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, real_length)) + ok = false; /* read_callback_ sets the state for us */ + break; + case FLAC__METADATA_TYPE_APPLICATION: + /* remember, we read the ID already */ + if(real_length > 0) { + if(0 == (block.data.application.data = malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + ok = false; + } + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.application.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ + } + else + block.data.application.data = 0; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(!read_metadata_vorbiscomment_(decoder, &block.data.vorbis_comment, real_length)) + ok = false; + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(!read_metadata_cuesheet_(decoder, &block.data.cue_sheet)) + ok = false; + break; + case FLAC__METADATA_TYPE_PICTURE: + if(!read_metadata_picture_(decoder, &block.data.picture)) + ok = false; + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + break; + default: + if(real_length > 0) { + if(0 == (block.data.unknown.data = malloc(real_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + ok = false; + } + else if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, block.data.unknown.data, real_length)) + ok = false; /* read_callback_ sets the state for us */ + } + else + block.data.unknown.data = 0; + break; + } + if(ok && !decoder->private_->is_seeking && decoder->private_->metadata_callback) + decoder->private_->metadata_callback(decoder, &block, decoder->private_->client_data); + + /* now we have to free any malloc()ed data in the block */ + switch(type) { + case FLAC__METADATA_TYPE_PADDING: + break; + case FLAC__METADATA_TYPE_APPLICATION: + if(0 != block.data.application.data) + free(block.data.application.data); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + if(0 != block.data.vorbis_comment.vendor_string.entry) + free(block.data.vorbis_comment.vendor_string.entry); + if(block.data.vorbis_comment.num_comments > 0) + for(i = 0; i < block.data.vorbis_comment.num_comments; i++) + if(0 != block.data.vorbis_comment.comments[i].entry) + free(block.data.vorbis_comment.comments[i].entry); + if(0 != block.data.vorbis_comment.comments) + free(block.data.vorbis_comment.comments); + break; + case FLAC__METADATA_TYPE_CUESHEET: + if(block.data.cue_sheet.num_tracks > 0) + for(i = 0; i < block.data.cue_sheet.num_tracks; i++) + if(0 != block.data.cue_sheet.tracks[i].indices) + free(block.data.cue_sheet.tracks[i].indices); + if(0 != block.data.cue_sheet.tracks) + free(block.data.cue_sheet.tracks); + break; + case FLAC__METADATA_TYPE_PICTURE: + if(0 != block.data.picture.mime_type) + free(block.data.picture.mime_type); + if(0 != block.data.picture.description) + free(block.data.picture.description); + if(0 != block.data.picture.data) + free(block.data.picture.data); + break; + case FLAC__METADATA_TYPE_STREAMINFO: + case FLAC__METADATA_TYPE_SEEKTABLE: + default: + if(0 != block.data.unknown.data) + free(block.data.unknown.data); + break; + } + + if(!ok) /* anything that unsets "ok" should also make sure decoder->protected_->state is updated */ + return false; + } + } + + if(is_last) { + /* if this fails, it's OK, it's just a hint for the seek routine */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &decoder->private_->first_frame_offset)) + decoder->private_->first_frame_offset = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + + return true; +} + +FLAC__bool read_metadata_streaminfo_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length) +{ + FLAC__uint32 x; + uint32_t bits, used_bits = 0; + + decoder->private_->stream_info.type = FLAC__METADATA_TYPE_STREAMINFO; + decoder->private_->stream_info.is_last = is_last; + decoder->private_->stream_info.length = length; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, bits)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_blocksize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.min_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.max_framesize = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.sample_rate = x; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.channels = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->stream_info.data.stream_info.bits_per_sample = x+1; + used_bits += bits; + + bits = FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &decoder->private_->stream_info.data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + used_bits += bits; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, decoder->private_->stream_info.data.stream_info.md5sum, 16)) + return false; /* read_callback_ sets the state for us */ + used_bits += 16*8; + + /* skip the rest of the block */ + length -= (used_bits / 8); + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + + return true; +} + +FLAC__bool read_metadata_seektable_(FLAC__StreamDecoder *decoder, FLAC__bool is_last, uint32_t length) +{ + FLAC__uint32 i, x; + FLAC__uint64 xx; + + decoder->private_->seek_table.type = FLAC__METADATA_TYPE_SEEKTABLE; + decoder->private_->seek_table.is_last = is_last; + decoder->private_->seek_table.length = length; + + decoder->private_->seek_table.data.seek_table.num_points = length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH; + + /* use realloc since we may pass through here several times (e.g. after seeking) */ + if(0 == (decoder->private_->seek_table.data.seek_table.points = safe_realloc_mul_2op_(decoder->private_->seek_table.data.seek_table.points, decoder->private_->seek_table.data.seek_table.num_points, /*times*/sizeof(FLAC__StreamMetadata_SeekPoint)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < decoder->private_->seek_table.data.seek_table.num_points; i++) { + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].sample_number = xx; + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &xx, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].stream_offset = xx; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->seek_table.data.seek_table.points[i].frame_samples = x; + } + length -= (decoder->private_->seek_table.data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH); + /* if there is a partial point left, skip over it */ + if(length > 0) { + /*@@@ do a send_error_to_client_() here? there's an argument for either way */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_VorbisComment *obj, uint32_t length) +{ + FLAC__uint32 i; + + /* read vendor string */ + if (length >= 8) { + length -= 8; /* vendor string length + num comments entries alone take 8 bytes */ + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + if (obj->vendor_string.length > 0) { + if (length < obj->vendor_string.length) { + obj->vendor_string.length = 0; + obj->vendor_string.entry = 0; + goto skip; + } + else + length -= obj->vendor_string.length; + if (0 == (obj->vendor_string.entry = safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->vendor_string.entry, obj->vendor_string.length)) + return false; /* read_callback_ sets the state for us */ + obj->vendor_string.entry[obj->vendor_string.length] = '\0'; + } + else + obj->vendor_string.entry = 0; + + /* read num comments */ + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->num_comments)) + return false; /* read_callback_ sets the state for us */ + + /* read comments */ + if (obj->num_comments > 100000) { + /* Possibly malicious file. */ + obj->num_comments = 0; + return false; + } + if (obj->num_comments > 0) { + if (0 == (obj->comments = safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { + obj->num_comments = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for (i = 0; i < obj->num_comments; i++) { + /* Initialize here just to make sure. */ + obj->comments[i].length = 0; + obj->comments[i].entry = 0; + + if (length < 4) { + obj->num_comments = i; + goto skip; + } + else + length -= 4; + if (!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) { + obj->num_comments = i; + return false; /* read_callback_ sets the state for us */ + } + if (obj->comments[i].length > 0) { + if (length < obj->comments[i].length) { + obj->num_comments = i; + goto skip; + } + else + length -= obj->comments[i].length; + if (0 == (obj->comments[i].entry = safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + obj->num_comments = i; + return false; + } + memset (obj->comments[i].entry, 0, obj->comments[i].length) ; + if (!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->comments[i].entry, obj->comments[i].length)) { + /* Current i-th entry is bad, so we delete it. */ + free (obj->comments[i].entry) ; + obj->comments[i].entry = NULL ; + obj->num_comments = i; + goto skip; + } + obj->comments[i].entry[obj->comments[i].length] = '\0'; + } + else + obj->comments[i].entry = 0; + } + } + } + + skip: + if (length > 0) { + /* length > 0 can only happen on files with invalid data in comments */ + if(obj->num_comments < 1) { + free(obj->comments); + obj->comments = NULL; + } + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_CueSheet *obj) +{ + FLAC__uint32 i, j, x; + + memset(obj, 0, sizeof(FLAC__StreamMetadata_CueSheet)); + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &obj->lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->is_cd = x? true : false; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->num_tracks = x; + + if(obj->num_tracks > 0) { + if(0 == (obj->tracks = safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(i = 0; i < obj->num_tracks; i++) { + FLAC__StreamMetadata_CueSheet_Track *track = &obj->tracks[i]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + track->number = (FLAC__byte)x; + + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + track->type = x; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) + return false; /* read_callback_ sets the state for us */ + track->pre_emphasis = x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) + return false; /* read_callback_ sets the state for us */ + track->num_indices = (FLAC__byte)x; + + if(track->num_indices > 0) { + if(0 == (track->indices = safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + for(j = 0; j < track->num_indices; j++) { + FLAC__StreamMetadata_CueSheet_Index *indx = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &indx->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + return false; /* read_callback_ sets the state for us */ + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + return false; /* read_callback_ sets the state for us */ + indx->number = (FLAC__byte)x; + + if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) + return false; /* read_callback_ sets the state for us */ + } + } + } + } + + return true; +} + +FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMetadata_Picture *obj) +{ + FLAC__uint32 x; + + /* read type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + obj->type = x; + + /* read MIME type */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->mime_type = safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, (FLAC__byte*)obj->mime_type, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->mime_type[x] = '\0'; + + /* read description */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->description = safe_malloc_add_2op_(x, /*+*/1))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(x > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->description, x)) + return false; /* read_callback_ sets the state for us */ + } + obj->description[x] = '\0'; + + /* read width */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read height */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read depth */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read colors */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &obj->colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) + return false; /* read_callback_ sets the state for us */ + + /* read data */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &(obj->data_length), FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) + return false; /* read_callback_ sets the state for us */ + if(0 == (obj->data = safe_malloc_(obj->data_length))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + if(obj->data_length > 0) { + if(!FLAC__bitreader_read_byte_block_aligned_no_crc(decoder->private_->input, obj->data, obj->data_length)) + return false; /* read_callback_ sets the state for us */ + } + + return true; +} + +FLAC__bool skip_id3v2_tag_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + uint32_t i, skip; + + /* skip the version and flags bytes */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 24)) + return false; /* read_callback_ sets the state for us */ + /* get the size (in bytes) to skip */ + skip = 0; + for(i = 0; i < 4; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + skip <<= 7; + skip |= (x & 0x7f); + } + /* skip the rest of the tag */ + if(!FLAC__bitreader_skip_byte_block_aligned_no_crc(decoder->private_->input, skip)) + return false; /* read_callback_ sets the state for us */ + return true; +} + +FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__bool first = true; + + /* If we know the total number of samples in the stream, stop if we've read that many. */ + /* This will stop us, for example, from wasting time trying to sync on an ID3V1 tag. */ + if(FLAC__stream_decoder_get_total_samples(decoder) > 0) { + if(decoder->private_->samples_decoded >= FLAC__stream_decoder_get_total_samples(decoder)) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return true; + } + } + + /* make sure we're byte aligned */ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + } + + while(1) { + if(decoder->private_->cached) { + x = (FLAC__uint32)decoder->private_->lookahead; + decoder->private_->cached = false; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + } + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->header_warmup[0] = (FLAC__byte)x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + + /* we have to check if we just read two 0xff's in a row; the second may actually be the beginning of the sync code */ + /* else we have to check if the second byte is the end of a sync code */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + } + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ + decoder->private_->header_warmup[1] = (FLAC__byte)x; + decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; + return true; + } + } + if(first) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + first = false; + } + } + + return true; +} + +FLAC__bool read_frame_(FLAC__StreamDecoder *decoder, FLAC__bool *got_a_frame, FLAC__bool do_full_decode) +{ + uint32_t channel; + uint32_t i; + FLAC__int32 mid, side; + uint32_t frame_crc; /* the one we calculate from the input stream */ + FLAC__uint32 x; + + *got_a_frame = false; + + /* init the CRC */ + frame_crc = 0; + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[0], frame_crc); + frame_crc = FLAC__CRC16_UPDATE(decoder->private_->header_warmup[1], frame_crc); + FLAC__bitreader_reset_read_crc16(decoder->private_->input, (FLAC__uint16)frame_crc); + + if(!read_frame_header_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means we didn't sync on a valid header */ + return true; + if(!allocate_output_(decoder, decoder->private_->frame.header.blocksize, decoder->private_->frame.header.channels)) + return false; + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + /* + * first figure the correct bits-per-sample of the subframe + */ + uint32_t bps = decoder->private_->frame.header.bits_per_sample; + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* no adjustment needed */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + if(channel == 1) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + if(channel == 0) + bps++; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + if(channel == 1) + bps++; + break; + default: break; + } + /* + * now read it + */ + if(!read_subframe_(decoder, channel, bps, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + if(!read_zero_padding_(decoder)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption (i.e. "zero bits" were not all zeroes) */ + return true; + + /* + * Read the frame CRC-16 from the footer and check + */ + frame_crc = FLAC__bitreader_get_read_crc16(decoder->private_->input); + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__FRAME_FOOTER_CRC_LEN)) + return false; /* read_callback_ sets the state for us */ + if(frame_crc == x) { + if(do_full_decode) { + /* Undo any special channel coding */ + switch(decoder->private_->frame.header.channel_assignment) { + case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: + /* do nothing */ + break; + case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[1][i] = decoder->private_->output[0][i] - decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + decoder->private_->output[0][i] += decoder->private_->output[1][i]; + break; + case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + mid = decoder->private_->output[0][i]; + side = decoder->private_->output[1][i]; + mid = ((uint32_t) mid) << 1; + mid |= (side & 1); /* i.e. if 'side' is odd... */ + decoder->private_->output[0][i] = (mid + side) >> 1; + decoder->private_->output[1][i] = (mid - side) >> 1; + } + break; + default: break; + } + } + } + else { + /* Bad frame, emit error and zero the output signal */ + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH); + if(do_full_decode) { + for(channel = 0; channel < decoder->private_->frame.header.channels; channel++) { + memset(decoder->private_->output[channel], 0, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + } + } + } + + *got_a_frame = true; + + /* we wait to update fixed_block_size until here, when we're sure we've got a proper frame and hence a correct blocksize */ + if(decoder->private_->next_fixed_block_size) + decoder->private_->fixed_block_size = decoder->private_->next_fixed_block_size; + + /* put the latest values into the public section of the decoder instance */ + decoder->protected_->channels = decoder->private_->frame.header.channels; + decoder->protected_->channel_assignment = decoder->private_->frame.header.channel_assignment; + decoder->protected_->bits_per_sample = decoder->private_->frame.header.bits_per_sample; + decoder->protected_->sample_rate = decoder->private_->frame.header.sample_rate; + decoder->protected_->blocksize = decoder->private_->frame.header.blocksize; + decoder->private_->samples_decoded = decoder->private_->frame.header.number.sample_number + decoder->private_->frame.header.blocksize; + + /* write it */ + if(do_full_decode) { + if(write_audio_frame_to_client_(decoder, &decoder->private_->frame, (const FLAC__int32 * const *)decoder->private_->output) != FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + } + + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; +} + +FLAC__bool read_frame_header_(FLAC__StreamDecoder *decoder) +{ + FLAC__uint32 x; + FLAC__uint64 xx; + uint32_t i, blocksize_hint = 0, sample_rate_hint = 0; + FLAC__byte crc8, raw_header[16]; /* MAGIC NUMBER based on the maximum frame header size, including CRC */ + uint32_t raw_header_len; + FLAC__bool is_unparseable = false; + + /* init the raw header with the saved bits from synchronization */ + raw_header[0] = decoder->private_->header_warmup[0]; + raw_header[1] = decoder->private_->header_warmup[1]; + raw_header_len = 2; + + /* check to make sure that reserved bit is 0 */ + if(raw_header[1] & 0x02) /* MAGIC NUMBER */ + is_unparseable = true; + + /* + * Note that along the way as we read the header, we look for a sync + * code inside. If we find one it would indicate that our original + * sync was bad since there cannot be a sync code in a valid header. + * + * Three kinds of things can go wrong when reading the frame header: + * 1) We may have sync'ed incorrectly and not landed on a frame header. + * If we don't find a sync code, it can end up looking like we read + * a valid but unparseable header, until getting to the frame header + * CRC. Even then we could get a false positive on the CRC. + * 2) We may have sync'ed correctly but on an unparseable frame (from a + * future encoder). + * 3) We may be on a damaged frame which appears valid but unparseable. + * + * For all these reasons, we try and read a complete frame header as + * long as it seems valid, even if unparseable, up until the frame + * header CRC. + */ + + /* + * read in the raw header as bytes so we can CRC it, and parse it on the way + */ + for(i = 0; i < 2; i++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ + /* if we get here it means our original sync was erroneous since the sync code cannot appear in the header */ + decoder->private_->lookahead = (FLAC__byte)x; + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + raw_header[raw_header_len++] = (FLAC__byte)x; + } + + switch(x = raw_header[2] >> 4) { + case 0: + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.blocksize = 192; + break; + case 2: + case 3: + case 4: + case 5: + decoder->private_->frame.header.blocksize = 576 << (x-2); + break; + case 6: + case 7: + blocksize_hint = x; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + decoder->private_->frame.header.blocksize = 256 << (x-8); + break; + default: + break; + } + + switch(x = raw_header[2] & 0x0f) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.sample_rate = decoder->private_->stream_info.data.stream_info.sample_rate; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.sample_rate = 88200; + break; + case 2: + decoder->private_->frame.header.sample_rate = 176400; + break; + case 3: + decoder->private_->frame.header.sample_rate = 192000; + break; + case 4: + decoder->private_->frame.header.sample_rate = 8000; + break; + case 5: + decoder->private_->frame.header.sample_rate = 16000; + break; + case 6: + decoder->private_->frame.header.sample_rate = 22050; + break; + case 7: + decoder->private_->frame.header.sample_rate = 24000; + break; + case 8: + decoder->private_->frame.header.sample_rate = 32000; + break; + case 9: + decoder->private_->frame.header.sample_rate = 44100; + break; + case 10: + decoder->private_->frame.header.sample_rate = 48000; + break; + case 11: + decoder->private_->frame.header.sample_rate = 96000; + break; + case 12: + case 13: + case 14: + sample_rate_hint = x; + break; + case 15: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + default: + break; + } + + x = (uint32_t)(raw_header[3] >> 4); + if(x & 8) { + decoder->private_->frame.header.channels = 2; + switch(x & 7) { + case 0: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE; + break; + case 1: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE; + break; + case 2: + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_MID_SIDE; + break; + default: + is_unparseable = true; + break; + } + } + else { + decoder->private_->frame.header.channels = (uint32_t)x + 1; + decoder->private_->frame.header.channel_assignment = FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT; + } + + switch(x = (uint32_t)(raw_header[3] & 0x0e) >> 1) { + case 0: + if(decoder->private_->has_stream_info) + decoder->private_->frame.header.bits_per_sample = decoder->private_->stream_info.data.stream_info.bits_per_sample; + else + is_unparseable = true; + break; + case 1: + decoder->private_->frame.header.bits_per_sample = 8; + break; + case 2: + decoder->private_->frame.header.bits_per_sample = 12; + break; + case 4: + decoder->private_->frame.header.bits_per_sample = 16; + break; + case 5: + decoder->private_->frame.header.bits_per_sample = 20; + break; + case 6: + decoder->private_->frame.header.bits_per_sample = 24; + break; + case 3: + case 7: + is_unparseable = true; + break; + default: + break; + } + + /* check to make sure that reserved bit is 0 */ + if(raw_header[3] & 0x01) /* MAGIC NUMBER */ + is_unparseable = true; + + /* read the frame's starting sample number (or frame number as the case may be) */ + if( + raw_header[1] & 0x01 || + /*@@@ this clause is a concession to the old way of doing variable blocksize; the only known implementation is flake and can probably be removed without inconveniencing anyone */ + (decoder->private_->has_stream_info && decoder->private_->stream_info.data.stream_info.min_blocksize != decoder->private_->stream_info.data.stream_info.max_blocksize) + ) { /* variable blocksize */ + if(!FLAC__bitreader_read_utf8_uint64(decoder->private_->input, &xx, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(xx == FLAC__U64L(0xffffffffffffffff)) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + decoder->private_->frame.header.number.sample_number = xx; + } + else { /* fixed blocksize */ + if(!FLAC__bitreader_read_utf8_uint32(decoder->private_->input, &x, raw_header, &raw_header_len)) + return false; /* read_callback_ sets the state for us */ + if(x == 0xffffffff) { /* i.e. non-UTF8 code... */ + decoder->private_->lookahead = raw_header[raw_header_len-1]; /* back up as much as we can */ + decoder->private_->cached = true; + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER; + decoder->private_->frame.header.number.frame_number = x; + } + + if(blocksize_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(blocksize_hint == 7) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + decoder->private_->frame.header.blocksize = x+1; + } + + if(sample_rate_hint) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)x; + if(sample_rate_hint != 12) { + FLAC__uint32 _x; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &_x, 8)) + return false; /* read_callback_ sets the state for us */ + raw_header[raw_header_len++] = (FLAC__byte)_x; + x = (x << 8) | _x; + } + if(sample_rate_hint == 12) + decoder->private_->frame.header.sample_rate = x*1000; + else if(sample_rate_hint == 13) + decoder->private_->frame.header.sample_rate = x; + else + decoder->private_->frame.header.sample_rate = x*10; + } + + /* read the CRC-8 byte */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) + return false; /* read_callback_ sets the state for us */ + crc8 = (FLAC__byte)x; + + if(FLAC__crc8(raw_header, raw_header_len) != crc8) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* calculate the sample number from the frame number if needed */ + decoder->private_->next_fixed_block_size = 0; + if(decoder->private_->frame.header.number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { + x = decoder->private_->frame.header.number.frame_number; + decoder->private_->frame.header.number_type = FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER; + if(decoder->private_->fixed_block_size) + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->fixed_block_size * (FLAC__uint64)x; + else if(decoder->private_->has_stream_info) { + if(decoder->private_->stream_info.data.stream_info.min_blocksize == decoder->private_->stream_info.data.stream_info.max_blocksize) { + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->stream_info.data.stream_info.min_blocksize * (FLAC__uint64)x; + decoder->private_->next_fixed_block_size = decoder->private_->stream_info.data.stream_info.max_blocksize; + } + else + is_unparseable = true; + } + else if(x == 0) { + decoder->private_->frame.header.number.sample_number = 0; + decoder->private_->next_fixed_block_size = decoder->private_->frame.header.blocksize; + } + else { + /* can only get here if the stream has invalid frame numbering and no STREAMINFO, so assume it's not the last (possibly short) frame */ + decoder->private_->frame.header.number.sample_number = (FLAC__uint64)decoder->private_->frame.header.blocksize * (FLAC__uint64)x; + } + } + + if(is_unparseable) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + return true; +} + +FLAC__bool read_subframe_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) +{ + FLAC__uint32 x; + FLAC__bool wasted_bits; + uint32_t i; + + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) /* MAGIC NUMBER */ + return false; /* read_callback_ sets the state for us */ + + wasted_bits = (x & 1); + x &= 0xfe; + + if(wasted_bits) { + uint32_t u; + if(!FLAC__bitreader_read_unary_unsigned(decoder->private_->input, &u)) + return false; /* read_callback_ sets the state for us */ + decoder->private_->frame.subframes[channel].wasted_bits = u+1; + if (decoder->private_->frame.subframes[channel].wasted_bits >= bps) + return false; + bps -= decoder->private_->frame.subframes[channel].wasted_bits; + } + else + decoder->private_->frame.subframes[channel].wasted_bits = 0; + + /* + * Lots of magic numbers here + */ + if(x & 0x80) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x == 0) { + if(!read_subframe_constant_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x == 2) { + if(!read_subframe_verbatim_(decoder, channel, bps, do_full_decode)) + return false; + } + else if(x < 16) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else if(x <= 24) { + if(!read_subframe_fixed_(decoder, channel, bps, (x>>1)&7, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + else if(x < 64) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + else { + if(!read_subframe_lpc_(decoder, channel, bps, ((x>>1)&31)+1, do_full_decode)) + return false; + if(decoder->protected_->state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) /* means bad sync or got corruption */ + return true; + } + + if(wasted_bits && do_full_decode) { + x = decoder->private_->frame.subframes[channel].wasted_bits; + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + uint32_t val = decoder->private_->output[channel][i]; + decoder->private_->output[channel][i] = (val << x); + } + } + + return true; +} + +FLAC__bool read_subframe_constant_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Constant *subframe = &decoder->private_->frame.subframes[channel].data.constant; + FLAC__int32 x; + uint32_t i; + FLAC__int32 *output = decoder->private_->output[channel]; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_CONSTANT; + + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + + subframe->value = x; + + /* decode the subframe */ + if(do_full_decode) { + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) + output[i] = x; + } + + return true; +} + +FLAC__bool read_subframe_fixed_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Fixed *subframe = &decoder->private_->frame.subframes[channel].data.fixed; + FLAC__int32 i32; + FLAC__uint32 u32; + uint32_t u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_FIXED; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + if(decoder->private_->frame.header.blocksize >> u32 < order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + break; + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + FLAC__fixed_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, order, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_lpc_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, const uint32_t order, FLAC__bool do_full_decode) +{ + FLAC__Subframe_LPC *subframe = &decoder->private_->frame.subframes[channel].data.lpc; + FLAC__int32 i32; + FLAC__uint32 u32; + uint32_t u; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_LPC; + + subframe->residual = decoder->private_->residual[channel]; + subframe->order = order; + + /* read warm-up samples */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, bps)) + return false; /* read_callback_ sets the state for us */ + subframe->warmup[u] = i32; + } + + /* read qlp coeff precision */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) + return false; /* read_callback_ sets the state for us */ + if(u32 == (1u << FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN) - 1) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->qlp_coeff_precision = u32+1; + + /* read qlp shift */ + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) + return false; /* read_callback_ sets the state for us */ + if(i32 < 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->quantization_level = i32; + + /* read quantized lp coefficiencts */ + for(u = 0; u < order; u++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i32, subframe->qlp_coeff_precision)) + return false; /* read_callback_ sets the state for us */ + subframe->qlp_coeff[u] = i32; + } + + /* read entropy coding method info */ + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) + return false; /* read_callback_ sets the state for us */ + subframe->entropy_coding_method.type = (FLAC__EntropyCodingMethodType)u32; + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &u32, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) + return false; /* read_callback_ sets the state for us */ + if(decoder->private_->frame.header.blocksize >> u32 < order) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + subframe->entropy_coding_method.data.partitioned_rice.order = u32; + subframe->entropy_coding_method.data.partitioned_rice.contents = &decoder->private_->partitioned_rice_contents[channel]; + break; + default: + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + return true; + } + + /* read residual */ + switch(subframe->entropy_coding_method.type) { + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: + case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: + if(!read_residual_partitioned_rice_(decoder, order, subframe->entropy_coding_method.data.partitioned_rice.order, &decoder->private_->partitioned_rice_contents[channel], decoder->private_->residual[channel], /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2)) + return false; + break; + default: + break; + } + + /* decode the subframe */ + if(do_full_decode) { + memcpy(decoder->private_->output[channel], subframe->warmup, sizeof(FLAC__int32) * order); + if(bps + subframe->qlp_coeff_precision + FLAC__bitmath_ilog2(order) <= 32) + if(bps <= 16 && subframe->qlp_coeff_precision <= 16) + decoder->private_->local_lpc_restore_signal_16bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + else + decoder->private_->local_lpc_restore_signal_64bit(decoder->private_->residual[channel], decoder->private_->frame.header.blocksize-order, subframe->qlp_coeff, order, subframe->quantization_level, decoder->private_->output[channel]+order); + } + + return true; +} + +FLAC__bool read_subframe_verbatim_(FLAC__StreamDecoder *decoder, uint32_t channel, uint32_t bps, FLAC__bool do_full_decode) +{ + FLAC__Subframe_Verbatim *subframe = &decoder->private_->frame.subframes[channel].data.verbatim; + FLAC__int32 x, *residual = decoder->private_->residual[channel]; + uint32_t i; + + decoder->private_->frame.subframes[channel].type = FLAC__SUBFRAME_TYPE_VERBATIM; + + subframe->data = residual; + + for(i = 0; i < decoder->private_->frame.header.blocksize; i++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &x, bps)) + return false; /* read_callback_ sets the state for us */ + residual[i] = x; + } + + /* decode the subframe */ + if(do_full_decode) + memcpy(decoder->private_->output[channel], subframe->data, sizeof(FLAC__int32) * decoder->private_->frame.header.blocksize); + + return true; +} + +FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, uint32_t predictor_order, uint32_t partition_order, FLAC__EntropyCodingMethod_PartitionedRiceContents *partitioned_rice_contents, FLAC__int32 *residual, FLAC__bool is_extended) +{ + FLAC__uint32 rice_parameter; + int i; + uint32_t partition, sample, u; + const uint32_t partitions = 1u << partition_order; + const uint32_t partition_samples = partition_order > 0? decoder->private_->frame.header.blocksize >> partition_order : decoder->private_->frame.header.blocksize - predictor_order; + const uint32_t plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; + const uint32_t pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; + + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order))) { + decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; + return false; + } + + sample = 0; + for(partition = 0; partition < partitions; partition++) { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, plen)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->parameters[partition] = rice_parameter; + if(rice_parameter < pesc) { + partitioned_rice_contents->raw_bits[partition] = 0; + u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; + if(!FLAC__bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + sample += u; + } + else { + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &rice_parameter, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) + return false; /* read_callback_ sets the state for us */ + partitioned_rice_contents->raw_bits[partition] = rice_parameter; + for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) + return false; /* read_callback_ sets the state for us */ + residual[sample] = i; + } + } + } + + return true; +} + +FLAC__bool read_zero_padding_(FLAC__StreamDecoder *decoder) +{ + if(!FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)) { + FLAC__uint32 zero = 0; + if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &zero, FLAC__bitreader_bits_left_for_byte_alignment(decoder->private_->input))) + return false; /* read_callback_ sets the state for us */ + if(zero != 0) { + send_error_to_client_(decoder, FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC); + decoder->protected_->state = FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; + } + } + return true; +} + +FLAC__bool read_callback_(FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FLAC__StreamDecoder *decoder = (FLAC__StreamDecoder *)client_data; + + if( + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) { + *bytes = 0; + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else if(*bytes > 0) { + /* While seeking, it is possible for our seek to land in the + * middle of audio data that looks exactly like a frame header + * from a future version of an encoder. When that happens, our + * error callback will get an + * FLAC__STREAM_DECODER_UNPARSEABLE_STREAM and increment its + * unparseable_frame_count. But there is a remote possibility + * that it is properly synced at such a "future-codec frame", + * so to make sure, we wait to see many "unparseable" errors in + * a row before bailing out. + */ + if(decoder->private_->is_seeking && decoder->private_->unparseable_frame_count > 20) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else { + const FLAC__StreamDecoderReadStatus status = + decoder->private_->read_callback(decoder, buffer, bytes, decoder->private_->client_data) + ; + if(status == FLAC__STREAM_DECODER_READ_STATUS_ABORT) { + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + else if(*bytes == 0) { + if( + status == FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM || + ( + decoder->private_->eof_callback && decoder->private_->eof_callback(decoder, decoder->private_->client_data) + ) + ) { + decoder->protected_->state = FLAC__STREAM_DECODER_END_OF_STREAM; + return false; + } + else + return true; + } + else + return true; + } + } + else { + /* abort to avoid a deadlock */ + decoder->protected_->state = FLAC__STREAM_DECODER_ABORTED; + return false; + } + /* [1] @@@ HACK NOTE: The end-of-stream checking has to be hacked around + * for Ogg FLAC. This is because the ogg decoder aspect can lose sync + * and at the same time hit the end of the stream (for example, seeking + * to a point that is after the beginning of the last Ogg page). There + * is no way to report an Ogg sync loss through the callbacks (see note + * in read_callback_ogg_aspect_()) so it returns CONTINUE with *bytes==0. + * So to keep the decoder from stopping at this point we gate the call + * to the eof_callback and let the Ogg decoder aspect set the + * end-of-stream state when it is needed. + */ +} + +FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]) +{ + if(decoder->private_->is_seeking) { + FLAC__uint64 this_frame_sample = frame->header.number.sample_number; + FLAC__uint64 next_frame_sample = this_frame_sample + (FLAC__uint64)frame->header.blocksize; + FLAC__uint64 target_sample = decoder->private_->target_sample; + + decoder->private_->last_frame = *frame; /* save the frame */ + if(this_frame_sample <= target_sample && target_sample < next_frame_sample) { /* we hit our target frame */ + uint32_t delta = (uint32_t)(target_sample - this_frame_sample); + /* kick out of seek mode */ + decoder->private_->is_seeking = false; + /* shift out the samples before target_sample */ + if(delta > 0) { + uint32_t channel; + const FLAC__int32 *newbuffer[FLAC__MAX_CHANNELS]; + for(channel = 0; channel < frame->header.channels; channel++) + newbuffer[channel] = buffer[channel] + delta; + decoder->private_->last_frame.header.blocksize -= delta; + decoder->private_->last_frame.header.number.sample_number += (FLAC__uint64)delta; + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, &decoder->private_->last_frame, newbuffer, decoder->private_->client_data); + } + else { + /* write the relevant samples */ + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } + } + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); + } +} + +void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) +{ + if(!decoder->private_->is_seeking) + decoder->private_->error_callback(decoder, status, decoder->private_->client_data); + else if(status == FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM) + decoder->private_->unparseable_frame_count++; +} + +FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample) +{ + FLAC__uint64 first_frame_offset = decoder->private_->first_frame_offset, lower_bound, upper_bound, lower_bound_sample, upper_bound_sample, this_frame_sample; + FLAC__int64 pos = -1; + int i; + uint32_t approx_bytes_per_frame; + FLAC__bool first_seek = true; + const FLAC__uint64 total_samples = FLAC__stream_decoder_get_total_samples(decoder); + const uint32_t min_blocksize = decoder->private_->stream_info.data.stream_info.min_blocksize; + const uint32_t max_blocksize = decoder->private_->stream_info.data.stream_info.max_blocksize; + const uint32_t max_framesize = decoder->private_->stream_info.data.stream_info.max_framesize; + const uint32_t min_framesize = decoder->private_->stream_info.data.stream_info.min_framesize; + /* take these from the current frame in case they've changed mid-stream */ + uint32_t channels = FLAC__stream_decoder_get_channels(decoder); + uint32_t bps = FLAC__stream_decoder_get_bits_per_sample(decoder); + const FLAC__StreamMetadata_SeekTable *seek_table = decoder->private_->has_seek_table? &decoder->private_->seek_table.data.seek_table : 0; + + /* use values from stream info if we didn't decode a frame */ + if(channels == 0) + channels = decoder->private_->stream_info.data.stream_info.channels; + if(bps == 0) + bps = decoder->private_->stream_info.data.stream_info.bits_per_sample; + + /* we are just guessing here */ + if(max_framesize > 0) + approx_bytes_per_frame = (max_framesize + min_framesize) / 2 + 1; + /* + * Check if it's a known fixed-blocksize stream. Note that though + * the spec doesn't allow zeroes in the STREAMINFO block, we may + * never get a STREAMINFO block when decoding so the value of + * min_blocksize might be zero. + */ + else if(min_blocksize == max_blocksize && min_blocksize > 0) { + /* note there are no () around 'bps/8' to keep precision up since it's an integer calculation */ + approx_bytes_per_frame = min_blocksize * channels * bps/8 + 64; + } + else + approx_bytes_per_frame = 4096 * channels * bps/8 + 64; + + /* + * First, we set an upper and lower bound on where in the + * stream we will search. For now we assume the worst case + * scenario, which is our best guess at the beginning of + * the first frame and end of the stream. + */ + lower_bound = first_frame_offset; + lower_bound_sample = 0; + upper_bound = stream_length; + upper_bound_sample = total_samples > 0 ? total_samples : target_sample /*estimate it*/; + + /* + * Now we refine the bounds if we have a seektable with + * suitable points. Note that according to the spec they + * must be ordered by ascending sample number. + * + * Note: to protect against invalid seek tables we will ignore points + * that have frame_samples==0 or sample_number>=total_samples + */ + if(seek_table) { + FLAC__uint64 new_lower_bound = lower_bound; + FLAC__uint64 new_upper_bound = upper_bound; + FLAC__uint64 new_lower_bound_sample = lower_bound_sample; + FLAC__uint64 new_upper_bound_sample = upper_bound_sample; + + /* find the closest seek point <= target_sample, if it exists */ + for(i = (int)seek_table->num_points - 1; i >= 0; i--) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number <= target_sample + ) + break; + } + if(i >= 0) { /* i.e. we found a suitable seek point... */ + new_lower_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_lower_bound_sample = seek_table->points[i].sample_number; + } + + /* find the closest seek point > target_sample, if it exists */ + for(i = 0; i < (int)seek_table->num_points; i++) { + if( + seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && + seek_table->points[i].frame_samples > 0 && /* defense against bad seekpoints */ + (total_samples <= 0 || seek_table->points[i].sample_number < total_samples) && /* defense against bad seekpoints */ + seek_table->points[i].sample_number > target_sample + ) + break; + } + if(i < (int)seek_table->num_points) { /* i.e. we found a suitable seek point... */ + new_upper_bound = first_frame_offset + seek_table->points[i].stream_offset; + new_upper_bound_sample = seek_table->points[i].sample_number; + } + /* final protection against unsorted seek tables; keep original values if bogus */ + if(new_upper_bound >= new_lower_bound) { + lower_bound = new_lower_bound; + upper_bound = new_upper_bound; + lower_bound_sample = new_lower_bound_sample; + upper_bound_sample = new_upper_bound_sample; + } + } + + /* there are 2 insidious ways that the following equality occurs, which + * we need to fix: + * 1) total_samples is 0 (unknown) and target_sample is 0 + * 2) total_samples is 0 (unknown) and target_sample happens to be + * exactly equal to the last seek point in the seek table; this + * means there is no seek point above it, and upper_bound_samples + * remains equal to the estimate (of target_samples) we made above + * in either case it does not hurt to move upper_bound_sample up by 1 + */ + if(upper_bound_sample == lower_bound_sample) + upper_bound_sample++; + + decoder->private_->target_sample = target_sample; + while(1) { + /* check if the bounds are still ok */ + if (lower_bound_sample >= upper_bound_sample || lower_bound > upper_bound) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + pos = (FLAC__int64)lower_bound + (FLAC__int64)((double)(target_sample - lower_bound_sample) / (double)(upper_bound_sample - lower_bound_sample) * (double)(upper_bound - lower_bound)) - approx_bytes_per_frame; + if(pos >= (FLAC__int64)upper_bound) + pos = (FLAC__int64)upper_bound - 1; + if(pos < (FLAC__int64)lower_bound) + pos = (FLAC__int64)lower_bound; + if(decoder->private_->seek_callback(decoder, (FLAC__uint64)pos, decoder->private_->client_data) != FLAC__STREAM_DECODER_SEEK_STATUS_OK) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + if(!FLAC__stream_decoder_flush(decoder)) { + /* above call sets the state for us */ + return false; + } + /* Now we need to get a frame. First we need to reset our + * unparseable_frame_count; if we get too many unparseable + * frames in a row, the read callback will return + * FLAC__STREAM_DECODER_READ_STATUS_ABORT, causing + * FLAC__stream_decoder_process_single() to return false. + */ + decoder->private_->unparseable_frame_count = 0; + if(!FLAC__stream_decoder_process_single(decoder) || + decoder->protected_->state == FLAC__STREAM_DECODER_ABORTED) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our write callback will change the state when it gets to the target frame */ + /* actually, we could have got_a_frame if our decoder is at FLAC__STREAM_DECODER_END_OF_STREAM so we need to check for that also */ + if(!decoder->private_->is_seeking) + break; + + this_frame_sample = decoder->private_->last_frame.header.number.sample_number; + + if (0 == decoder->private_->samples_decoded || (this_frame_sample + decoder->private_->last_frame.header.blocksize >= upper_bound_sample && !first_seek)) { + if (pos == (FLAC__int64)lower_bound) { + /* can't move back any more than the first frame, something is fatally wrong */ + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + /* our last move backwards wasn't big enough, try again */ + approx_bytes_per_frame = approx_bytes_per_frame? approx_bytes_per_frame * 2 : 16; + continue; + } + /* allow one seek over upper bound, so we can get a correct upper_bound_sample for streams with unknown total_samples */ + first_seek = false; + + /* make sure we are not seeking in corrupted stream */ + if (this_frame_sample < lower_bound_sample) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + + /* we need to narrow the search */ + if(target_sample < this_frame_sample) { + upper_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; +/*@@@@@@ what will decode position be if at end of stream? */ + if(!FLAC__stream_decoder_get_decode_position(decoder, &upper_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (uint32_t)(2 * (upper_bound - pos) / 3 + 16); + } + else { /* target_sample >= this_frame_sample + this frame's blocksize */ + lower_bound_sample = this_frame_sample + decoder->private_->last_frame.header.blocksize; + if(!FLAC__stream_decoder_get_decode_position(decoder, &lower_bound)) { + decoder->protected_->state = FLAC__STREAM_DECODER_SEEK_ERROR; + return false; + } + approx_bytes_per_frame = (uint32_t)(2 * (lower_bound - pos) / 3 + 16); + } + } + + return true; +} + +FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + (void)client_data; + + if(*bytes > 0) { + *bytes = fread(buffer, sizeof(FLAC__byte), *bytes, decoder->private_->file); + if(ferror(decoder->private_->file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if(*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ +} + +FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; + else if(fseeko(decoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; +} + +FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + FLAC__off_t pos; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED; + else if((pos = ftello(decoder->private_->file)) < 0) + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + else { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } +} + +FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + struct flac_stat_s filestats; + (void)client_data; + + if(decoder->private_->file == stdin) + return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; + else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0) + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + else { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } +} + +FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data) +{ + (void)client_data; + + return feof(decoder->private_->file)? true : false; +} + +void *get_client_data_from_decoder(FLAC__StreamDecoder *decoder) +{ + return decoder->private_->client_data; +} diff --git a/src/libflac/window.c b/src/libflac/window.c @@ -0,0 +1,272 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <math.h> +#include "share/compat.h" +#include "FLAC/format.h" +#include "private/window.h" + +void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + if (L & 1) { + for (n = 0; n <= N/2; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } + else { + for (n = 0; n <= L/2-1; n++) + window[n] = 2.0f * n / (float)N; + for (; n <= N; n++) + window[n] = 2.0f - 2.0f * n / (float)N; + } +} + +void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N-0.5f) - 0.38f * cos(2.0f * M_PI * ((float)n/(float)N))); +} + +void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); +} + +/* 4-term -92dB side-lobe */ +void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n <= N; n++) + window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + double k = ((double)n - N2) / N2; + k = 1.0f - k * k; + window[n] = (FLAC__real)(k * k); + } +} + +void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.21557895f - 0.41663158f * cos(2.0f * M_PI * n / N) + 0.277263158f * cos(4.0f * M_PI * n / N) - 0.083578947f * cos(6.0f * M_PI * n / N) + 0.006947368f * cos(8.0f * M_PI * n / N)); +} + +void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / (stddev * N2); + window[n] = (FLAC__real)exp(-0.5f * k * k); + } +} + +void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); +} + +void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); +} + +void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); +} + +void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + for (n = 0; n < L; n++) + window[n] = 1.0f; +} + +void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) +{ + FLAC__int32 n; + + if (L & 1) { + for (n = 1; n <= (L+1)/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } + else { + for (n = 1; n <= L/2; n++) + window[n-1] = 2.0f * n / ((float)L + 1.0f); + for (; n <= L; n++) + window[n-1] = (float)(2 * (L - n + 1)) / ((float)L + 1.0f); + } +} + +void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) +{ + if (p <= 0.0) + FLAC__window_rectangle(window, L); + else if (p >= 1.0) + FLAC__window_hann(window, L); + else { + const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; + FLAC__int32 n; + /* start with rectangle... */ + FLAC__window_rectangle(window, L); + /* ...replace ends with hann */ + if (Np > 0) { + for (n = 0; n <= Np; n++) { + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); + window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); + } + } + } +} + +void FLAC__window_partial_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + const FLAC__int32 N = end_n - start_n; + FLAC__int32 Np, n, i; + + if (p <= 0.0f) + FLAC__window_partial_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_partial_tukey(window, L, 0.95f, start, end); + else { + + Np = (FLAC__int32)(p / 2.0f * N); + + for (n = 0; n < start_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < (start_n+Np) && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < (end_n-Np) && n < L; n++) + window[n] = 1.0f; + for (i = Np; n < end_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Np)); + for (; n < L; n++) + window[n] = 0.0f; + } +} + +void FLAC__window_punchout_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p, const FLAC__real start, const FLAC__real end) +{ + const FLAC__int32 start_n = (FLAC__int32)(start * L); + const FLAC__int32 end_n = (FLAC__int32)(end * L); + FLAC__int32 Ns, Ne, n, i; + + if (p <= 0.0f) + FLAC__window_punchout_tukey(window, L, 0.05f, start, end); + else if (p >= 1.0f) + FLAC__window_punchout_tukey(window, L, 0.95f, start, end); + else { + + Ns = (FLAC__int32)(p / 2.0f * start_n); + Ne = (FLAC__int32)(p / 2.0f * (L - end_n)); + + for (n = 0, i = 1; n < Ns && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < start_n-Ns && n < L; n++) + window[n] = 1.0f; + for (i = Ns; n < start_n && n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ns)); + for (; n < end_n && n < L; n++) + window[n] = 0.0f; + for (i = 1; n < end_n+Ne && n < L; n++, i++) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + for (; n < L - (Ne) && n < L; n++) + window[n] = 1.0f; + for (i = Ne; n < L; n++, i--) + window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * i / Ne)); + } +} + +void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) +{ + const FLAC__int32 N = L - 1; + const double N2 = (double)N / 2.; + FLAC__int32 n; + + for (n = 0; n <= N; n++) { + const double k = ((double)n - N2) / N2; + window[n] = (FLAC__real)(1.0f - k * k); + } +} diff --git a/src/libflac/windows_unicode_filenames.c b/src/libflac/windows_unicode_filenames.c @@ -0,0 +1,194 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2013-2016 Xiph.Org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#define WIN32_MEAN_AND_LEAN +#include <io.h> +#include <windows.h> +#include "share/windows_unicode_filenames.h" + +/* convert UTF-8 back to WCHAR. Caller is responsible for freeing memory */ +static wchar_t *wchar_from_utf8(const char *str) +{ + wchar_t *widestr; + int len; + + if (!str) + return NULL; + if ((len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0)) == 0) + return NULL; + if ((widestr = (wchar_t *)malloc(len*sizeof(wchar_t))) == NULL) + return NULL; + if (MultiByteToWideChar(CP_UTF8, 0, str, -1, widestr, len) == 0) { + free(widestr); + widestr = NULL; + } + + return widestr; +} + + +static FLAC__bool utf8_filenames = false; + + +void flac_internal_set_utf8_filenames(FLAC__bool flag) +{ + utf8_filenames = flag ? true : false; +} + +FLAC__bool flac_internal_get_utf8_filenames(void) +{ + return utf8_filenames; +} + +/* file functions */ + +FILE* flac_internal_fopen_utf8(const char *filename, const char *mode) +{ + if (!utf8_filenames) { + return fopen(filename, mode); + } else { + wchar_t *wname = NULL; + wchar_t *wmode = NULL; + FILE *f = NULL; + + do { + wname = wchar_from_utf8(filename); + if (!wname) break; + wmode = wchar_from_utf8(mode); + if (!wmode) break; + f = _wfopen(wname, wmode); + } while(0); + + free(wname); + free(wmode); + + return f; + } +} + +int flac_internal_stat64_utf8(const char *path, struct __stat64 *buffer) +{ + if (!utf8_filenames) { + return _stat64(path, buffer); + } else { + wchar_t *wpath; + int ret; + + wpath = wchar_from_utf8(path); + if (!wpath) return -1; + ret = _wstat64(wpath, buffer); + free(wpath); + + return ret; + } +} + +int flac_internal_chmod_utf8(const char *filename, int pmode) +{ + if (!utf8_filenames) { + return _chmod(filename, pmode); + } else { + wchar_t *wname; + int ret; + + wname = wchar_from_utf8(filename); + if (!wname) return -1; + ret = _wchmod(wname, pmode); + free(wname); + + return ret; + } +} + +int flac_internal_utime_utf8(const char *filename, struct utimbuf *times) +{ + if (!utf8_filenames) { + return utime(filename, times); + } else { + wchar_t *wname; + struct __utimbuf64 ut; + int ret; + + wname = wchar_from_utf8(filename); + if (!wname) return -1; + ut.actime = times->actime; + ut.modtime = times->modtime; + ret = _wutime64(wname, &ut); + free(wname); + + return ret; + } +} + +int flac_internal_unlink_utf8(const char *filename) +{ + if (!utf8_filenames) { + return _unlink(filename); + } else { + wchar_t *wname; + int ret; + + wname = wchar_from_utf8(filename); + if (!wname) return -1; + ret = _wunlink(wname); + free(wname); + + return ret; + } +} + +int flac_internal_rename_utf8(const char *oldname, const char *newname) +{ + if (!utf8_filenames) { + return rename(oldname, newname); + } else { + wchar_t *wold = NULL; + wchar_t *wnew = NULL; + int ret = -1; + + do { + wold = wchar_from_utf8(oldname); + if (!wold) break; + wnew = wchar_from_utf8(newname); + if (!wnew) break; + ret = _wrename(wold, wnew); + } while(0); + + free(wold); + free(wnew); + + return ret; + } +} + +#endif diff --git a/src/mixer/ft2_center_mix.c b/src/mixer/ft2_center_mix.c @@ -1,6 +1,7 @@ #include <stdint.h> #include <stdbool.h> #include "ft2_mix_macros.h" +#include "../ft2_cpu.h" /* Check out ft2_mix.c for comments on how this works. ** These are duplicates for center-mixing (slightly faster when it can be used). @@ -13,10 +14,10 @@ void centerMix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -55,10 +56,10 @@ void centerMix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -97,10 +98,10 @@ void centerMix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -141,10 +142,10 @@ void centerMix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -184,10 +185,10 @@ void centerMix8bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -251,10 +252,10 @@ void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl { const int8_t *base, *revBase, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -320,10 +321,10 @@ void centerMix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -362,10 +363,10 @@ void centerMix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples void centerMix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -404,10 +405,10 @@ void centerMix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -448,11 +449,11 @@ void centerMix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl void centerMix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -498,11 +499,11 @@ void centerMix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -548,11 +549,11 @@ void centerMix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -600,11 +601,11 @@ void centerMix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples void centerMix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -651,11 +652,11 @@ void centerMix8bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl { const int8_t *base, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -731,11 +732,11 @@ void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numS { const int8_t *base, *revBase, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -812,11 +813,11 @@ void centerMix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numS void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -862,11 +863,11 @@ void centerMix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -912,11 +913,11 @@ void centerMix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -968,10 +969,10 @@ void centerMix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numS void centerMix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1010,10 +1011,10 @@ void centerMix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1052,10 +1053,10 @@ void centerMix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -1096,10 +1097,10 @@ void centerMix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1139,10 +1140,10 @@ void centerMix16bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1206,10 +1207,10 @@ void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp { const int16_t *base, *revBase, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -1274,10 +1275,10 @@ void centerMix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1316,10 +1317,10 @@ void centerMix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSample void centerMix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO GET_MIXER_VARS @@ -1358,10 +1359,10 @@ void centerMix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO GET_MIXER_VARS @@ -1403,11 +1404,11 @@ void centerMix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp void centerMix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1453,11 +1454,11 @@ void centerMix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1503,11 +1504,11 @@ void centerMix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) void centerMix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1555,11 +1556,11 @@ void centerMix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSample void centerMix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1606,11 +1607,11 @@ void centerMix16bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp { const int16_t *base, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1686,11 +1687,11 @@ void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t num { const int16_t *base, *revBase, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1767,11 +1768,11 @@ void centerMix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t num void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1817,11 +1818,11 @@ void centerMix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSa void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP @@ -1867,11 +1868,11 @@ void centerMix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp void centerMix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolL; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeL; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_MONO_RAMP GET_MIXER_VARS_MONO_RAMP diff --git a/src/mixer/ft2_mix.c b/src/mixer/ft2_mix.c @@ -3,16 +3,17 @@ #include "ft2_mix.h" #include "ft2_mix_macros.h" #include "ft2_center_mix.h" +#include "../ft2_cpu.h" /* -** ------------ double-precision floating-point audio channel mixer ------------ -** (Note: Mixing macros can be found in ft2_mix_macros.h) +** ------------ 32-bit floating-point audio channel mixer ------------ +** (Note: Mixing macros can be found in ft2_mix_macros.h) ** ** Specifications: -** - Either no interpolation, 2-tap linear interpolation (FT2) or 8-tap windowed-sinc interpolation -** - Linear volume ramping, matching FT2 (can be turned off) -** - 32.32 fixed-point logic for resampling delta -** - 64-bit doube-precision logic for mixing and interpolation +** - No interpolation, 2-tap linear interpolation (FT2) or 8-tap windowed-sinc interpolation +** - FT2-styled linear volume ramping (can be turned off) +** - 32.32 (16.16 if 32-bit CPU) fixed-point precision for resampling delta/position +** - 32-bit floating-point precision for mixing and interpolation ** ** This file has separate routines for EVERY possible sampling variation: ** Interpolation none/sinc/linear, volumeramp on/off, 8-bit, 16-bit, no loop, loop, bidi. @@ -34,10 +35,10 @@ static void mix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -76,10 +77,10 @@ static void mix8bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -118,10 +119,10 @@ static void mix8bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -161,10 +162,10 @@ static void mix8bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -204,10 +205,10 @@ static void mix8bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -271,10 +272,10 @@ static void mix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp { const int8_t *base, *revBase, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -339,10 +340,10 @@ static void mix8bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp static void mix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -381,10 +382,10 @@ static void mix8bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSample static void mix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -423,10 +424,10 @@ static void mix8bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -467,11 +468,11 @@ static void mix8bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -517,11 +518,11 @@ static void mix8bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -567,11 +568,11 @@ static void mix8bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -619,11 +620,11 @@ static void mix8bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSample static void mix8bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -670,11 +671,11 @@ static void mix8bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp { const int8_t *base, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -750,11 +751,11 @@ static void mix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t num { const int8_t *base, *revBase, *smpPtr; int8_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -831,11 +832,11 @@ static void mix8bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t num static void mix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -881,11 +882,11 @@ static void mix8bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSa static void mix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -931,11 +932,11 @@ static void mix8bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamp static void mix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int8_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -987,10 +988,10 @@ static void mix8bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t num static void mix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1029,10 +1030,10 @@ static void mix16bNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1071,10 +1072,10 @@ static void mix16bLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -1115,10 +1116,10 @@ static void mix16bBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix16bNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1158,10 +1159,10 @@ static void mix16bLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples { const int16_t *base, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1225,10 +1226,10 @@ static void mix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam { const int16_t *base, *revBase, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -1293,10 +1294,10 @@ static void mix16bBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam static void mix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1335,10 +1336,10 @@ static void mix16bNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSampl static void mix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL GET_MIXER_VARS @@ -1377,10 +1378,10 @@ static void mix16bLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples static void mix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL GET_MIXER_VARS @@ -1421,11 +1422,11 @@ static void mix16bBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam static void mix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1471,11 +1472,11 @@ static void mix16bRampNoLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples static void mix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1521,11 +1522,11 @@ static void mix16bRampLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) static void mix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1573,11 +1574,11 @@ static void mix16bRampBidiLoop(voice_t *v, uint32_t bufferPos, uint32_t numSampl static void mix16bRampNoLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1624,11 +1625,11 @@ static void mix16bRampLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam { const int16_t *base, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1704,11 +1705,11 @@ static void mix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t nu { const int16_t *base, *revBase, *smpPtr; int16_t *smpTapPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1785,11 +1786,11 @@ static void mix16bRampBidiLoopSIntrp(voice_t *v, uint32_t bufferPos, uint32_t nu static void mix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1835,11 +1836,11 @@ static void mix16bRampNoLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numS static void mix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac; + uintCPUWord_t positionFrac; GET_VOL_RAMP GET_MIXER_VARS_RAMP @@ -1885,11 +1886,11 @@ static void mix16bRampLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSam static void mix16bRampBidiLoopLIntrp(voice_t *v, uint32_t bufferPos, uint32_t numSamples) { const int16_t *base, *revBase, *smpPtr; - double dSample, *dMixBufferL, *dMixBufferR; - int32_t pos; - double dVolLDelta, dVolRDelta, dVolL, dVolR; + float fSample, *fMixBufferL, *fMixBufferR; + int32_t position; + float fVolumeLDelta, fVolumeRDelta, fVolumeL, fVolumeR; uint32_t i, samplesToMix, samplesLeft; - uint64_t posFrac, tmpDelta; + uintCPUWord_t positionFrac, tmpDelta; GET_VOL_RAMP GET_MIXER_VARS_RAMP diff --git a/src/mixer/ft2_mix.h b/src/mixer/ft2_mix.h @@ -1,7 +1,16 @@ #pragma once #include <stdint.h> -#include "../ft2_audio.h" +#include "../ft2_cpu.h" + +#if CPU_64BIT +#define MIXER_FRAC_BITS 32 +#else +#define MIXER_FRAC_BITS 16 +#endif + +#define MIXER_FRAC_SCALE ((intCPUWord_t)1 << MIXER_FRAC_BITS) +#define MIXER_FRAC_MASK (MIXER_FRAC_SCALE-1) typedef void (*mixFunc)(void *, uint32_t, uint32_t); diff --git a/src/mixer/ft2_mix_macros.h b/src/mixer/ft2_mix_macros.h @@ -1,6 +1,5 @@ #pragma once -#include <assert.h> #include "../ft2_audio.h" #include "ft2_windowed_sinc.h" @@ -9,160 +8,155 @@ /* ----------------------------------------------------------------------- */ #define GET_VOL \ - const double dVolL = v->dVolL; \ - const double dVolR = v->dVolR; \ + const float fVolumeL = v->fVolumeL; \ + const float fVolumeR = v->fVolumeR; #define GET_VOL_MONO \ - const double dVolL = v->dVolL; \ + const float fVolumeL = v->fVolumeL; #define GET_VOL_RAMP \ - dVolL = v->dVolL; \ - dVolR = v->dVolR; \ + fVolumeL = v->fVolumeL; \ + fVolumeR = v->fVolumeR; #define GET_VOL_MONO_RAMP \ - dVolL = v->dVolL; \ + fVolumeL = v->fVolumeL; #define SET_VOL_BACK \ - v->dVolL = dVolL; \ - v->dVolR = dVolR; \ + v->fVolumeL = fVolumeL; \ + v->fVolumeR = fVolumeR; #define SET_VOL_BACK_MONO \ - v->dVolL = v->dVolR = dVolL; \ + v->fVolumeL = fVolumeL; \ + v->fVolumeR = fVolumeL; #define GET_MIXER_VARS \ - const uint64_t delta = v->delta; \ - dMixBufferL = audio.dMixBufferL + bufferPos; \ - dMixBufferR = audio.dMixBufferR + bufferPos; \ - pos = v->pos; \ - posFrac = v->posFrac; \ + const uintCPUWord_t delta = v->delta; \ + fMixBufferL = audio.fMixBufferL + bufferPos; \ + fMixBufferR = audio.fMixBufferR + bufferPos; \ + position = v->position; \ + positionFrac = v->positionFrac; #define GET_MIXER_VARS_RAMP \ - const uint64_t delta = v->delta; \ - dMixBufferL = audio.dMixBufferL + bufferPos; \ - dMixBufferR = audio.dMixBufferR + bufferPos; \ - dVolLDelta = v->dVolDeltaL; \ - dVolRDelta = v->dVolDeltaR; \ - pos = v->pos; \ - posFrac = v->posFrac; \ + const uintCPUWord_t delta = v->delta; \ + fMixBufferL = audio.fMixBufferL + bufferPos; \ + fMixBufferR = audio.fMixBufferR + bufferPos; \ + fVolumeLDelta = v->fVolumeLDelta; \ + fVolumeRDelta = v->fVolumeRDelta; \ + position = v->position; \ + positionFrac = v->positionFrac; #define GET_MIXER_VARS_MONO_RAMP \ - const uint64_t delta = v->delta; \ - dMixBufferL = audio.dMixBufferL + bufferPos; \ - dMixBufferR = audio.dMixBufferR + bufferPos; \ - dVolLDelta = v->dVolDeltaL; \ - pos = v->pos; \ - posFrac = v->posFrac; \ + const uintCPUWord_t delta = v->delta; \ + fMixBufferL = audio.fMixBufferL + bufferPos; \ + fMixBufferR = audio.fMixBufferR + bufferPos; \ + fVolumeLDelta = v->fVolumeLDelta; \ + position = v->position; \ + positionFrac = v->positionFrac; #define PREPARE_TAP_FIX8 \ const int8_t *loopStartPtr = &v->base8[v->loopStart]; \ - const int8_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; \ + const int8_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; #define PREPARE_TAP_FIX16 \ const int16_t *loopStartPtr = &v->base16[v->loopStart]; \ - const int16_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; \ + const int16_t *leftEdgePtr = loopStartPtr+SINC_LEFT_TAPS; #define SET_BASE8 \ base = v->base8; \ - smpPtr = base + pos; \ + smpPtr = base + position; #define SET_BASE16 \ base = v->base16; \ - smpPtr = base + pos; \ + smpPtr = base + position; #define SET_BASE8_BIDI \ base = v->base8; \ - revBase = v->revBase8; \ + revBase = v->revBase8; #define SET_BASE16_BIDI \ base = v->base16; \ - revBase = v->revBase16; \ + revBase = v->revBase16; #define INC_POS \ - posFrac += delta; \ - smpPtr += posFrac >> MIXER_FRAC_BITS; \ - posFrac &= MIXER_FRAC_MASK; \ + positionFrac += delta; \ + smpPtr += positionFrac >> MIXER_FRAC_BITS; \ + positionFrac &= MIXER_FRAC_MASK; #define INC_POS_BIDI \ - posFrac += deltaLo; \ - smpPtr += posFrac >> MIXER_FRAC_BITS; \ + positionFrac += deltaLo; \ + smpPtr += positionFrac >> MIXER_FRAC_BITS; \ smpPtr += deltaHi; \ - posFrac &= MIXER_FRAC_MASK; \ + positionFrac &= MIXER_FRAC_MASK; #define SET_BACK_MIXER_POS \ - v->posFrac = posFrac; \ - v->pos = pos; \ + v->positionFrac = positionFrac; \ + v->position = position; /* ----------------------------------------------------------------------- */ /* SAMPLE RENDERING MACROS */ /* ----------------------------------------------------------------------- */ #define VOLUME_RAMPING \ - dVolL += dVolLDelta; \ - dVolR += dVolRDelta; \ + fVolumeL += fVolumeLDelta; \ + fVolumeR += fVolumeRDelta; #define VOLUME_RAMPING_MONO \ - dVolL += dVolLDelta; \ + fVolumeL += fVolumeLDelta; #define RENDER_8BIT_SMP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - dSample = *smpPtr * (1.0 / 128.0); \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + fSample = *smpPtr * (1.0f / 128.0f); \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_8BIT_SMP_MONO \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - dSample = (*smpPtr * (1.0 / 128.0)) * dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + fSample = (*smpPtr * (1.0f / 128.0f)) * fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; #define RENDER_16BIT_SMP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - dSample = *smpPtr * (1.0 / 32768.0); \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + fSample = *smpPtr * (1.0f / 32768.0f); \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_16BIT_SMP_MONO \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - dSample = (*smpPtr * (1.0 / 32768.0)) * dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + fSample = (*smpPtr * (1.0f / 32768.0f)) * fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; // 2-tap linear interpolation (like FT2) +/* 8bitbubsy: It may look like we are potentially going out of bounds while looking up the sample points, +** but the sample data has a fixed sample after the end (sampleEnd/loopEnd). +*/ + #define LINEAR_INTERPOLATION(s, f, scale) \ { \ - /* uint32_t -> int32_t for less SIMD overhead when doing int->double conversion */ \ - const int32_t frac = (uint32_t)(f) >> 1; /* (2^32)-1 -> (2^31)-1 */ \ - \ - const double dFrac = (double)(frac * (1.0 / (INT32_MAX+1.0))); /* 0.0 .. 0.999999999 */ \ - dSample = ((s[0] + (s[1]-s[0]) * dFrac)) * (1.0 / scale); \ -} \ + const int32_t frac = (uint32_t)(f) >> 1; /* uint32 -> int32 range, faster int->float conv. (x86/x86_64) */ \ + const float fFrac = frac * (1.0f / (MIXER_FRAC_SCALE/2)); /* 0.0f .. 0.9999999f */ \ + fSample = ((s[0] + (s[1]-s[0]) * fFrac)) * (1.0f / scale); \ +} #define RENDER_8BIT_SMP_LINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - LINEAR_INTERPOLATION(smpPtr, posFrac, 128) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + LINEAR_INTERPOLATION(smpPtr, positionFrac, 128) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_8BIT_SMP_MONO_LINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - LINEAR_INTERPOLATION(smpPtr, posFrac, 128) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + LINEAR_INTERPOLATION(smpPtr, positionFrac, 128) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; #define RENDER_16BIT_SMP_LINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - LINEAR_INTERPOLATION(smpPtr, posFrac, 32768) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + LINEAR_INTERPOLATION(smpPtr, positionFrac, 32768) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_16BIT_SMP_MONO_LINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - LINEAR_INTERPOLATION(smpPtr, posFrac, 32768) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + LINEAR_INTERPOLATION(smpPtr, positionFrac, 32768) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; // 8-tap windowed-sinc interpolation (better quality, through LUT: mixer/ft2_windowed_sinc.c) @@ -176,113 +170,114 @@ #define WINDOWED_SINC_INTERPOLATION(s, f, scale) \ { \ - const double *t = v->dSincLUT + (((uint32_t)f >> SINC_FSHIFT) & SINC_FMASK); \ - dSample = ((s[-3] * t[0]) + \ + const float *t = v->fSincLUT + (((uint32_t)(f) >> SINC_FSHIFT) & SINC_FMASK); \ + fSample = ((s[-3] * t[0]) + \ (s[-2] * t[1]) + \ (s[-1] * t[2]) + \ ( s[0] * t[3]) + \ ( s[1] * t[4]) + \ ( s[2] * t[5]) + \ ( s[3] * t[6]) + \ - ( s[4] * t[7])) * (1.0 / scale); \ -} \ + ( s[4] * t[7])) * (1.0f / scale); \ +} #define RENDER_8BIT_SMP_SINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 128) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 128) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_8BIT_SMP_MONO_SINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 128) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 128) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; #define RENDER_16BIT_SMP_SINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 32768) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 32768) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_16BIT_SMP_MONO_SINTRP \ - assert(smpPtr >= base && smpPtr < base+v->end); \ - WINDOWED_SINC_INTERPOLATION(smpPtr, posFrac, 32768) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + WINDOWED_SINC_INTERPOLATION(smpPtr, positionFrac, 32768) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; /* Special left-edge case mixers to get proper tap data after one loop cycle. ** These are only used with sinc interpolation on looped samples. */ #define RENDER_8BIT_SMP_SINTRP_TAP_FIX \ - assert(smpPtr >= base && smpPtr < base+v->end); \ smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \ - WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 128) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 128) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_8BIT_SMP_MONO_SINTRP_TAP_FIX \ - assert(smpPtr >= base && smpPtr < base+v->end); \ smpTapPtr = (smpPtr <= leftEdgePtr) ? (int8_t *)&v->leftEdgeTaps8[(int32_t)(smpPtr-loopStartPtr)] : (int8_t *)smpPtr; \ - WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 128) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 128) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; #define RENDER_16BIT_SMP_SINTRP_TAP_FIX \ - assert(smpPtr >= base && smpPtr < base+v->end); \ smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \ - WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 32768) \ - *dMixBufferL++ += dSample * dVolL; \ - *dMixBufferR++ += dSample * dVolR; \ + WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 32768) \ + *fMixBufferL++ += fSample * fVolumeL; \ + *fMixBufferR++ += fSample * fVolumeR; #define RENDER_16BIT_SMP_MONO_SINTRP_TAP_FIX \ - assert(smpPtr >= base && smpPtr < base+v->end); \ smpTapPtr = (smpPtr <= leftEdgePtr) ? (int16_t *)&v->leftEdgeTaps16[(int32_t)(smpPtr-loopStartPtr)] : (int16_t *)smpPtr; \ - WINDOWED_SINC_INTERPOLATION(smpTapPtr, posFrac, 32768) \ - dSample *= dVolL; \ - *dMixBufferL++ += dSample; \ - *dMixBufferR++ += dSample; \ + WINDOWED_SINC_INTERPOLATION(smpTapPtr, positionFrac, 32768) \ + fSample *= fVolumeL; \ + *fMixBufferL++ += fSample; \ + *fMixBufferR++ += fSample; /* ----------------------------------------------------------------------- */ /* SAMPLES-TO-MIX LIMITING MACROS */ /* ----------------------------------------------------------------------- */ +#if CPU_64BIT +#define LIMIT_NUM +#else +#define LIMIT_NUM if (i > (1<<(32-MIXER_FRAC_BITS))-1) i = (1<<(32-MIXER_FRAC_BITS))-1; +#endif + #define LIMIT_MIX_NUM \ - i = (v->end - 1) - pos; \ - const uint64_t tmp64 = ((uint64_t)i << 32) | ((uint32_t)posFrac ^ 0xFFFFFFFF); \ + samplesToMix = INT32_MAX; \ + if (v->delta != 0) \ + { \ + i = (v->sampleEnd - 1) - position; \ + LIMIT_NUM \ + const uintCPUWord_t dividend = ((uintCPUWord_t)i << MIXER_FRAC_BITS) | ((uint32_t)positionFrac ^ MIXER_FRAC_MASK); \ + samplesToMix = (uint32_t)(dividend / (uintCPUWord_t)v->delta) + 1; \ + } \ \ - samplesToMix = (uint32_t)(tmp64 / (uint64_t)v->delta) + 1; /* this can be slow on 32-bit systems... */ \ if (samplesToMix > samplesLeft) \ - samplesToMix = samplesLeft; \ + samplesToMix = samplesLeft; #define START_BIDI \ - if (v->backwards) \ + if (v->samplingBackwards) \ { \ tmpDelta = 0 - delta; \ - assert(pos >= v->loopStart && pos < v->end); \ - pos = ~pos; \ - smpPtr = revBase + pos; \ - posFrac ^= MIXER_FRAC_MASK; \ + position = ~position; \ + smpPtr = revBase + position; \ + positionFrac ^= MIXER_FRAC_MASK; \ } \ else \ { \ tmpDelta = delta; \ - assert(pos >= 0 && pos < v->end); \ - smpPtr = base + pos; \ + smpPtr = base + position; \ } \ \ - const int32_t deltaHi = (int64_t)tmpDelta >> MIXER_FRAC_BITS; \ - const uint32_t deltaLo = tmpDelta & MIXER_FRAC_MASK; \ + const int32_t deltaHi = (intCPUWord_t)tmpDelta >> MIXER_FRAC_BITS; \ + const uint32_t deltaLo = tmpDelta & MIXER_FRAC_MASK; #define LIMIT_MIX_NUM_RAMP \ - if (v->volRampSamples == 0) \ + if (v->volumeRampLength == 0) \ { \ - dVolLDelta = 0.0; \ - dVolRDelta = 0.0; \ + fVolumeLDelta = 0.0; \ + fVolumeRDelta = 0.0; \ \ if (v->isFadeOutVoice) \ { \ @@ -292,16 +287,16 @@ } \ else \ { \ - if (samplesToMix > v->volRampSamples) \ - samplesToMix = v->volRampSamples; \ + if (samplesToMix > v->volumeRampLength) \ + samplesToMix = v->volumeRampLength; \ \ - v->volRampSamples -= samplesToMix; \ - } \ + v->volumeRampLength -= samplesToMix; \ + } #define LIMIT_MIX_NUM_MONO_RAMP \ - if (v->volRampSamples == 0) \ + if (v->volumeRampLength == 0) \ { \ - dVolLDelta = 0.0; \ + fVolumeLDelta = 0.0; \ if (v->isFadeOutVoice) \ { \ v->active = false; /* volume ramp fadeout-voice is done, shut it down */ \ @@ -310,55 +305,54 @@ } \ else \ { \ - if (samplesToMix > v->volRampSamples) \ - samplesToMix = v->volRampSamples; \ + if (samplesToMix > v->volumeRampLength) \ + samplesToMix = v->volumeRampLength; \ \ - v->volRampSamples -= samplesToMix; \ - } \ + v->volumeRampLength -= samplesToMix; \ + } #define HANDLE_SAMPLE_END \ - pos = (int32_t)(smpPtr - base); \ - if (pos >= v->end) \ + position = (int32_t)(smpPtr - base); \ + if (position >= v->sampleEnd) \ { \ v->active = false; \ return; \ - } \ + } #define WRAP_LOOP \ - pos = (int32_t)(smpPtr - base); \ - if (pos >= v->end) \ + position = (int32_t)(smpPtr - base); \ + if (position >= v->sampleEnd) \ { \ do \ { \ - pos -= v->loopLength; \ + position -= v->loopLength; \ } \ - while (pos >= v->end); \ + while (position >= v->sampleEnd); \ \ - smpPtr = base + pos; \ + smpPtr = base + position; \ \ v->hasLooped = true; \ - } \ + } #define WRAP_BIDI_LOOP \ - if (pos >= v->end) \ + if (position >= v->sampleEnd) \ { \ do \ { \ - pos -= v->loopLength; \ - v->backwards ^= 1; \ + position -= v->loopLength; \ + v->samplingBackwards ^= 1; \ } \ - while (pos >= v->end); \ + while (position >= v->sampleEnd); \ v->hasLooped = true; \ - } \ + } #define END_BIDI \ - if (v->backwards) \ + if (v->samplingBackwards) \ { \ - posFrac ^= MIXER_FRAC_MASK; \ - pos = ~(int32_t)(smpPtr - revBase); \ + positionFrac ^= MIXER_FRAC_MASK; \ + position = ~(int32_t)(smpPtr - revBase); \ } \ else \ { \ - pos = (int32_t)(smpPtr - base); \ - } \ - + position = (int32_t)(smpPtr - base); \ + } diff --git a/src/mixer/ft2_silence_mix.c b/src/mixer/ft2_silence_mix.c @@ -1,22 +1,23 @@ #include <stdint.h> #include "../ft2_audio.h" +#include "../ft2_cpu.h" // used for the audio channel mixer when voice volume is zero void silenceMixRoutine(voice_t *v, int32_t numSamples) { - const uint64_t newPos = v->delta * (uint64_t)numSamples; - const uint32_t addPos = (uint32_t)(newPos >> MIXER_FRAC_BITS); - uint64_t addFrac = newPos & MIXER_FRAC_MASK; + const uint64_t samplesToMix = (uint64_t)v->delta * (uint32_t)numSamples; // fixed-point - addFrac += v->posFrac; - int32_t pos = v->pos + addPos + (uint32_t)(addFrac >> MIXER_FRAC_BITS); - uint64_t posFrac = addFrac & MIXER_FRAC_MASK; + const uint32_t samples = (uint32_t)(samplesToMix >> MIXER_FRAC_BITS); + const uintCPUWord_t samplesFrac = (samplesToMix & MIXER_FRAC_MASK) + v->positionFrac; - if (pos < v->end) // we haven't reached the sample's end yet + uint32_t position = v->position + samples + (uint32_t)(samplesFrac >> MIXER_FRAC_BITS); + uintCPUWord_t positionFrac = samplesFrac & MIXER_FRAC_MASK; + + if (position < (unsigned)v->sampleEnd) // we haven't reached the sample's end yet { - v->posFrac = posFrac; - v->pos = pos; + v->positionFrac = positionFrac; + v->position = position; return; } @@ -31,28 +32,28 @@ void silenceMixRoutine(voice_t *v, int32_t numSamples) if (v->loopType == LOOP_FORWARD) { if (v->loopLength >= 2) - pos = v->loopStart + ((pos - v->end) % v->loopLength); + position = v->loopStart + ((position - v->sampleEnd) % v->loopLength); else - pos = v->loopStart; + position = v->loopStart; } else // pingpong loop { if (v->loopLength >= 2) { - const int32_t overflow = pos - v->end; - const int32_t cycles = overflow / v->loopLength; - const int32_t phase = overflow % v->loopLength; + const uint32_t overflow = position - v->sampleEnd; + const uint32_t cycles = overflow / v->loopLength; + const uint32_t phase = overflow % v->loopLength; - pos = v->loopStart + phase; - v->backwards ^= !(cycles & 1); + position = v->loopStart + phase; + v->samplingBackwards ^= !(cycles & 1); } else { - pos = v->loopStart; + position = v->loopStart; } } v->hasLooped = true; - v->posFrac = posFrac; - v->pos = pos; + v->positionFrac = positionFrac; + v->position = position; } diff --git a/src/mixer/ft2_windowed_sinc.c b/src/mixer/ft2_windowed_sinc.c @@ -1,3 +1,9 @@ +/* Code taken from the OpenMPT project, which has a BSD +** license which is compatible with this project. +** +** The code has been slightly modified. +*/ + #include <stdint.h> #include <stdbool.h> #include <stdlib.h> @@ -5,15 +11,9 @@ #include "ft2_windowed_sinc.h" // globalized -double *gKaiserSinc = NULL; -double *gDownSample1 = NULL; -double *gDownSample2 = NULL; - -/* Code taken from the OpenMPT project, which has a BSD -** license which is compatible with this this project. -** -** The code has been slightly modified. -*/ +float *fKaiserSinc = NULL; +float *fDownSample1 = NULL; +float *fDownSample2 = NULL; static double Izero(double y) // Compute Bessel function Izero(y) using a series approximation { @@ -32,7 +32,7 @@ static double Izero(double y) // Compute Bessel function Izero(y) using a series return s; } -static void getSinc(double *dLUTPtr, const double beta, const double cutoff) +static void getSinc(float *fLUTPtr, const double beta, const double cutoff) { const double izeroBeta = Izero(beta); const double kPi = 4.0 * atan(1.0) * cutoff; // M_PI can't be trusted @@ -56,44 +56,43 @@ static void getSinc(double *dLUTPtr, const double beta, const double cutoff) dSinc = sin(xPi) * Izero(beta * sqrt(1.0 - x * x * xMul)) / (izeroBeta * xPi); // Kaiser window } - dLUTPtr[i] = dSinc * cutoff; + fLUTPtr[i] = (float)(dSinc * cutoff); } } bool calcWindowedSincTables(void) { - gKaiserSinc = (double *)malloc(SINC_LUT_LEN * sizeof (double)); - gDownSample1 = (double *)malloc(SINC_LUT_LEN * sizeof (double)); - gDownSample2 = (double *)malloc(SINC_LUT_LEN * sizeof (double)); + fKaiserSinc = (float *)malloc(SINC_LUT_LEN * sizeof (float)); + fDownSample1 = (float *)malloc(SINC_LUT_LEN * sizeof (float)); + fDownSample2 = (float *)malloc(SINC_LUT_LEN * sizeof (float)); - if (gKaiserSinc == NULL || gDownSample1 == NULL || gDownSample2 == NULL) + if (fKaiserSinc == NULL || fDownSample1 == NULL || fDownSample2 == NULL) return false; - // OpenMPT parameters - getSinc(gKaiserSinc, 9.6377, 0.97); - getSinc(gDownSample1, 8.5, 0.5); - getSinc(gDownSample2, 2.7625, 0.425); + getSinc(fKaiserSinc, 9.6377, 0.97); + getSinc(fDownSample1, 8.5, 0.5); + getSinc(fDownSample2, 2.7625, 0.425); return true; } void freeWindowedSincTables(void) { - if (gKaiserSinc != NULL) + if (fKaiserSinc != NULL) { - free(gKaiserSinc); - gKaiserSinc = NULL; + free(fKaiserSinc); + fKaiserSinc = NULL; } - if (gDownSample1 != NULL) + if (fDownSample1 != NULL) { - free(gDownSample1); - gDownSample1 = NULL; + free(fDownSample1); + fDownSample1 = NULL; } - if (gDownSample2 != NULL) + if (fDownSample2 != NULL) { - free(gDownSample2); - gDownSample2 = NULL; + free(fDownSample2); + fDownSample2 = NULL; } } diff --git a/src/mixer/ft2_windowed_sinc.h b/src/mixer/ft2_windowed_sinc.h @@ -2,14 +2,11 @@ #include <stdint.h> #include <stdbool.h> +#include "ft2_mix.h" -#ifndef MIXER_FRAC_BITS -#define MIXER_FRAC_BITS 32 -#endif - -// if you change this, also change SINC_PHASES_BITS (>4096 makes little sense) -#define SINC_PHASES 4096 -#define SINC_PHASES_BITS 12 /* log2(SINC_PHASES) */ +// if you change this, also change SINC_PHASES_BITS (>8192 gives very little improvement) +#define SINC_PHASES 8192 +#define SINC_PHASES_BITS 13 /* log2(SINC_PHASES) */ // don't change these! @@ -19,15 +16,16 @@ #define SINC_FSHIFT (MIXER_FRAC_BITS-(SINC_PHASES_BITS+SINC_TAPS_BITS)) #define SINC_FMASK ((SINC_TAPS*SINC_PHASES)-SINC_TAPS) +#define SINC_CENTER_TAP (SINC_TAPS/2) #define SINC_LEFT_TAPS ((SINC_TAPS/2)-1) #define SINC_RIGHT_TAPS (SINC_TAPS/2) // for LUT calculation #define SINC_MID_TAP ((SINC_TAPS/2)*SINC_PHASES) -extern double *gKaiserSinc; -extern double *gDownSample1; -extern double *gDownSample2; +extern float *fKaiserSinc; +extern float *fDownSample1; +extern float *fDownSample2; bool calcWindowedSincTables(void); void freeWindowedSincTables(void); diff --git a/src/modloaders/ft2_load_digi.c b/src/modloaders/ft2_load_digi.c @@ -1,4 +1,8 @@ -// DIGI Booster (non-Pro) module loader +/* DIGI Booster (non-Pro) module loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -13,15 +17,15 @@ #pragma pack(push) #pragma pack(1) #endif -typedef struct digiHeaderTyp_t +typedef struct digiHdr_t { - char sig[20]; + char ID[20]; char verStr[4]; - uint8_t ver; + uint8_t version; uint8_t numChannels; uint8_t packedPatternsFlag; char reserved[19]; - uint8_t numPats; + uint8_t numPatterns; uint8_t numOrders; uint8_t orders[128]; uint32_t smpLength[31]; @@ -35,58 +39,58 @@ typedef struct digiHeaderTyp_t #ifdef __GNUC__ __attribute__ ((packed)) #endif -digiHeaderTyp; +digiHdr_t; #ifdef _MSC_VER #pragma pack(pop) #endif -static void readPatternNote(FILE *f, tonTyp *ton); +static void readPatternNote(FILE *f, note_t *p); bool loadDIGI(FILE *f, uint32_t filesize) { int16_t i, j, k; - tonTyp *ton; - sampleTyp *s; - digiHeaderTyp h_DIGI; + sample_t *s; + digiHdr_t hdr; tmpLinearPeriodsFlag = false; // use Amiga periods - if (filesize < sizeof (h_DIGI)) + if (filesize < sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - memset(&h_DIGI, 0, sizeof (digiHeaderTyp)); - if (fread(&h_DIGI, 1, sizeof (h_DIGI), f) != sizeof (h_DIGI)) + memset(&hdr, 0, sizeof (hdr)); + if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - if (h_DIGI.numChannels < 1 || h_DIGI.numChannels > 8) + if (hdr.numChannels < 1 || hdr.numChannels > 8) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - h_DIGI.numOrders++; - h_DIGI.numPats++; + hdr.numOrders++; + hdr.numPatterns++; - if (h_DIGI.numOrders < 1 || h_DIGI.numOrders > 128) + if (hdr.numOrders < 1 || hdr.numOrders > 128) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - songTmp.antChn = h_DIGI.numChannels; - songTmp.len = h_DIGI.numOrders; - songTmp.initialTempo = songTmp.tempo = 6; - songTmp.speed = 125; - memcpy(songTmp.songTab, h_DIGI.orders, 128); + memcpy(songTmp.orders, hdr.orders, 128); + + songTmp.numChannels = hdr.numChannels; + songTmp.songLength = hdr.numOrders; + songTmp.BPM = 125; + songTmp.speed = 6; // load pattern data - for (i = 0; i < h_DIGI.numPats; i++) + for (i = 0; i < hdr.numPatterns; i++) { if (!allocateTmpPatt(i, 64)) { @@ -94,7 +98,7 @@ bool loadDIGI(FILE *f, uint32_t filesize) return false; } - if (h_DIGI.packedPatternsFlag) + if (hdr.packedPatternsFlag) { uint16_t pattSize; uint8_t bitMasks[64]; @@ -105,61 +109,58 @@ bool loadDIGI(FILE *f, uint32_t filesize) for (j = 0; j < 64; j++) { uint8_t bit = 128; - for (k = 0; k < songTmp.antChn; k++, bit >>= 1) + for (k = 0; k < songTmp.numChannels; k++, bit >>= 1) { - ton = &pattTmp[i][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k]; if (bitMasks[j] & bit) - readPatternNote(f, ton); + readPatternNote(f, p); } } } else { - for (j = 0; j < songTmp.antChn; j++) + for (j = 0; j < songTmp.numChannels; j++) { for (k = 0; k < 64; k++) - { - ton = &pattTmp[i][(k * MAX_VOICES) + j]; - readPatternNote(f, ton); - } + readPatternNote(f, &patternTmp[i][(k * MAX_CHANNELS) + j]); } } if (tmpPatternEmpty(i)) { - if (pattTmp[i] != NULL) + if (patternTmp[i] != NULL) { - free(pattTmp[i]); - pattTmp[i] = NULL; + free(patternTmp[i]); + patternTmp[i] = NULL; } } } // pattern command handling - for (i = 0; i < h_DIGI.numPats; i++) + for (i = 0; i < hdr.numPatterns; i++) { - if (pattTmp[i] == NULL) + if (patternTmp[i] == NULL) continue; for (j = 0; j < 64; j++) { - for (k = 0; k < songTmp.antChn; k++) + for (k = 0; k < songTmp.numChannels; k++) { - ton = &pattTmp[i][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k]; - if (ton->effTyp == 0x8) // Robot effect (not supported) + if (p->efx == 0x8) // Robot effect (not supported) { - ton->effTyp = 0; - ton->eff = 0; + p->efx = 0; + p->efxData = 0; } - else if (ton->effTyp == 0xE) + else if (p->efx == 0xE) { - switch (ton->eff >> 4) + switch (p->efxData >> 4) { - case 0x3: ton->effTyp = ton->eff = 0; break; // backwards play (not supported) - case 0x4: ton->eff = 0xC0; break; // stop sample (convert to EC0) - case 0x8: ton->effTyp = ton->eff = 0; break; // high sample offset (not supported) - case 0x9: ton->effTyp = ton->eff = 0; break; // retrace (not supported) + case 0x3: p->efx = p->efxData = 0; break; // backwards play (not supported) + case 0x4: p->efxData = 0xC0; break; // stop sample (convert to EC0) + case 0x8: p->efx = p->efxData = 0; break; // high sample offset (not supported) + case 0x9: p->efx = p->efxData = 0; break; // retrace (not supported) default: break; } } @@ -170,8 +171,8 @@ bool loadDIGI(FILE *f, uint32_t filesize) // load sample data for (i = 0; i < 31; i++) { - memcpy(songTmp.instrName[1+i], h_DIGI.smpName[i], 22); - if (h_DIGI.smpLength[i] == 0) + memcpy(songTmp.instrName[1+i], hdr.smpName[i], 22); + if (hdr.smpLength[i] == 0) continue; if (!allocateTmpInstr(1+i)) @@ -182,63 +183,60 @@ bool loadDIGI(FILE *f, uint32_t filesize) setNoEnvelope(instrTmp[1+i]); - s = &instrTmp[1+i]->samp[0]; - memcpy(s->name, h_DIGI.smpName[i], 22); - - s->len = SWAP32(h_DIGI.smpLength[i]); - s->fine = 8 * ((2 * ((h_DIGI.smpFinetune[i] & 0xF) ^ 8)) - 16); - s->vol = h_DIGI.smpVolume[i]; - s->repS = SWAP32(h_DIGI.smpLoopStart[i]); - s->repL = SWAP32(h_DIGI.smpLoopLength[i]); + s = &instrTmp[1+i]->smp[0]; + memcpy(s->name, hdr.smpName[i], 22); - if (s->vol > 64) - s->vol = 64; + s->length = SWAP32(hdr.smpLength[i]); + s->finetune = FINETUNE_MOD2XM(hdr.smpFinetune[i]); + s->volume = hdr.smpVolume[i]; + s->loopStart = SWAP32(hdr.smpLoopStart[i]); + s->loopLength = SWAP32(hdr.smpLoopLength[i]); - if (s->repL < 2) - s->repL = 2; + if (s->loopLength < 2) + s->loopLength = 2; // fix overflown loop - if (s->repS+s->repL > s->len) + if (s->loopStart+s->loopLength > s->length) { - if (s->repS >= s->len) + if (s->loopStart >= s->length) { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; } else { - s->repL = s->len - s->repS; + s->loopLength = s->length - s->loopStart; } } - if (s->repS+s->repL > 2) + if (s->loopStart+s->loopLength > 2) { - s->typ = 1; // enable loop + s->flags |= LOOP_FWD; // enable loop } else { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; } - if (!allocateTmpSmpData(s, s->len)) + if (!allocateSmpData(s, s->length, false)) { loaderMsgBox("Not enough memory!"); return false; } - int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f); - if (bytesRead < s->len) + int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f); + if (bytesRead < s->length) { - int32_t bytesToClear = s->len - bytesRead; - memset(&s->pek[bytesRead], 0, bytesToClear); + int32_t bytesToClear = s->length - bytesRead; + memset(&s->dataPtr[bytesRead], 0, bytesToClear); } } return true; } -static void readPatternNote(FILE *f, tonTyp *ton) +static void readPatternNote(FILE *f, note_t *p) { uint8_t bytes[4]; fread(bytes, 1, 4, f); @@ -249,12 +247,12 @@ static void readPatternNote(FILE *f, tonTyp *ton) { if (period >= ptPeriods[i]) { - ton->ton = 1 + (3*12) + i; + p->note = 1 + (3*12) + i; break; } } - ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); - ton->effTyp = bytes[2] & 0x0F; - ton->eff = bytes[3]; + p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); + p->efx = bytes[2] & 0x0F; + p->efxData = bytes[3]; } diff --git a/src/modloaders/ft2_load_mod.c b/src/modloaders/ft2_load_mod.c @@ -1,4 +1,8 @@ -// NoiseTracker/ProTracker (or compatible) MOD loader +/* NoiseTracker/ProTracker (or compatible) MOD loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -27,75 +31,71 @@ bool loadMOD(FILE *f, uint32_t filesize) { uint8_t bytes[4], modFormat, numChannels; int16_t i, j, k; - uint16_t a, b, period; - tonTyp *ton; - sampleTyp *s; - songMOD31HeaderTyp h_MOD31; + uint16_t a, b; + sample_t *s; + modHdr_t hdr; tmpLinearPeriodsFlag = false; // use Amiga periods - if (filesize < sizeof (h_MOD31)) + if (filesize < sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - memset(&h_MOD31, 0, sizeof (songMOD31HeaderTyp)); - if (fread(&h_MOD31, 1, sizeof (h_MOD31), f) != sizeof (h_MOD31)) + memset(&hdr, 0, sizeof (hdr)); + if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - modFormat = getModType(&numChannels, h_MOD31.sig); + modFormat = getModType(&numChannels, hdr.ID); if (modFormat == FORMAT_UNKNOWN) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - bool hasMoreThan32Chans = numChannels > 32; + if (modFormat == FORMAT_MK && hdr.numOrders == 129) + hdr.numOrders = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this hack?) - songTmp.antChn = hasMoreThan32Chans ? 32 : numChannels; - songTmp.len = h_MOD31.len; - songTmp.repS = h_MOD31.repS; - songTmp.initialTempo = songTmp.tempo = 6; - songTmp.speed = 125; - - memcpy(songTmp.songTab, h_MOD31.songTab, 128); - - if (modFormat == FORMAT_MK && songTmp.len == 129) - songTmp.len = 127; // fixes a specific copy of beatwave.mod (FIXME: Do we want to keep this hack?) - - if (songTmp.repS >= songTmp.len) - songTmp.repS = 0; - - if (songTmp.antChn == 0 || songTmp.len < 1 || songTmp.len > 128) + if (numChannels == 0 || hdr.numOrders < 1 || hdr.numOrders > 128) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } + bool tooManyChannels = numChannels > MAX_CHANNELS; + + songTmp.numChannels = tooManyChannels ? MAX_CHANNELS : numChannels; + songTmp.songLength = hdr.numOrders; + songTmp.songLoopStart = hdr.songLoopStart; + songTmp.BPM = 125; + songTmp.speed = 6; + + memcpy(songTmp.orders, hdr.orders, 128); + for (a = 0; a < 31; a++) { - songMODInstrHeaderTyp *smp = &h_MOD31.instr[a]; + modSmpHdr_t *modSmp = &hdr.smp[a]; // copy over sample name if format isn't "His Master's Noisetracker" (junk sample names) if (modFormat != FORMAT_HMNT) - memcpy(songTmp.instrName[1+a], smp->name, 22); + memcpy(songTmp.instrName[1+a], modSmp->name, 22); } - memcpy(songTmp.name, h_MOD31.name, 20); + memcpy(songTmp.name, hdr.name, 20); // count number of patterns b = 0; for (a = 0; a < 128; a++) { if (modFormat == FORMAT_FLT8) - songTmp.songTab[a] >>= 1; + songTmp.orders[a] >>= 1; - if (songTmp.songTab[a] > b) - b = songTmp.songTab[a]; + if (songTmp.orders[a] > b) + b = songTmp.orders[a]; } b++; @@ -114,40 +114,40 @@ bool loadMOD(FILE *f, uint32_t filesize) for (j = 0; j < 64; j++) { - for (k = 0; k < songTmp.antChn; k++) + for (k = 0; k < songTmp.numChannels; k++) { - ton = &pattTmp[a][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k]; fread(bytes, 1, 4, f); // period to note - period = ((bytes[0] & 0x0F) << 8) | bytes[1]; + uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1]; for (i = 0; i < 8*12; i++) { if (period >= amigaPeriod[i]) { - ton->ton = (uint8_t)i + 1; + p->note = (uint8_t)i + 1; break; } } - ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); - ton->effTyp = bytes[2] & 0x0F; - ton->eff = bytes[3]; + p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); + p->efx = bytes[2] & 0x0F; + p->efxData = bytes[3]; } - if (hasMoreThan32Chans) + if (tooManyChannels) { - int32_t remainingChans = numChannels-songTmp.antChn; + int32_t remainingChans = numChannels-songTmp.numChannels; fseek(f, remainingChans*4, SEEK_CUR); } } if (tmpPatternEmpty(a)) { - if (pattTmp[a] != NULL) + if (patternTmp[a] != NULL) { - free(pattTmp[a]); - pattTmp[a] = NULL; + free(patternTmp[a]); + patternTmp[a] = NULL; } } } @@ -167,30 +167,30 @@ bool loadMOD(FILE *f, uint32_t filesize) for (a = 0; a < b*2; a++) { - int32_t pattern = a >> 1; + int32_t pattNum = a >> 1; int32_t chnOffset = (a & 1) * 4; for (j = 0; j < 64; j++) { for (k = 0; k < 4; k++) { - ton = &pattTmp[pattern][(j * MAX_VOICES) + (k+chnOffset)]; + note_t *p = &patternTmp[pattNum][(j * MAX_CHANNELS) + (k+chnOffset)]; fread(bytes, 1, 4, f); // period to note - period = ((bytes[0] & 0x0F) << 8) | bytes[1]; + uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1]; for (i = 0; i < 8*12; i++) { if (period >= amigaPeriod[i]) { - ton->ton = (uint8_t)i + 1; + p->note = (uint8_t)i + 1; break; } } - ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); - ton->effTyp = bytes[2] & 0x0F; - ton->eff = bytes[3]; + p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); + p->efx = bytes[2] & 0x0F; + p->efxData = bytes[3]; } } } @@ -199,10 +199,10 @@ bool loadMOD(FILE *f, uint32_t filesize) { if (tmpPatternEmpty(a)) { - if (pattTmp[a] != NULL) + if (patternTmp[a] != NULL) { - free(pattTmp[a]); - pattTmp[a] = NULL; + free(patternTmp[a]); + patternTmp[a] = NULL; } } } @@ -211,76 +211,76 @@ bool loadMOD(FILE *f, uint32_t filesize) // pattern command conversion for (a = 0; a < b; a++) { - if (pattTmp[a] == NULL) + if (patternTmp[a] == NULL) continue; for (j = 0; j < 64; j++) { - for (k = 0; k < songTmp.antChn; k++) + for (k = 0; k < songTmp.numChannels; k++) { - ton = &pattTmp[a][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k]; - if (ton->effTyp == 0xC) + if (p->efx == 0xC) { - if (ton->eff > 64) - ton->eff = 64; + if (p->efxData > 64) + p->efxData = 64; } - else if (ton->effTyp == 0x1) + else if (p->efx == 0x1) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efx = 0; } - else if (ton->effTyp == 0x2) + else if (p->efx == 0x2) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efx = 0; } - else if (ton->effTyp == 0x5) + else if (p->efx == 0x5) { - if (ton->eff == 0) - ton->effTyp = 0x3; + if (p->efxData == 0) + p->efx = 0x3; } - else if (ton->effTyp == 0x6) + else if (p->efx == 0x6) { - if (ton->eff == 0) - ton->effTyp = 0x4; + if (p->efxData == 0) + p->efx = 0x4; } - else if (ton->effTyp == 0xA) + else if (p->efx == 0xA) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efx = 0; } - else if (ton->effTyp == 0xE) + else if (p->efx == 0xE) { // check if certain E commands are empty - if (ton->eff == 0x10 || ton->eff == 0x20 || ton->eff == 0xA0 || ton->eff == 0xB0) + if (p->efxData == 0x10 || p->efxData == 0x20 || p->efxData == 0xA0 || p->efxData == 0xB0) { - ton->effTyp = 0; - ton->eff = 0; + p->efx = 0; + p->efxData = 0; } } if (modFormat == FORMAT_NT || modFormat == FORMAT_HMNT) { // any Dxx == D00 in NT/HMNT - if (ton->effTyp == 0xD) - ton->eff = 0; + if (p->efx == 0xD) + p->efxData = 0; // effect F with param 0x00 does nothing in NT/HMNT - if (ton->effTyp == 0xF && ton->eff == 0) - ton->effTyp = 0; + if (p->efx == 0xF && p->efxData == 0) + p->efx = 0; } else if (modFormat == FORMAT_FLT4 || modFormat == FORMAT_FLT8) // Startrekker { - if (ton->effTyp == 0xE) // remove unsupported "assembly macros" command + if (p->efx == 0xE) // remove unsupported "assembly macros" command { - ton->effTyp = 0; - ton->eff = 0; + p->efx = 0; + p->efxData = 0; } // Startrekker is always in vblank mode, and limits speed to 0x1F - if (ton->effTyp == 0xF && ton->eff > 0x1F) - ton->eff = 0x1F; + if (p->efx == 0xF && p->efxData > 0x1F) + p->efxData = 0x1F; } } } @@ -288,7 +288,7 @@ bool loadMOD(FILE *f, uint32_t filesize) for (a = 0; a < 31; a++) { - if (h_MOD31.instr[a].len == 0) + if (hdr.smp[a].length == 0) continue; if (!allocateTmpInstr(1+a)) @@ -299,72 +299,69 @@ bool loadMOD(FILE *f, uint32_t filesize) setNoEnvelope(instrTmp[1+a]); - s = &instrTmp[1+a]->samp[0]; + s = &instrTmp[1+a]->smp[0]; // copy over sample name if format isn't "His Master's Noisetracker" (junk sample names) if (modFormat != FORMAT_HMNT) memcpy(s->name, songTmp.instrName[1+a], 22); if (modFormat == FORMAT_HMNT) // finetune in "His Master's NoiseTracker" is different - h_MOD31.instr[a].fine = (uint8_t)((-h_MOD31.instr[a].fine & 0x1F) >> 1); // one more bit of precision, + inverted - - s->len = 2 * SWAP16(h_MOD31.instr[a].len); - s->fine = 8 * ((2 * ((h_MOD31.instr[a].fine & 0xF) ^ 8)) - 16); - s->vol = h_MOD31.instr[a].vol; - s->repS = 2 * SWAP16(h_MOD31.instr[a].repS); - s->repL = 2 * SWAP16(h_MOD31.instr[a].repL); + hdr.smp[a].finetune = (uint8_t)((-hdr.smp[a].finetune & 0x1F) >> 1); // one more bit of precision, + inverted - if (s->vol > 64) - s->vol = 64; + s->length = 2 * SWAP16(hdr.smp[a].length); + s->finetune = FINETUNE_MOD2XM(hdr.smp[a].finetune); + s->volume = hdr.smp[a].volume; + s->loopStart = 2 * SWAP16(hdr.smp[a].loopStart); + s->loopLength = 2 * SWAP16(hdr.smp[a].loopLength); - if (s->repL < 2) - s->repL = 2; + if (s->loopLength < 2) + s->loopLength = 2; // fix for poorly converted STK (< v2.5) -> PT/NT modules (FIXME: Worth keeping or not?) - if (s->repL > 2 && s->repS+s->repL > s->len) + if (s->loopLength > 2 && s->loopStart+s->loopLength > s->length) { - if ((s->repS>>1)+s->repL <= s->len) - s->repS >>= 1; + if ((s->loopStart >> 1) + s->loopLength <= s->length) + s->loopStart >>= 1; } // fix overflown loop - if (s->repS+s->repL > s->len) + if (s->loopStart+s->loopLength > s->length) { - if (s->repS >= s->len) + if (s->loopStart >= s->length) { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; } else { - s->repL = s->len - s->repS; + s->loopLength = s->length - s->loopStart; } } - if (s->repS+s->repL > 2) - s->typ = 1; // enable loop + if (s->loopStart+s->loopLength > 2) + s->flags |= LOOP_FWD; // enable loop - if (!allocateTmpSmpData(s, s->len)) + if (!allocateSmpData(s, s->length, false)) { loaderMsgBox("Not enough memory!"); return false; } - int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f); - if (bytesRead < s->len) + int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f); + if (bytesRead < s->length) { - int32_t bytesToClear = s->len - bytesRead; - memset(&s->pek[bytesRead], 0, bytesToClear); + int32_t bytesToClear = s->length - bytesRead; + memset(&s->dataPtr[bytesRead], 0, bytesToClear); } - if (s->typ == 0) // clear repL and repS on non-looping samples... + if (GET_LOOPTYPE(s->flags) == LOOP_OFF) // clear loopLength and loopStart on non-looping samples... { - s->repL = 0; - s->repS = 0; + s->loopLength = 0; + s->loopStart = 0; } } - if (hasMoreThan32Chans) + if (tooManyChannels) loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!"); return true; diff --git a/src/modloaders/ft2_load_s3m.c b/src/modloaders/ft2_load_s3m.c @@ -1,4 +1,8 @@ -// Scream Tracker 3 (or compatible) S3M loader +/* Scream Tracker 3 (or compatible) S3M loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -13,38 +17,36 @@ #pragma pack(push) #pragma pack(1) #endif -typedef struct songS3MinstrHeaderTyp_t +typedef struct s3mSmpHdr_t { - uint8_t typ; - char dosName[12]; - uint8_t memSegH; - uint16_t memSeg; - int32_t len, repS, repE; - uint8_t vol, dsk, pack, flags; - int32_t c2Spd, res1; - uint16_t gusPos; - uint8_t res2[6]; - char name[28], id[4]; + uint8_t type, junk1[12], offsetInFileH; + uint16_t offsetInFile; + int32_t length, loopStart, loopEnd; + uint8_t volume, junk2, packFlag, flags; + int32_t midCFreq, junk3; + uint16_t junk4; + uint8_t junk5[6]; + char name[28], ID[4]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songS3MinstrHeaderTyp; +s3mSmpHdr_t; -typedef struct songS3MHeaderTyp_t +typedef struct s3mHdr_t { char name[28]; - uint8_t id1a, typ; - uint16_t res1; - int16_t songTabLen, antInstr, antPatt; - uint16_t flags, trackerID, ver; - char id[4]; - uint8_t globalVol, defSpeed, defTempo, masterVol, res2[12], chanType[32]; + uint8_t junk1, type; + uint16_t junk2; + int16_t numOrders, numSamples, numPatterns; + uint16_t flags, junk3, version; + char ID[4]; + uint8_t junk4, speed, BPM, junk5, junk6[12], chnSettings[32]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songS3MHeaderTyp; +s3mHdr_t; #ifdef _MSC_VER #pragma pack(pop) #endif @@ -55,117 +57,111 @@ static int8_t countS3MChannels(uint16_t antPtn); bool loadS3M(FILE *f, uint32_t filesize) { - uint8_t ha[2048]; uint8_t alastnfo[32], alastefx[32], alastvibnfo[32], s3mLastGInstr[32]; - uint8_t typ; - int16_t ai, ap, ver, ii, kk, tmp; - uint16_t ptnOfs[256]; - int32_t i, j, k, len; - tonTyp ton; - sampleTyp *s; - songS3MHeaderTyp h_S3M; - songS3MinstrHeaderTyp h_S3MInstr; + int16_t ii, kk, tmp; + int32_t patternOffsets[256], sampleOffsets[256], j, k; + note_t tmpNote; + sample_t *s; + s3mHdr_t hdr; + s3mSmpHdr_t smpHdr; tmpLinearPeriodsFlag = false; // use Amiga periods - if (filesize < sizeof (h_S3M)) + if (filesize < sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - memset(&h_S3M, 0, sizeof (h_S3M)); - if (fread(&h_S3M, 1, sizeof (h_S3M), f) != sizeof (h_S3M)) + memset(&hdr, 0, sizeof (hdr)); + if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - if (h_S3M.antInstr > MAX_INST || h_S3M.songTabLen > 256 || h_S3M.antPatt > 256 || - h_S3M.typ != 16 || h_S3M.ver < 1 || h_S3M.ver > 2) + if (hdr.numSamples > MAX_INST || hdr.numOrders > MAX_ORDERS || hdr.numPatterns > MAX_PATTERNS || + hdr.type != 16 || hdr.version < 1 || hdr.version > 2) { loaderMsgBox("Error loading .s3m: Incompatible module!"); return false; } - memset(songTmp.songTab, 255, sizeof (songTmp.songTab)); - if (fread(songTmp.songTab, h_S3M.songTabLen, 1, f) != 1) + memset(songTmp.orders, 255, 256); // pad by 255 + if (fread(songTmp.orders, hdr.numOrders, 1, f) != 1) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - songTmp.len = h_S3M.songTabLen; + songTmp.songLength = hdr.numOrders; // remove pattern separators (254) k = 0; j = 0; - for (i = 0; i < songTmp.len; i++) + for (int32_t i = 0; i < songTmp.songLength; i++) { - if (songTmp.songTab[i] != 254) - songTmp.songTab[j++] = songTmp.songTab[i]; + if (songTmp.orders[i] != 254) + songTmp.orders[j++] = songTmp.orders[i]; else k++; } - if (k <= songTmp.len) - songTmp.len -= (uint16_t)k; + if (k <= songTmp.songLength) + songTmp.songLength -= (uint16_t)k; else - songTmp.len = 1; + songTmp.songLength = 1; - for (i = 1; i < songTmp.len; i++) + for (int32_t i = 1; i < songTmp.songLength; i++) { - if (songTmp.songTab[i] == 255) + if (songTmp.orders[i] == 255) { - songTmp.len = (uint16_t)i; + songTmp.songLength = (uint16_t)i; break; } } // clear unused song table entries - if (songTmp.len < 255) - memset(&songTmp.songTab[songTmp.len], 0, 255-songTmp.len); + if (songTmp.songLength < 255) + memset(&songTmp.orders[songTmp.songLength], 0, 255-songTmp.songLength); - songTmp.speed = h_S3M.defTempo; - if (songTmp.speed < 32) - songTmp.speed = 32; + memcpy(songTmp.name, hdr.name, 20); - songTmp.tempo = h_S3M.defSpeed; - if (songTmp.tempo == 0) - songTmp.tempo = 6; + songTmp.BPM = hdr.BPM; + songTmp.speed = hdr.speed; - if (songTmp.tempo > 31) - songTmp.tempo = 31; - - songTmp.initialTempo = songTmp.tempo; - memcpy(songTmp.name, h_S3M.name, 20); - - ap = h_S3M.antPatt; - ai = h_S3M.antInstr; - ver = h_S3M.ver; - - k = 31; - while (k >= 0 && h_S3M.chanType[k] >= 16) k--; - songTmp.antChn = (k + 2) & 254; - - if (fread(ha, ai*2, 1, f) != 1) + // load sample offsets + for (int32_t i = 0; i < hdr.numSamples; i++) { - loaderMsgBox("General I/O error during loading! Is the file in use?"); - return false; + uint16_t offset; + if (fread(&offset, 2, 1, f) != 1) + { + loaderMsgBox("General I/O error during loading! Is the file in use?"); + return false; + } + + sampleOffsets[i] = offset << 4; } - if (fread(ptnOfs, ap*2, 1, f) != 1) + // load pattern offsets + for (int32_t i = 0; i < hdr.numPatterns; i++) { - loaderMsgBox("General I/O error during loading! Is the file in use?"); - return false; + uint16_t offset; + if (fread(&offset, 2, 1, f) != 1) + { + loaderMsgBox("General I/O error during loading! Is the file in use?"); + return false; + } + + patternOffsets[i] = offset << 4; } // *** PATTERNS *** k = 0; - for (i = 0; i < ap; i++) + for (int32_t i = 0; i < hdr.numPatterns; i++) { - if (ptnOfs[i] == 0) + if (patternOffsets[i] == 0) continue; // empty pattern memset(alastnfo, 0, sizeof (alastnfo)); @@ -173,7 +169,7 @@ bool loadS3M(FILE *f, uint32_t filesize) memset(alastvibnfo, 0, sizeof (alastvibnfo)); memset(s3mLastGInstr, 0, sizeof (s3mLastGInstr)); - fseek(f, ptnOfs[i] << 4, SEEK_SET); + fseek(f, patternOffsets[i], SEEK_SET); if (feof(f)) continue; @@ -198,126 +194,126 @@ bool loadS3M(FILE *f, uint32_t filesize) while (k < j && kk < 64) { - typ = pattBuff[k++]; + uint8_t bits = pattBuff[k++]; - if (typ == 0) + if (bits == 0) { kk++; } else { - ii = typ & 31; + ii = bits & 31; - memset(&ton, 0, sizeof (ton)); + memset(&tmpNote, 0, sizeof (tmpNote)); // note and sample - if (typ & 32) + if (bits & 32) { - ton.ton = pattBuff[k++]; - ton.instr = pattBuff[k++]; + tmpNote.note = pattBuff[k++]; + tmpNote.instr = pattBuff[k++]; - if (ton.instr > MAX_INST) - ton.instr = 0; + if (tmpNote.instr > MAX_INST) + tmpNote.instr = 0; - if (ton.ton == 254) ton.ton = 97; - else if (ton.ton == 255) ton.ton = 0; + if (tmpNote.note == 254) tmpNote.note = NOTE_OFF; + else if (tmpNote.note == 255) tmpNote.note = 0; else { - ton.ton = 1 + (ton.ton & 0xF) + (ton.ton >> 4) * 12; - if (ton.ton > 96) - ton.ton = 0; + tmpNote.note = 1 + (tmpNote.note & 0xF) + (tmpNote.note >> 4) * 12; + if (tmpNote.note > 96) + tmpNote.note = 0; } } // volume column - if (typ & 64) + if (bits & 64) { - ton.vol = pattBuff[k++]; + tmpNote.vol = pattBuff[k++]; - if (ton.vol <= 64) - ton.vol += 0x10; + if (tmpNote.vol <= 64) + tmpNote.vol += 0x10; else - ton.vol = 0; + tmpNote.vol = 0; } // effect - if (typ & 128) + if (bits & 128) { - ton.effTyp = pattBuff[k++]; - ton.eff = pattBuff[k++]; + tmpNote.efx = pattBuff[k++]; + tmpNote.efxData = pattBuff[k++]; - if (ton.eff > 0) + if (tmpNote.efxData > 0) { - alastnfo[ii] = ton.eff; - if (ton.effTyp == 8 || ton.effTyp == 21) - alastvibnfo[ii] = ton.eff; // H/U + alastnfo[ii] = tmpNote.efxData; + if (tmpNote.efx == 8 || tmpNote.efx == 21) + alastvibnfo[ii] = tmpNote.efxData; // H/U } // in ST3, a lot of effects directly share the same memory! - if (ton.eff == 0 && ton.effTyp != 7) // G + if (tmpNote.efxData == 0 && tmpNote.efx != 7) // G { - uint8_t efx = ton.effTyp; + uint8_t efx = tmpNote.efx; if (efx == 8 || efx == 21) // H/U - ton.eff = alastvibnfo[ii]; + tmpNote.efxData = alastvibnfo[ii]; else if ((efx >= 4 && efx <= 12) || (efx >= 17 && efx <= 19)) // D/E/F/I/J/K/L/Q/R/S - ton.eff = alastnfo[ii]; + tmpNote.efxData = alastnfo[ii]; /* If effect data is zero and effect type was the same as last one, clear out ** data if it's not J or S (those have no memory in the equivalent XM effects). ** Also goes for extra fine pitch slides and fine volume slides, ** since they get converted to other effects. */ - if (efx == alastefx[ii] && ton.effTyp != 10 && ton.effTyp != 19) // J/S + if (efx == alastefx[ii] && tmpNote.efx != 10 && tmpNote.efx != 19) // J/S { - uint8_t nfo = ton.eff; + uint8_t nfo = tmpNote.efxData; bool extraFinePitchSlides = (efx == 5 || efx == 6) && ((nfo & 0xF0) == 0xE0); bool fineVolSlides = (efx == 4 || efx == 11) && ((nfo > 0xF0) || (((nfo & 0xF) == 0xF) && ((nfo & 0xF0) > 0))); if (!extraFinePitchSlides && !fineVolSlides) - ton.eff = 0; + tmpNote.efxData = 0; } } - if (ton.effTyp > 0) - alastefx[ii] = ton.effTyp; + if (tmpNote.efx > 0) + alastefx[ii] = tmpNote.efx; - switch (ton.effTyp) + switch (tmpNote.efx) { case 1: // A { - ton.effTyp = 0xF; - if (ton.eff == 0) + tmpNote.efx = 0xF; + if (tmpNote.efxData == 0) { - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } - else if (ton.eff > 0x1F) + else if (tmpNote.efxData > 0x1F) { - ton.eff = 0x1F; + tmpNote.efxData = 0x1F; } } break; - case 2: ton.effTyp = 0xB; break; // B - case 3: ton.effTyp = 0xD; break; // C + case 2: tmpNote.efx = 0xB; break; // B + case 3: tmpNote.efx = 0xD; break; // C case 4: // D { - if (ton.eff > 0xF0) // fine slide up + if (tmpNote.efxData > 0xF0) // fine slide up { - ton.effTyp = 0xE; - ton.eff = 0xB0 | (ton.eff & 0xF); + tmpNote.efx = 0xE; + tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF); } - else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down + else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down { - ton.effTyp = 0xE; - ton.eff = 0xA0 | (ton.eff >> 4); + tmpNote.efx = 0xE; + tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4); } else { - ton.effTyp = 0xA; - if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3 - ton.eff &= 0x0F; + tmpNote.efx = 0xA; + if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3 + tmpNote.efxData &= 0x0F; } } break; @@ -325,179 +321,179 @@ bool loadS3M(FILE *f, uint32_t filesize) case 5: // E case 6: // F { - if ((ton.eff & 0xF0) >= 0xE0) + if ((tmpNote.efxData & 0xF0) >= 0xE0) { // convert to fine slide - if ((ton.eff & 0xF0) == 0xE0) + if ((tmpNote.efxData & 0xF0) == 0xE0) tmp = 0x21; else tmp = 0xE; - ton.eff &= 0x0F; + tmpNote.efxData &= 0x0F; - if (ton.effTyp == 0x05) - ton.eff |= 0x20; + if (tmpNote.efx == 0x05) + tmpNote.efxData |= 0x20; else - ton.eff |= 0x10; + tmpNote.efxData |= 0x10; - ton.effTyp = (uint8_t)tmp; + tmpNote.efx = (uint8_t)tmp; - if (ton.effTyp == 0x21 && ton.eff == 0) + if (tmpNote.efx == 0x21 && tmpNote.efxData == 0) { - ton.effTyp = 0; + tmpNote.efx = 0; } } else { // convert to normal 1xx/2xx slide - ton.effTyp = 7 - ton.effTyp; + tmpNote.efx = 7 - tmpNote.efx; } } break; case 7: // G { - ton.effTyp = 0x03; + tmpNote.efx = 0x03; // fix illegal slides (to new instruments) - if (ton.instr != 0 && ton.instr != s3mLastGInstr[ii]) - ton.instr = s3mLastGInstr[ii]; + if (tmpNote.instr != 0 && tmpNote.instr != s3mLastGInstr[ii]) + tmpNote.instr = s3mLastGInstr[ii]; } break; case 11: // K { - if (ton.eff > 0xF0) // fine slide up + if (tmpNote.efxData > 0xF0) // fine slide up { - ton.effTyp = 0xE; - ton.eff = 0xB0 | (ton.eff & 0xF); + tmpNote.efx = 0xE; + tmpNote.efxData = 0xB0 | (tmpNote.efxData & 0xF); // if volume column is unoccupied, set to vibrato - if (ton.vol == 0) - ton.vol = 0xB0; + if (tmpNote.vol == 0) + tmpNote.vol = 0xB0; } - else if ((ton.eff & 0x0F) == 0x0F && (ton.eff & 0xF0) > 0) // fine slide down + else if ((tmpNote.efxData & 0x0F) == 0x0F && (tmpNote.efxData & 0xF0) > 0) // fine slide down { - ton.effTyp = 0xE; - ton.eff = 0xA0 | (ton.eff >> 4); + tmpNote.efx = 0xE; + tmpNote.efxData = 0xA0 | (tmpNote.efxData >> 4); // if volume column is unoccupied, set to vibrato - if (ton.vol == 0) - ton.vol = 0xB0; + if (tmpNote.vol == 0) + tmpNote.vol = 0xB0; } else { - ton.effTyp = 0x6; - if (ton.eff & 0x0F) // on D/K, last nybble has first priority in ST3 - ton.eff &= 0x0F; + tmpNote.efx = 0x6; + if (tmpNote.efxData & 0x0F) // on D/K, last nybble has first priority in ST3 + tmpNote.efxData &= 0x0F; } } break; - case 8: ton.effTyp = 0x04; break; // H - case 9: ton.effTyp = 0x1D; break; // I - case 10: ton.effTyp = 0x00; break; // J - case 12: ton.effTyp = 0x05; break; // L - case 15: ton.effTyp = 0x09; break; // O - case 17: ton.effTyp = 0x1B; break; // Q - case 18: ton.effTyp = 0x07; break; // R + case 8: tmpNote.efx = 0x04; break; // H + case 9: tmpNote.efx = 0x1D; break; // I + case 10: tmpNote.efx = 0x00; break; // J + case 12: tmpNote.efx = 0x05; break; // L + case 15: tmpNote.efx = 0x09; break; // O + case 17: tmpNote.efx = 0x1B; break; // Q + case 18: tmpNote.efx = 0x07; break; // R case 19: // S { - ton.effTyp = 0xE; - tmp = ton.eff >> 4; - ton.eff &= 0x0F; - - if (tmp == 0x1) ton.eff |= 0x30; - else if (tmp == 0x2) ton.eff |= 0x50; - else if (tmp == 0x3) ton.eff |= 0x40; - else if (tmp == 0x4) ton.eff |= 0x70; + tmpNote.efx = 0xE; + tmp = tmpNote.efxData >> 4; + tmpNote.efxData &= 0x0F; + + if (tmp == 0x1) tmpNote.efxData |= 0x30; + else if (tmp == 0x2) tmpNote.efxData |= 0x50; + else if (tmp == 0x3) tmpNote.efxData |= 0x40; + else if (tmp == 0x4) tmpNote.efxData |= 0x70; // ignore S8x becuase it's not compatible with FT2 panning - else if (tmp == 0xB) ton.eff |= 0x60; + else if (tmp == 0xB) tmpNote.efxData |= 0x60; else if (tmp == 0xC) // Note Cut { - ton.eff |= 0xC0; - if (ton.eff == 0xC0) + tmpNote.efxData |= 0xC0; + if (tmpNote.efxData == 0xC0) { // EC0 does nothing in ST3 but cuts voice in FT2, remove effect - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } } else if (tmp == 0xD) // Note Delay { - ton.eff |= 0xD0; - if (ton.ton == 0 || ton.ton == 97) + tmpNote.efxData |= 0xD0; + if (tmpNote.note == 0 || tmpNote.note == NOTE_OFF) { // EDx without a note does nothing in ST3 but retrigs in FT2, remove effect - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } - else if (ton.eff == 0xD0) + else if (tmpNote.efxData == 0xD0) { // ED0 prevents note/smp/vol from updating in ST3, remove everything - ton.ton = 0; - ton.instr = 0; - ton.vol = 0; - ton.effTyp = 0; - ton.eff = 0; + tmpNote.note = 0; + tmpNote.instr = 0; + tmpNote.vol = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } } - else if (tmp == 0xE) ton.eff |= 0xE0; - else if (tmp == 0xF) ton.eff |= 0xF0; + else if (tmp == 0xE) tmpNote.efxData |= 0xE0; + else if (tmp == 0xF) tmpNote.efxData |= 0xF0; else { - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } } break; case 20: // T { - ton.effTyp = 0x0F; - if (ton.eff < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect + tmpNote.efx = 0x0F; + if (tmpNote.efxData < 0x21) // Txx with a value lower than 33 (0x21) does nothing in ST3, remove effect { - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } } break; case 22: // V { - ton.effTyp = 0x10; - if (ton.eff > 0x40) + tmpNote.efx = 0x10; + if (tmpNote.efxData > 0x40) { // Vxx > 0x40 does nothing in ST3 - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } } break; default: { - ton.effTyp = 0; - ton.eff = 0; + tmpNote.efx = 0; + tmpNote.efxData = 0; } break; } } - if (ton.instr != 0 && ton.effTyp != 0x3) - s3mLastGInstr[ii] = ton.instr; + if (tmpNote.instr != 0 && tmpNote.efx != 0x3) + s3mLastGInstr[ii] = tmpNote.instr; - pattTmp[i][(kk * MAX_VOICES) + ii] = ton; + patternTmp[i][(kk * MAX_CHANNELS) + ii] = tmpNote; } } if (tmpPatternEmpty((uint16_t)i)) { - if (pattTmp[i] != NULL) + if (patternTmp[i] != NULL) { - free(pattTmp[i]); - pattTmp[i] = NULL; + free(patternTmp[i]); + patternTmp[i] = NULL; } } } @@ -506,32 +502,34 @@ bool loadS3M(FILE *f, uint32_t filesize) // *** SAMPLES *** bool adlibInsWarn = false; - - memcpy(ptnOfs, ha, 512); - for (i = 0; i < ai; i++) + for (int32_t i = 0; i < hdr.numSamples; i++) { - fseek(f, ptnOfs[i] << 4, SEEK_SET); + if (sampleOffsets[i] == 0) + continue; - if (fread(&h_S3MInstr, 1, sizeof (h_S3MInstr), f) != sizeof (h_S3MInstr)) + fseek(f, sampleOffsets[i], SEEK_SET); + + if (fread(&smpHdr, 1, sizeof (smpHdr), f) != sizeof (smpHdr)) { loaderMsgBox("Not enough memory!"); return false; } - memcpy(songTmp.instrName[1+i], h_S3MInstr.name, 22); + memcpy(songTmp.instrName[1+i], smpHdr.name, 22); - if (h_S3MInstr.typ == 2) + if (smpHdr.type == 2) { adlibInsWarn = true; } - else if (h_S3MInstr.typ == 1) + else if (smpHdr.type == 1) { - if ((h_S3MInstr.flags & (255-1-2-4)) != 0 || h_S3MInstr.pack != 0) + int32_t offsetInFile = ((smpHdr.offsetInFileH << 16) | smpHdr.offsetInFile) << 4; + if ((smpHdr.flags & (255-1-2-4)) != 0 || smpHdr.packFlag != 0) { loaderMsgBox("Error loading .s3m: Incompatible module!"); return false; } - else if (h_S3MInstr.memSeg > 0 && h_S3MInstr.len > 0) + else if (offsetInFile > 0 && smpHdr.length > 0) { if (!allocateTmpInstr((int16_t)(1 + i))) { @@ -540,90 +538,86 @@ bool loadS3M(FILE *f, uint32_t filesize) } setNoEnvelope(instrTmp[1 + i]); - s = &instrTmp[1+i]->samp[0]; + s = &instrTmp[1+i]->smp[0]; - // non-FT2: fixes "miracle man.s3m" and other broken S3Ms - if ((h_S3MInstr.memSeg<<4)+h_S3MInstr.len > (int32_t)filesize) - h_S3MInstr.len = filesize - (h_S3MInstr.memSeg << 4); + if (smpHdr.midCFreq > 65535) // ST3 (and OpenMPT) does this + smpHdr.midCFreq = 65535; - len = h_S3MInstr.len; + memcpy(s->name, smpHdr.name, 22); - bool hasLoop = h_S3MInstr.flags & 1; - bool stereoSample = (h_S3MInstr.flags >> 1) & 1; - bool is16Bit = (h_S3MInstr.flags >> 2) & 1; - - if (is16Bit) // 16-bit - len <<= 1; + // non-FT2: fixes "miracle man.s3m" and other broken S3Ms + if (offsetInFile+smpHdr.length > (int32_t)filesize) + smpHdr.length = filesize - offsetInFile; - if (stereoSample) // stereo - len <<= 1; + bool hasLoop = !!(smpHdr.flags & 1); + bool stereoSample = !!(smpHdr.flags & 2); + bool sample16Bit = !!(smpHdr.flags & 4); - if (!allocateTmpSmpData(s, len)) - { - loaderMsgBox("Not enough memory!"); - return false; - } + if (stereoSample) + smpHdr.length <<= 1; - memcpy(s->name, h_S3MInstr.name, 21); + int32_t lengthInFile = smpHdr.length; - if (h_S3MInstr.c2Spd > 65535) // ST3 (and OpenMPT) does this - h_S3MInstr.c2Spd = 65535; + s->length = smpHdr.length; + s->volume = smpHdr.volume; + s->loopStart = smpHdr.loopStart; + s->loopLength = smpHdr.loopEnd - smpHdr.loopStart; - s->len = h_S3MInstr.len; - s->vol = h_S3MInstr.vol; - s->repS = h_S3MInstr.repS; - s->repL = h_S3MInstr.repE - h_S3MInstr.repS; + tuneSample(s, smpHdr.midCFreq, tmpLinearPeriodsFlag); - tuneSample(s, h_S3MInstr.c2Spd, tmpLinearPeriodsFlag); + if (sample16Bit) + { + s->flags |= SAMPLE_16BIT; + lengthInFile <<= 1; + } - if (s->vol > 64) - s->vol = 64; + if (!allocateSmpData(s, s->length, sample16Bit)) + { + loaderMsgBox("Not enough memory!"); + return false; + } - if (s->repL <= 2 || s->repS+s->repL > s->len || s->repL == 0) + if (s->loopLength <= 1 || s->loopStart+s->loopLength > s->length || s->loopLength == 0) { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; hasLoop = false; } - s->typ = hasLoop + (is16Bit << 4); + if (hasLoop) + s->flags |= LOOP_FWD; - fseek(f, h_S3MInstr.memSeg << 4, SEEK_SET); + fseek(f, offsetInFile, SEEK_SET); - if (ver == 1) + if (hdr.version == 1) { - fseek(f, len, SEEK_CUR); // sample not supported + fseek(f, lengthInFile, SEEK_CUR); // sample not supported } else { - if (fread(s->pek, len, 1, f) != 1) + if (fread(s->dataPtr, SAMPLE_LENGTH_BYTES(s), 1, f) != 1) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - if (is16Bit) - { - conv16BitSample(s->pek, len, stereoSample); - - s->len <<= 1; - s->repS <<= 1; - s->repL <<= 1; - } + if (sample16Bit) + conv16BitSample(s->dataPtr, s->length, stereoSample); else - { - conv8BitSample(s->pek, len, stereoSample); - } + conv8BitSample(s->dataPtr, s->length, stereoSample); // if stereo sample: reduce memory footprint after sample was downmixed to mono if (stereoSample) - reallocateTmpSmpData(s, s->len); + { + s->length >>= 1; + reallocateSmpData(s, s->length, sample16Bit); + } } } } } - songTmp.antChn = countS3MChannels(ap); + songTmp.numChannels = countS3MChannels(hdr.numPatterns); if (adlibInsWarn) loaderMsgBox("Warning: The module contains unsupported AdLib instruments!"); @@ -639,15 +633,15 @@ static int8_t countS3MChannels(uint16_t antPtn) int32_t channels = 0; for (int32_t i = 0; i < antPtn; i++) { - if (pattTmp[i] == NULL) + if (patternTmp[i] == NULL) continue; - tonTyp *ton = pattTmp[i]; + note_t *p = patternTmp[i]; for (int32_t j = 0; j < 64; j++) { - for (int32_t k = 0; k < MAX_VOICES; k++, ton++) + for (int32_t k = 0; k < MAX_CHANNELS; k++, p++) { - if (ton->eff == 0 && ton->effTyp == 0 && ton->instr == 0 && ton->ton == 0 && ton->vol == 0) + if (p->note == 0 && p->instr == 0 && p->vol == 0 && p->efx == 0 && p->efxData == 0) continue; if (k > channels) diff --git a/src/modloaders/ft2_load_stk.c b/src/modloaders/ft2_load_stk.c @@ -1,4 +1,8 @@ -// Ultimate SoundTracker (or compatible) STK loader +/* Ultimate SoundTracker (or compatible) STK loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -13,16 +17,16 @@ #pragma pack(push) #pragma pack(1) #endif -typedef struct songMOD15HeaderTyp_t +typedef struct stkHdr_t { char name[20]; - songMODInstrHeaderTyp instr[15]; - uint8_t len, CIAVal, songTab[128]; + modSmpHdr_t smp[15]; + uint8_t numOrders, CIAVal, orders[128]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songMOD15HeaderTyp; +stkHdr_t; #ifdef _MSC_VER #pragma pack(pop) #endif @@ -31,79 +35,79 @@ bool loadSTK(FILE *f, uint32_t filesize) { uint8_t bytes[4]; int16_t i, j, k; - uint16_t a, b, period; - tonTyp *ton; - sampleTyp *s; - songMOD15HeaderTyp h_MOD15; + uint16_t a, b; + sample_t *s; + stkHdr_t h; tmpLinearPeriodsFlag = false; // use Amiga periods bool veryLateSTKVerFlag = false; // "DFJ SoundTracker III" nad later bool lateSTKVerFlag = false; // "TJC SoundTracker II" and later - if (filesize < sizeof (h_MOD15)) + if (filesize < sizeof (h)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - memset(&h_MOD15, 0, sizeof (songMOD15HeaderTyp)); - if (fread(&h_MOD15, 1, sizeof (h_MOD15), f) != sizeof (h_MOD15)) + memset(&h, 0, sizeof (stkHdr_t)); + if (fread(&h, 1, sizeof (h), f) != sizeof (h)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - if (h_MOD15.CIAVal == 0) // a CIA value of 0 results in 120 - h_MOD15.CIAVal = 120; + if (h.CIAVal == 0) // a CIA value of 0 results in 120 + h.CIAVal = 120; - songTmp.antChn = 4; - songTmp.len = h_MOD15.len; - songTmp.speed = 125; - songTmp.initialTempo = songTmp.tempo = 6; - memcpy(songTmp.songTab, h_MOD15.songTab, 128); - - if (songTmp.len < 1 || songTmp.len > 128 || h_MOD15.CIAVal > 220) + if (h.numOrders < 1 || h.numOrders > 128 || h.CIAVal > 220) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } + memcpy(songTmp.orders, h.orders, 128); + + songTmp.numChannels = 4; + songTmp.songLength = h.numOrders; + songTmp.BPM = 125; + songTmp.speed = 6; + for (a = 0; a < 15; a++) { - songMODInstrHeaderTyp *smp = &h_MOD15.instr[a]; - memcpy(songTmp.instrName[1+a], smp->name, 22); + modSmpHdr_t *modSmp = &h.smp[a]; + memcpy(songTmp.instrName[1+a], modSmp->name, 22); /* Only late versions of Ultimate SoundTracker supports samples larger than 9999 bytes. ** If found, we know for sure that this is a late STK module. */ - const int32_t sampleLen = 2*SWAP16(smp->len); + const int32_t sampleLen = 2*SWAP16(modSmp->length); if (sampleLen > 9999) lateSTKVerFlag = true; } // jjk55.mod by Jesper Kyd has a bogus STK tempo value that should be ignored (hackish!) - if (!strcmp("jjk55", h_MOD15.name)) - h_MOD15.CIAVal = 120; + if (!strcmp("jjk55", h.name)) + h.CIAVal = 120; - if (h_MOD15.CIAVal != 120) // 120 is a special case and means 50Hz (125BPM) + if (h.CIAVal != 120) // 120 is a special case and means 50Hz (125BPM) { // convert UST tempo to BPM - uint16_t ciaPeriod = (240 - h_MOD15.CIAVal) * 122; + uint16_t ciaPeriod = (240 - h.CIAVal) * 122; double dHz = 709379.0 / ciaPeriod; int32_t BPM = (int32_t)((dHz * 2.5) + 0.5); - songTmp.speed = (uint16_t)BPM; + songTmp.BPM = (uint16_t)BPM; } - memcpy(songTmp.name, h_MOD15.name, 20); + memcpy(songTmp.name, h.name, 20); // count number of patterns b = 0; for (a = 0; a < 128; a++) { - if (songTmp.songTab[a] > b) - b = songTmp.songTab[a]; + if (songTmp.orders[a] > b) + b = songTmp.orders[a]; } b++; @@ -117,9 +121,9 @@ bool loadSTK(FILE *f, uint32_t filesize) for (j = 0; j < 64; j++) { - for (k = 0; k < songTmp.antChn; k++) + for (k = 0; k < songTmp.numChannels; k++) { - ton = &pattTmp[a][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k]; if (fread(bytes, 1, 4, f) != 4) { @@ -128,70 +132,70 @@ bool loadSTK(FILE *f, uint32_t filesize) } // period to note - period = ((bytes[0] & 0x0F) << 8) | bytes[1]; + uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1]; for (i = 0; i < 3*12; i++) { if (period >= ptPeriods[i]) { - ton->ton = 1 + (3*12) + (uint8_t)i; + p->note = 1 + (3*12) + (uint8_t)i; break; } } - ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); - ton->effTyp = bytes[2] & 0x0F; - ton->eff = bytes[3]; + p->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4); + p->efx = bytes[2] & 0x0F; + p->efxData = bytes[3]; - if (ton->effTyp == 0xC || ton->effTyp == 0xD || ton->effTyp == 0xE) + if (p->efx == 0xC || p->efx == 0xD || p->efx == 0xE) { // "TJC SoundTracker II" and later lateSTKVerFlag = true; } - if (ton->effTyp == 0xF) + if (p->efx == 0xF) { // "DFJ SoundTracker III" and later lateSTKVerFlag = true; veryLateSTKVerFlag = true; } - if (ton->effTyp == 0xC) + if (p->efx == 0xC) { - if (ton->eff > 64) - ton->eff = 64; + if (p->efxData > 64) + p->efxData = 64; } - else if (ton->effTyp == 0x1) + else if (p->efx == 0x1) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efxData = 0; } - else if (ton->effTyp == 0x2) + else if (p->efx == 0x2) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efxData = 0; } - else if (ton->effTyp == 0x5) + else if (p->efx == 0x5) { - if (ton->eff == 0) - ton->effTyp = 0x3; + if (p->efxData == 0) + p->efxData = 0x3; } - else if (ton->effTyp == 0x6) + else if (p->efx == 0x6) { - if (ton->eff == 0) - ton->effTyp = 0x4; + if (p->efxData == 0) + p->efxData = 0x4; } - else if (ton->effTyp == 0xA) + else if (p->efx == 0xA) { - if (ton->eff == 0) - ton->effTyp = 0; + if (p->efxData == 0) + p->efxData = 0; } - else if (ton->effTyp == 0xE) + else if (p->efx == 0xE) { // check if certain E commands are empty - if (ton->eff == 0x10 || ton->eff == 0x20 || ton->eff == 0xA0 || ton->eff == 0xB0) + if (p->efxData == 0x10 || p->efxData == 0x20 || p->efxData == 0xA0 || p->efxData == 0xB0) { - ton->effTyp = 0; - ton->eff = 0; + p->efx = 0; + p->efxData = 0; } } } @@ -199,10 +203,10 @@ bool loadSTK(FILE *f, uint32_t filesize) if (tmpPatternEmpty(a)) { - if (pattTmp[a] != NULL) + if (patternTmp[a] != NULL) { - free(pattTmp[a]); - pattTmp[a] = NULL; + free(patternTmp[a]); + patternTmp[a] = NULL; } } } @@ -210,14 +214,14 @@ bool loadSTK(FILE *f, uint32_t filesize) // pattern command conversion for non-PT formats for (a = 0; a < b; a++) { - if (pattTmp[a] == NULL) + if (patternTmp[a] == NULL) continue; for (j = 0; j < 64; j++) { - for (k = 0; k < songTmp.antChn; k++) + for (k = 0; k < songTmp.numChannels; k++) { - ton = &pattTmp[a][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[a][(j * MAX_CHANNELS) + k]; // convert STK effects to PT effects @@ -225,24 +229,24 @@ bool loadSTK(FILE *f, uint32_t filesize) { // old SoundTracker 1.x commands - if (ton->effTyp == 1) + if (p->efx == 1) { // arpeggio - ton->effTyp = 0; + p->efx = 0; } - else if (ton->effTyp == 2) + else if (p->efx == 2) { // pitch slide - if (ton->eff & 0xF0) + if (p->efxData & 0xF0) { // pitch slide down - ton->effTyp = 2; - ton->eff >>= 4; + p->efx = 2; + p->efxData >>= 4; } - else if (ton->eff & 0x0F) + else if (p->efxData & 0x0F) { // pitch slide up - ton->effTyp = 1; + p->efx = 1; } } } @@ -250,31 +254,31 @@ bool loadSTK(FILE *f, uint32_t filesize) { // "DFJ SoundTracker II" or later - if (ton->effTyp == 0xD) + if (p->efx == 0xD) { if (veryLateSTKVerFlag) // "DFJ SoundTracker III" or later { // pattern break w/ no param (param must be cleared to fix some songs) - ton->eff = 0; + p->efxData = 0; } else { // volume slide - ton->effTyp = 0xA; + p->efx = 0xA; } } } // effect F with param 0x00 does nothing in UST/STK (I think) - if (ton->effTyp == 0xF && ton->eff == 0) - ton->effTyp = 0; + if (p->efx == 0xF && p->efxData == 0) + p->efx = 0; } } } for (a = 0; a < 15; a++) { - if (h_MOD15.instr[a].len == 0) + if (h.smp[a].length == 0) continue; if (!allocateTmpInstr(1+a)) @@ -285,64 +289,60 @@ bool loadSTK(FILE *f, uint32_t filesize) setNoEnvelope(instrTmp[1+a]); - s = &instrTmp[1+a]->samp[0]; - s->vol = h_MOD15.instr[a].vol; - - s->len = 2 * SWAP16(h_MOD15.instr[a].len); - s->repS = SWAP16(h_MOD15.instr[a].repS); // in STK, loopStart = bytes, not words - s->repL = 2 * SWAP16(h_MOD15.instr[a].repL); - - if (s->vol > 64) - s->vol = 64; + s = &instrTmp[1+a]->smp[0]; + s->volume = h.smp[a].volume; + s->length = 2 * SWAP16(h.smp[a].length); + s->loopStart = SWAP16(h.smp[a].loopStart); // in STK, loopStart = bytes, not words + s->loopLength = 2 * SWAP16(h.smp[a].loopLength); - if (s->repL < 2) - s->repL = 2; + if (s->loopLength < 2) + s->loopLength = 2; // fix overflown loop - if (s->repS+s->repL > s->len) + if (s->loopStart+s->loopLength > s->length) { - if (s->repS >= s->len) + if (s->loopStart >= s->length) { - s->repS = 0; - s->repL = 0; + s->loopStart = 0; + s->loopLength = 0; } else { - s->repL = s->len - s->repS; + s->loopLength = s->length - s->loopStart; } } - if (s->repS+s->repL > 2) + if (s->loopStart+s->loopLength > 2) { - s->typ = 1; // enable loop + s->flags |= LOOP_FWD; // enable loop } else { - s->repL = 0; - s->repS = 0; + s->loopLength = 0; + s->loopStart = 0; } /* In STK, only the loop area of a looped sample is played. ** Skip loading of eventual data present before loop start. */ - if (s->repS > 0 && s->repL < s->len) + if (s->loopStart > 0 && s->loopLength < s->length) { - s->len -= s->repS; - fseek(f, s->repS, SEEK_CUR); - s->repS = 0; + s->length -= s->loopStart; + fseek(f, s->loopStart, SEEK_CUR); + s->loopStart = 0; } - if (!allocateTmpSmpData(s, s->len)) + if (!allocateSmpData(s, s->length, false)) { loaderMsgBox("Not enough memory!"); return false; } - int32_t bytesRead = (int32_t)fread(s->pek, 1, s->len, f); - if (bytesRead < s->len) + int32_t bytesRead = (int32_t)fread(s->dataPtr, 1, s->length, f); + if (bytesRead < s->length) { - int32_t bytesToClear = s->len - bytesRead; - memset(&s->pek[bytesRead], 0, bytesToClear); + int32_t bytesToClear = s->length - bytesRead; + memset(&s->dataPtr[bytesRead], 0, bytesToClear); } } diff --git a/src/modloaders/ft2_load_stm.c b/src/modloaders/ft2_load_stm.c @@ -1,4 +1,8 @@ -// Scream Tracker 2 STM loader +/* Scream Tracker 2 STM loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -13,98 +17,92 @@ #pragma pack(push) #pragma pack(1) #endif -typedef struct songSTMinstrHeaderTyp_t +typedef struct stmSmpHdr_t { char name[12]; - uint8_t nul, insDisk; - uint16_t reserved1, len, repS, repE; - uint8_t vol, reserved2; - uint16_t rate; - int32_t reserved3; + uint8_t nul, junk1; + uint16_t junk2, length, loopStart, loopEnd; + uint8_t volume, junk3; + uint16_t midCFreq; + int32_t junk4; uint16_t paraLen; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songSTMinstrHeaderTyp; +stmSmpHdr_t; -typedef struct songSTMHeaderTyp_t +typedef struct stmHdr_t { char name[20], sig[8]; - uint8_t id1a, typ; + uint8_t x1A, type; uint8_t verMajor, verMinor; - uint8_t tempo, ap, vol, reserved[13]; - songSTMinstrHeaderTyp instr[31]; - uint8_t songTab[128]; + uint8_t tempo, numPatterns, volume, reserved[13]; + stmSmpHdr_t smp[31]; + uint8_t orders[128]; } #ifdef __GNUC__ __attribute__ ((packed)) #endif -songSTMHeaderTyp; +stmHdr_t; #ifdef _MSC_VER #pragma pack(pop) #endif -static const uint8_t stmEff[16] = { 0, 0, 11, 0, 10, 2, 1, 3, 4, 7, 0, 5, 6, 0, 0, 0 }; +static const uint8_t stmEfx[16] = { 0, 0, 11, 0, 10, 2, 1, 3, 4, 7, 0, 5, 6, 0, 0, 0 }; +static uint8_t pattBuff[64*4*4]; -static uint8_t stmTempoToBPM(uint8_t tempo); +static uint16_t stmTempoToBPM(uint8_t tempo); bool loadSTM(FILE *f, uint32_t filesize) { - uint8_t typ, tempo, pattBuff[1024]; - int16_t i, j, k, ap, tmp; - uint16_t a; - tonTyp *ton; - sampleTyp *s; - songSTMHeaderTyp h_STM; + int16_t i, j, k; + stmHdr_t hdr; tmpLinearPeriodsFlag = false; // use Amiga periods - if (filesize < sizeof (h_STM)) + if (filesize < sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - if (fread(&h_STM, 1, sizeof (h_STM), f) != sizeof (h_STM)) + if (fread(&hdr, 1, sizeof (hdr), f) != sizeof (hdr)) { loaderMsgBox("Error: This file is either not a module, or is not supported."); return false; } - if (h_STM.verMinor == 0 || h_STM.typ != 2) + if (hdr.verMinor == 0 || hdr.type != 2) { loaderMsgBox("Error loading STM: Incompatible module!"); return false; } - songTmp.antChn = 4; - memcpy(songTmp.songTab, h_STM.songTab, 128); + songTmp.numChannels = 4; + memcpy(songTmp.orders, hdr.orders, 128); i = 0; - while (i < 128 && songTmp.songTab[i] < 99) i++; - songTmp.len = i + (i == 0); + while (i < 128 && songTmp.orders[i] < 99) + i++; + songTmp.songLength = i + (i == 0); - if (songTmp.len < 255) - memset(&songTmp.songTab[songTmp.len], 0, 256 - songTmp.len); + if (songTmp.songLength < 255) + memset(&songTmp.orders[songTmp.songLength], 0, 256 - songTmp.songLength); - memcpy(songTmp.name, h_STM.name, 20); + memcpy(songTmp.name, hdr.name, 20); - tempo = h_STM.tempo; - if (h_STM.verMinor < 21) + uint8_t tempo = hdr.tempo; + if (hdr.verMinor < 21) tempo = ((tempo / 10) << 4) + (tempo % 10); if (tempo == 0) tempo = 96; - songTmp.initialTempo = songTmp.tempo = CLAMP(h_STM.tempo >> 4, 1, 31); - songTmp.speed = stmTempoToBPM(tempo); + songTmp.BPM = stmTempoToBPM(tempo); + songTmp.speed = hdr.tempo >> 4; - if (h_STM.verMinor > 10) - songTmp.globVol = MIN(h_STM.vol, 64); - - ap = h_STM.ap; - for (i = 0; i < ap; i++) + for (i = 0; i < hdr.numPatterns; i++) { if (!allocateTmpPatt(i, 64)) { @@ -118,123 +116,118 @@ bool loadSTM(FILE *f, uint32_t filesize) return false; } - a = 0; + uint8_t *pattPtr = pattBuff; for (j = 0; j < 64; j++) { - for (k = 0; k < 4; k++) + for (k = 0; k < 4; k++, pattPtr += 4) { - ton = &pattTmp[i][(j * MAX_VOICES) + k]; + note_t *p = &patternTmp[i][(j * MAX_CHANNELS) + k]; - if (pattBuff[a] == 254) + if (pattPtr[0] == 254) { - ton->ton = 97; + p->note = NOTE_OFF; } - else if (pattBuff[a] < 96) + else if (pattPtr[0] < 96) { - ton->ton = (12 * (pattBuff[a] >> 4)) + (25 + (pattBuff[a] & 0x0F)); - if (ton->ton > 96) - ton->ton = 0; + p->note = (12 * (pattPtr[0] >> 4)) + (25 + (pattPtr[0] & 0x0F)); + if (p->note > 96) + p->note = 0; } else { - ton->ton = 0; + p->note = 0; } - ton->instr = pattBuff[a + 1] >> 3; - typ = (pattBuff[a + 1] & 7) + ((pattBuff[a + 2] & 0xF0) >> 1); - if (typ <= 64) - ton->vol = typ + 0x10; + p->instr = pattPtr[1] >> 3; + + uint8_t vol = (pattPtr[1] & 7) + ((pattPtr[2] & 0xF0) >> 1); + if (vol <= 64) + p->vol = 0x10 + vol; - ton->eff = pattBuff[a + 3]; + p->efxData = pattPtr[3]; - tmp = pattBuff[a + 2] & 0x0F; - if (tmp == 1) + uint8_t efx = pattPtr[2] & 0x0F; + if (efx == 1) { - ton->effTyp = 15; + p->efx = 15; - if (h_STM.verMinor < 21) - ton->eff = ((ton->eff / 10) << 4) + (ton->eff % 10); + if (hdr.verMinor < 21) + p->efxData = ((p->efxData / 10) << 4) + (p->efxData % 10); - ton->eff >>= 4; + p->efxData >>= 4; } - else if (tmp == 3) + else if (efx == 3) { - ton->effTyp = 13; - ton->eff = 0; + p->efx = 13; + p->efxData = 0; } - else if (tmp == 2 || (tmp >= 4 && tmp <= 12)) + else if (efx == 2 || (efx >= 4 && efx <= 12)) { - ton->effTyp = stmEff[tmp]; - if (ton->effTyp == 0xA) + p->efx = stmEfx[efx]; + if (p->efx == 0xA) { - if (ton->eff & 0x0F) - ton->eff &= 0x0F; + if (p->efxData & 0x0F) + p->efxData &= 0x0F; else - ton->eff &= 0xF0; + p->efxData &= 0xF0; } } else { - ton->eff = 0; + p->efxData = 0; } - - a += 4; } } if (tmpPatternEmpty(i)) { - if (pattTmp[i] != NULL) + if (patternTmp[i] != NULL) { - free(pattTmp[i]); - pattTmp[i] = NULL; + free(patternTmp[i]); + patternTmp[i] = NULL; } } } for (i = 0; i < 31; i++) { - memcpy(&songTmp.instrName[1+i], h_STM.instr[i].name, 12); + memcpy(&songTmp.instrName[1+i], hdr.smp[i].name, 12); - if (h_STM.instr[i].len != 0 && h_STM.instr[i].reserved1 != 0) + if (hdr.smp[i].length > 0) { allocateTmpInstr(1 + i); setNoEnvelope(instrTmp[i]); - s = &instrTmp[1+i]->samp[0]; + sample_t *s = &instrTmp[1+i]->smp[0]; - if (!allocateTmpSmpData(s, h_STM.instr[i].len)) + if (!allocateSmpData(s, hdr.smp[i].length, false)) { loaderMsgBox("Not enough memory!"); return false; } - s->len = h_STM.instr[i].len; - s->vol = h_STM.instr[i].vol; - s->repS = h_STM.instr[i].repS; - s->repL = h_STM.instr[i].repE - h_STM.instr[i].repS; + s->length = hdr.smp[i].length; + s->volume = hdr.smp[i].volume; + s->loopStart = hdr.smp[i].loopStart; + s->loopLength = hdr.smp[i].loopEnd - hdr.smp[i].loopStart; - memcpy(s->name, h_STM.instr[i].name, 12); - tuneSample(s, h_STM.instr[i].rate, tmpLinearPeriodsFlag); + memcpy(s->name, hdr.smp[i].name, 12); + tuneSample(s, hdr.smp[i].midCFreq, tmpLinearPeriodsFlag); - if (s->repS < s->len && h_STM.instr[i].repE > s->repS && h_STM.instr[i].repE != 0xFFFF) + if (s->loopStart < s->length && hdr.smp[i].loopEnd > s->loopStart && hdr.smp[i].loopEnd != 0xFFFF) { - if (s->repS+s->repL > s->len) - s->repL = s->len - s->repS; + if (s->loopStart+s->loopLength > s->length) + s->loopLength = s->length - s->loopStart; - s->typ = 1; // enable loop + s->flags |= LOOP_FWD; // enable loop } else { - s->repS = 0; - s->repL = 0; - s->typ = 0; + s->loopStart = 0; + s->loopLength = 0; } - if (s->vol > 64) - s->vol = 64; - - if (fread(s->pek, s->len, 1, f) != 1) + if (fread(s->dataPtr, s->length, 1, f) != 1) { loaderMsgBox("General I/O error during loading! Possibly corrupt module?"); return false; @@ -245,13 +238,13 @@ bool loadSTM(FILE *f, uint32_t filesize) return true; } -static uint8_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replayer code +static uint16_t stmTempoToBPM(uint8_t tempo) // ported from original ST2.3 replayer code { const uint8_t slowdowns[16] = { 140, 50, 25, 15, 10, 7, 6, 4, 3, 3, 2, 2, 2, 2, 1, 1 }; uint16_t hz = 50; hz -= ((slowdowns[tempo >> 4] * (tempo & 15)) >> 4); // can and will underflow - const uint32_t bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5 - return (uint8_t)CLAMP(bpm, 32, 255); // result can be slightly off, but close enough... + const uint16_t bpm = (hz << 1) + (hz >> 1); // BPM = hz * 2.5 + return bpm; // result can be slightly off, but close enough... } diff --git a/src/modloaders/ft2_load_xm.c b/src/modloaders/ft2_load_xm.c @@ -1,4 +1,8 @@ -// Fasttracker II (or compatible) XM loader +/* Fasttracker II (or compatible) XM loader +** +** Note: Data sanitation is done in the last stage +** of module loading, so you don't need to do that here. +*/ #include <stdio.h> #include <stdint.h> @@ -20,14 +24,13 @@ static uint32_t extraSampleLengths[32-MAX_SMP_PER_INST]; static bool loadInstrHeader(FILE *f, uint16_t i); static bool loadInstrSample(FILE *f, uint16_t i); static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); -static bool loadPatterns(FILE *f, uint16_t antPtn); +static bool loadPatterns(FILE *f, uint16_t antPtn, uint16_t xmVersion); static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn); -static void sanitizeInstrument(instrTyp *ins); +static void loadADPCMSample(FILE *f, sample_t *s); // ModPlug Tracker bool loadXM(FILE *f, uint32_t filesize) { - uint16_t i; - songHeaderTyp h; + xmHdr_t h; if (filesize < sizeof (h)) { @@ -41,31 +44,31 @@ bool loadXM(FILE *f, uint32_t filesize) return false; } - if (h.ver < 0x0102 || h.ver > 0x0104) + if (h.version < 0x0102 || h.version > 0x0104) { - loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.ver >> 8) & 15, h.ver & 0xFF); + loaderMsgBox("Error loading XM: Unsupported file version (v%01X.%02X).", (h.version >> 8) & 15, h.version & 0xFF); return false; } - if (h.len > MAX_ORDERS) + if (h.numOrders > MAX_ORDERS) { loaderMsgBox("Error loading XM: The song has more than 256 orders!"); return false; } - if (h.antPtn > MAX_PATTERNS) + if (h.numPatterns > MAX_PATTERNS) { loaderMsgBox("Error loading XM: The song has more than 256 patterns!"); return false; } - if (h.antChn == 0) + if (h.numChannels == 0) { loaderMsgBox("Error loading XM: This file is corrupt."); return false; } - if (h.antInstrs > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them + if (h.numInstr > 256) // if >128 instruments, we fake-load up to 128 extra instruments and discard them { loaderMsgBox("Error loading XM: This file is corrupt."); return false; @@ -79,52 +82,48 @@ bool loadXM(FILE *f, uint32_t filesize) } memcpy(songTmp.name, h.name, 20); + songTmp.name[20] = '\0'; - songTmp.len = h.len; - songTmp.repS = h.repS; - songTmp.antChn = (uint8_t)h.antChn; - songTmp.speed = h.defSpeed; - songTmp.tempo = h.defTempo; - songTmp.ver = h.ver; + songTmp.songLength = h.numOrders; + songTmp.songLoopStart = h.songLoopStart; + songTmp.numChannels = (uint8_t)h.numChannels; + songTmp.BPM = h.BPM; + songTmp.speed = h.speed; tmpLinearPeriodsFlag = h.flags & 1; - // non-FT2: clamp to max numbers that are okay for GUI - songTmp.speed = CLAMP(songTmp.speed, 1, 999); - songTmp.initialTempo = songTmp.tempo = CLAMP(songTmp.tempo, 1, 99); - - if (songTmp.len == 0) - songTmp.len = 1; // songTmp.songTab is already empty + if (songTmp.songLength == 0) + songTmp.songLength = 1; // songTmp.songTab is already empty else - memcpy(songTmp.songTab, h.songTab, songTmp.len); + memcpy(songTmp.orders, h.orders, songTmp.songLength); // some strange XMs have the order list padded with 0xFF, remove them! for (int16_t j = 255; j >= 0; j--) { - if (songTmp.songTab[j] != 0xFF) + if (songTmp.orders[j] != 0xFF) break; - if (songTmp.len > j) - songTmp.len = j; + if (songTmp.songLength > j) + songTmp.songLength = j; } // even though XM supports 256 orders, FT2 supports only 255... - if (songTmp.len > 0xFF) - songTmp.len = 0xFF; + if (songTmp.songLength > 255) + songTmp.songLength = 255; - if (songTmp.ver < 0x0104) + if (h.version < 0x0104) { // XM v1.02 and XM v1.03 - for (i = 1; i <= h.antInstrs; i++) + for (uint16_t i = 1; i <= h.numInstr; i++) { if (!loadInstrHeader(f, i)) return false; } - if (!loadPatterns(f, h.antPtn)) + if (!loadPatterns(f, h.numPatterns, h.version)) return false; - for (i = 1; i <= h.antInstrs; i++) + for (uint16_t i = 1; i <= h.numInstr; i++) { if (!loadInstrSample(f, i)) return false; @@ -134,10 +133,10 @@ bool loadXM(FILE *f, uint32_t filesize) { // XM v1.04 (latest version) - if (!loadPatterns(f, h.antPtn)) + if (!loadPatterns(f, h.numPatterns, h.version)) return false; - for (i = 1; i <= h.antInstrs; i++) + for (uint16_t i = 1; i <= h.numInstr; i++) { if (!loadInstrHeader(f, i)) return false; @@ -148,9 +147,9 @@ bool loadXM(FILE *f, uint32_t filesize) } // if we temporarily loaded more than 128 instruments, clear the extra allocated memory - if (h.antInstrs > MAX_INST) + if (h.numInstr > MAX_INST) { - for (i = MAX_INST+1; i <= h.antInstrs; i++) + for (int32_t i = MAX_INST+1; i <= h.numInstr; i++) { if (instrTmp[i] != NULL) { @@ -165,22 +164,22 @@ bool loadXM(FILE *f, uint32_t filesize) ** back to max 16 in the headers before loading is done. */ bool instrHasMoreThan16Samples = false; - for (i = 1; i <= MAX_INST; i++) + for (int32_t i = 1; i <= MAX_INST; i++) { - if (instrTmp[i] != NULL && instrTmp[i]->antSamp > MAX_SMP_PER_INST) + if (instrTmp[i] != NULL && instrTmp[i]->numSamples > MAX_SMP_PER_INST) { instrHasMoreThan16Samples = true; - instrTmp[i]->antSamp = MAX_SMP_PER_INST; + instrTmp[i]->numSamples = MAX_SMP_PER_INST; } } - if (songTmp.antChn > MAX_VOICES) + if (songTmp.numChannels > MAX_CHANNELS) { - songTmp.antChn = MAX_VOICES; + songTmp.numChannels = MAX_CHANNELS; loaderMsgBox("Warning: Module contains >32 channels. The extra channels will be discarded!"); } - if (h.antInstrs > MAX_INST) + if (h.numInstr > MAX_INST) loaderMsgBox("Warning: Module contains >128 instruments. The extra instruments will be discarded!"); if (instrHasMoreThan16Samples) @@ -191,37 +190,35 @@ bool loadXM(FILE *f, uint32_t filesize) static bool loadInstrHeader(FILE *f, uint16_t i) { - uint8_t j; uint32_t readSize; - instrHeaderTyp ih; - instrTyp *ins; - sampleHeaderTyp *src; - sampleTyp *s; + xmInsHdr_t ih; + instr_t *ins; + xmSmpHdr_t *src; + sample_t *s; memset(extraSampleLengths, 0, sizeof (extraSampleLengths)); memset(&ih, 0, sizeof (ih)); - fread(&ih.instrSize, 4, 1, f); - readSize = ih.instrSize; + fread(&readSize, 4, 1, f); + fseek(f, -4, SEEK_CUR); // yes, some XMs can have a header size of 0, and it usually means 263 bytes (INSTR_HEADER_SIZE) if (readSize == 0 || readSize > INSTR_HEADER_SIZE) readSize = INSTR_HEADER_SIZE; - if (readSize < 4) + if ((int32_t)readSize < 0) { - loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!"); + loaderMsgBox("Error loading XM: This file is corrupt!"); return false; } - // load instrument data into temp buffer - fread(ih.name, readSize-4, 1, f); // -4 = skip ih.instrSize + fread(&ih, readSize, 1, f); // read instrument header // FT2 bugfix: skip instrument header data if instrSize is above INSTR_HEADER_SIZE if (ih.instrSize > INSTR_HEADER_SIZE) fseek(f, ih.instrSize-INSTR_HEADER_SIZE, SEEK_CUR); - if (ih.antSamp < 0 || ih.antSamp > 32) + if (ih.numSamples < 0 || ih.numSamples > 32) { loaderMsgBox("Error loading XM: This file is corrupt (or not supported)!"); return false; @@ -230,7 +227,7 @@ static bool loadInstrHeader(FILE *f, uint16_t i) if (i <= MAX_INST) // copy over instrument names memcpy(songTmp.instrName[i], ih.name, 22); - if (ih.antSamp > 0) + if (ih.numSamples > 0 && ih.numSamples <= 32) { if (!allocateTmpInstr(i)) { @@ -241,78 +238,77 @@ static bool loadInstrHeader(FILE *f, uint16_t i) // copy instrument header elements to our instrument struct ins = instrTmp[i]; - memcpy(ins->ta, ih.ta, 96); - memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t)); - memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t)); - ins->envVPAnt = ih.envVPAnt; - ins->envPPAnt = ih.envPPAnt; - ins->envVSust = ih.envVSust; - ins->envVRepS = ih.envVRepS; - ins->envVRepE = ih.envVRepE; - ins->envPSust = ih.envPSust; - ins->envPRepS = ih.envPRepS; - ins->envPRepE = ih.envPRepE; - ins->envVTyp = ih.envVTyp; - ins->envPTyp = ih.envPTyp; - ins->vibTyp = ih.vibTyp; + memcpy(ins->note2SampleLUT, ih.note2SampleLUT, 96); + memcpy(ins->volEnvPoints, ih.volEnvPoints, 12*2*sizeof(int16_t)); + memcpy(ins->panEnvPoints, ih.panEnvPoints, 12*2*sizeof(int16_t)); + ins->volEnvLength = ih.volEnvLength; + ins->panEnvLength = ih.panEnvLength; + ins->volEnvSustain = ih.volEnvSustain; + ins->volEnvLoopStart = ih.volEnvLoopStart; + ins->volEnvLoopEnd = ih.volEnvLoopEnd; + ins->panEnvSustain = ih.panEnvSustain; + ins->panEnvLoopStart = ih.panEnvLoopStart; + ins->panEnvLoopEnd = ih.panEnvLoopEnd; + ins->volEnvFlags = ih.volEnvFlags; + ins->panEnvFlags = ih.panEnvFlags; + ins->vibType = ih.vibType; ins->vibSweep = ih.vibSweep; ins->vibDepth = ih.vibDepth; ins->vibRate = ih.vibRate; - ins->fadeOut = ih.fadeOut; + ins->fadeout = ih.fadeout; ins->midiOn = (ih.midiOn == 1) ? true : false; ins->midiChannel = ih.midiChannel; ins->midiProgram = ih.midiProgram; ins->midiBend = ih.midiBend; ins->mute = (ih.mute == 1) ? true : false; // correct logic, don't change this! - ins->antSamp = ih.antSamp; // used in loadInstrSample() + ins->numSamples = ih.numSamples; // used in loadInstrSample() - sanitizeInstrument(ins); - - int32_t sampleHeadersToRead = ih.antSamp; + int32_t sampleHeadersToRead = ih.numSamples; if (sampleHeadersToRead > MAX_SMP_PER_INST) sampleHeadersToRead = MAX_SMP_PER_INST; - if (fread(ih.samp, sampleHeadersToRead * sizeof (sampleHeaderTyp), 1, f) != 1) + if (fread(ih.smp, sampleHeadersToRead * sizeof (xmSmpHdr_t), 1, f) != 1) { loaderMsgBox("General I/O error during loading!"); return false; } // if instrument contains more than 16 sample headers (unsupported), skip them - if (ih.antSamp > MAX_SMP_PER_INST) // can only be 0..32 at this point + if (ih.numSamples > MAX_SMP_PER_INST) // can only be 0..32 at this point { - const int32_t samplesToSkip = ih.antSamp-MAX_SMP_PER_INST; - for (j = 0; j < samplesToSkip; j++) + const int32_t samplesToSkip = ih.numSamples-MAX_SMP_PER_INST; + for (int32_t j = 0; j < samplesToSkip; j++) { fread(&extraSampleLengths[j], 4, 1, f); // used for skipping data in loadInstrSample() - fseek(f, sizeof (sampleHeaderTyp)-4, SEEK_CUR); + fseek(f, sizeof (xmSmpHdr_t)-4, SEEK_CUR); } } - for (j = 0; j < sampleHeadersToRead; j++) + for (int32_t j = 0; j < sampleHeadersToRead; j++) { - s = &instrTmp[i]->samp[j]; - src = &ih.samp[j]; + s = &instrTmp[i]->smp[j]; + src = &ih.smp[j]; // copy sample header elements to our sample struct - s->len = src->len; - s->repS = src->repS; - s->repL = src->repL; - s->vol = src->vol; - s->fine = src->fine; - s->typ = src->typ; - s->pan = src->pan; - s->relTon = src->relTon; - memcpy(s->name, src->name, 22); - - // dst->pek is set up later + s->length = src->length; + s->loopStart = src->loopStart; + s->loopLength = src->loopLength; + s->volume = src->volume; + s->finetune = src->finetune; + s->flags = src->flags; + s->panning = src->panning; + s->relativeNote = src->relativeNote; + + /* If the sample is 8-bit mono and nameLength (reserved) is 0xAD, + ** then this is a 4-bit ADPCM compressed sample (ModPlug Tracker). + */ + if (src->nameLength == 0xAD && !(src->flags & (SAMPLE_16BIT | SAMPLE_STEREO))) + s->flags |= SAMPLE_ADPCM; - // sanitize stuff broken/unsupported samples (FT2 doesn't do this, but we do!) - if (s->vol > 64) - s->vol = 64; + memcpy(s->name, src->name, 22); - s->relTon = CLAMP(s->relTon, -48, 71); + // dst->dataPtr is set up later } } @@ -324,90 +320,90 @@ static bool loadInstrSample(FILE *f, uint16_t i) if (instrTmp[i] == NULL) return true; // empty instrument, let's just pretend it got loaded successfully - uint16_t k = instrTmp[i]->antSamp; + uint16_t k = instrTmp[i]->numSamples; if (k > MAX_SMP_PER_INST) k = MAX_SMP_PER_INST; - sampleTyp *s = instrTmp[i]->samp; + sample_t *s = instrTmp[i]->smp; if (i > MAX_INST) // insNum > 128, just skip sample data { for (uint16_t j = 0; j < k; j++, s++) { - if (s->len > 0) - fseek(f, s->len, SEEK_CUR); + if (s->length > 0) + fseek(f, s->length, SEEK_CUR); } } else { for (uint16_t j = 0; j < k; j++, s++) { - // FT2: a sample with both forward loop and pingpong loop set results in pingpong - if ((s->typ & 3) == 3) - s->typ &= 0xFE; // remove forward loop flag - - int32_t l = s->len; - if (l <= 0) + if (s->length <= 0) { - s->len = 0; - s->repL = 0; - s->repS = 0; - - if (s->typ & 32) // remove stereo flag if present - s->typ &= ~32; + s->length = 0; + s->loopStart = 0; + s->loopLength = 0; + s->flags = 0; } else { - int32_t bytesToSkip = 0; - if (l > MAX_SAMPLE_LEN) + const int32_t lengthInFile = s->length; + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + bool stereoSample = !!(s->flags & SAMPLE_STEREO); + bool adpcmSample = !!(s->flags & SAMPLE_ADPCM); // ModPlug Tracker + + if (sample16Bit) // we use units of samples (not bytes like in FT2) { - bytesToSkip = l - MAX_SAMPLE_LEN; - l = MAX_SAMPLE_LEN; + s->length >>= 1; + s->loopStart >>= 1; + s->loopLength >>= 1; } - if (!allocateTmpSmpData(s, l)) + if (s->length > MAX_SAMPLE_LEN) + s->length = MAX_SAMPLE_LEN; + + if (!allocateSmpData(s, s->length, sample16Bit)) { loaderMsgBox("Not enough memory!"); return false; } - const int32_t bytesRead = (int32_t)fread(s->pek, 1, l, f); - if (bytesRead < l) + if (adpcmSample) { - const int32_t bytesToClear = l - bytesRead; - memset(&s->pek[bytesRead], 0, bytesToClear); + loadADPCMSample(f, s); } + else + { + const int32_t sampleLengthInBytes = SAMPLE_LENGTH_BYTES(s); + fread(s->dataPtr, 1, sampleLengthInBytes, f); - if (bytesToSkip > 0) - fseek(f, bytesToSkip, SEEK_CUR); - - delta2Samp(s->pek, l, s->typ); + if (sampleLengthInBytes < lengthInFile) + fseek(f, lengthInFile-sampleLengthInBytes, SEEK_CUR); - if (s->typ & 32) // stereo sample - already downmixed to mono in delta2samp() - { - s->typ &= ~32; // remove stereo flag + delta2Samp(s->dataPtr, s->length, s->flags); - s->len >>= 1; - s->repL >>= 1; - s->repS >>= 1; + if (stereoSample) // stereo sample - already downmixed to mono in delta2samp() + { + s->length >>= 1; + s->loopStart >>= 1; + s->loopLength >>= 1; - reallocateTmpSmpData(s, s->len); // dealloc unused memory + reallocateSmpData(s, s->length, sample16Bit); // dealloc unused memory + } } } - // NON-FT2 FIX: Align to 2-byte if 16-bit sample - if (s->typ & 16) - { - s->repL &= 0xFFFFFFFE; - s->repS &= 0xFFFFFFFE; - s->len &= 0xFFFFFFFE; - } + // remove stereo flag if present (already handled) + if (s->flags & SAMPLE_STEREO) + s->flags &= ~SAMPLE_STEREO; } } - if (instrTmp[i]->antSamp > MAX_SMP_PER_INST) + // skip sample headers if we have more than 16 samples in instrument + if (instrTmp[i]->numSamples > MAX_SMP_PER_INST) { - const int32_t samplesToSkip = instrTmp[i]->antSamp-MAX_SMP_PER_INST; + const int32_t samplesToSkip = instrTmp[i]->numSamples-MAX_SMP_PER_INST; for (i = 0; i < samplesToSkip; i++) { if (extraSampleLengths[i] > 0) @@ -418,80 +414,80 @@ static bool loadInstrSample(FILE *f, uint16_t i) return true; } -static bool loadPatterns(FILE *f, uint16_t antPtn) +static bool loadPatterns(FILE *f, uint16_t antPtn, uint16_t xmVersion) { uint8_t tmpLen; - patternHeaderTyp ph; + xmPatHdr_t ph; bool pattLenWarn = false; for (uint16_t i = 0; i < antPtn; i++) { - if (fread(&ph.patternHeaderSize, 4, 1, f) != 1) + if (fread(&ph.headerSize, 4, 1, f) != 1) goto pattCorrupt; - if (fread(&ph.typ, 1, 1, f) != 1) + if (fread(&ph.type, 1, 1, f) != 1) goto pattCorrupt; - ph.pattLen = 0; - if (songTmp.ver == 0x0102) + ph.numRows = 0; + if (xmVersion == 0x0102) { if (fread(&tmpLen, 1, 1, f) != 1) goto pattCorrupt; - if (fread(&ph.dataLen, 2, 1, f) != 1) + if (fread(&ph.dataSize, 2, 1, f) != 1) goto pattCorrupt; - ph.pattLen = tmpLen + 1; // +1 in v1.02 + ph.numRows = tmpLen + 1; // +1 in v1.02 - if (ph.patternHeaderSize > 8) - fseek(f, ph.patternHeaderSize - 8, SEEK_CUR); + if (ph.headerSize > 8) + fseek(f, ph.headerSize - 8, SEEK_CUR); } else { - if (fread(&ph.pattLen, 2, 1, f) != 1) + if (fread(&ph.numRows, 2, 1, f) != 1) goto pattCorrupt; - if (fread(&ph.dataLen, 2, 1, f) != 1) + if (fread(&ph.dataSize, 2, 1, f) != 1) goto pattCorrupt; - if (ph.patternHeaderSize > 9) - fseek(f, ph.patternHeaderSize - 9, SEEK_CUR); + if (ph.headerSize > 9) + fseek(f, ph.headerSize - 9, SEEK_CUR); } if (feof(f)) goto pattCorrupt; - pattLensTmp[i] = ph.pattLen; - if (pattLensTmp[i] > MAX_PATT_LEN) + patternNumRowsTmp[i] = ph.numRows; + if (patternNumRowsTmp[i] > MAX_PATT_LEN) { - pattLensTmp[i] = MAX_PATT_LEN; + patternNumRowsTmp[i] = MAX_PATT_LEN; pattLenWarn = true; } - if (ph.dataLen > 0) + if (ph.dataSize > 0) { - if (!allocateTmpPatt(i, pattLensTmp[i])) + if (!allocateTmpPatt(i, patternNumRowsTmp[i])) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(packedPattData, 1, ph.dataLen, f) != ph.dataLen) + if (fread(packedPattData, 1, ph.dataSize, f) != ph.dataSize) goto pattCorrupt; - unpackPatt((uint8_t *)pattTmp[i], packedPattData, pattLensTmp[i], songTmp.antChn); - clearUnusedChannels(pattTmp[i], pattLensTmp[i], songTmp.antChn); + unpackPatt((uint8_t *)patternTmp[i], packedPattData, patternNumRowsTmp[i], songTmp.numChannels); + clearUnusedChannels(patternTmp[i], patternNumRowsTmp[i], songTmp.numChannels); } if (tmpPatternEmpty(i)) { - if (pattTmp[i] != NULL) + if (patternTmp[i] != NULL) { - free(pattTmp[i]); - pattTmp[i] = NULL; + free(patternTmp[i]); + patternTmp[i] = NULL; } - pattLensTmp[i] = 64; + patternNumRowsTmp[i] = 64; } } @@ -512,14 +508,14 @@ static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) if (dst == NULL) return; - const int32_t srcEnd = len * (sizeof (tonTyp) * antChn); + const int32_t srcEnd = len * (sizeof (note_t) * antChn); int32_t srcIdx = 0; int32_t numChannels = antChn; - if (numChannels > MAX_VOICES) - numChannels = MAX_VOICES; + if (numChannels > MAX_CHANNELS) + numChannels = MAX_CHANNELS; - const int32_t pitch = sizeof (tonTyp) * (MAX_VOICES - antChn); + const int32_t pitch = sizeof (note_t) * (MAX_CHANNELS - antChn); for (int32_t i = 0; i < len; i++) { for (j = 0; j < numChannels; j++) @@ -545,7 +541,7 @@ static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) *dst++ = *src++; } - srcIdx += sizeof (tonTyp); + srcIdx += sizeof (note_t); } // if more than 32 channels, skip rest of the channels for this row @@ -571,45 +567,32 @@ static void unpackPatt(uint8_t *dst, uint8_t *src, uint16_t len, int32_t antChn) src++; } - srcIdx += sizeof (tonTyp); + srcIdx += sizeof (note_t); } // if song has <32 channels, align pointer to next row (skip unused channels) - if (antChn < MAX_VOICES) + if (antChn < MAX_CHANNELS) dst += pitch; } } -static void sanitizeInstrument(instrTyp *ins) // FT2 doesn't do this, but we do! +static void loadADPCMSample(FILE *f, sample_t *s) // ModPlug Tracker { - ins->midiProgram = CLAMP(ins->midiProgram, 0, 127); - ins->midiBend = CLAMP(ins->midiBend, 0, 36); + int8_t deltaLUT[16]; + fread(deltaLUT, 1, 16, f); - if (ins->midiChannel > 15) ins->midiChannel = 15; - if (ins->vibDepth > 0x0F) ins->vibDepth = 0x0F; - if (ins->vibRate > 0x3F) ins->vibRate = 0x3F; - if (ins->vibTyp > 3) ins->vibTyp = 0; + int8_t *dataPtr = s->dataPtr; + const int32_t dataLength = (s->length + 1) / 2; - for (int32_t i = 0; i < 96; i++) + int8_t currSample = 0; + for (int32_t i = 0; i < dataLength; i++) { - if (ins->ta[i] >= MAX_SMP_PER_INST) - ins->ta[i] = MAX_SMP_PER_INST-1; - } + const uint8_t nibbles = (uint8_t)fgetc(f); - if (ins->envVPAnt > 12) ins->envVPAnt = 12; - if (ins->envVRepS > 11) ins->envVRepS = 11; - if (ins->envVRepE > 11) ins->envVRepE = 11; - if (ins->envVSust > 11) ins->envVSust = 11; - if (ins->envPPAnt > 12) ins->envPPAnt = 12; - if (ins->envPRepS > 11) ins->envPRepS = 11; - if (ins->envPRepE > 11) ins->envPRepE = 11; - if (ins->envPSust > 11) ins->envPSust = 11; + currSample += deltaLUT[nibbles & 0x0F]; + *dataPtr++ = currSample; - for (int32_t i= 0; i < 12; i++) - { - if ((uint16_t)ins->envVP[i][0] > 32767) ins->envVP[i][0] = 32767; - if ((uint16_t)ins->envPP[i][0] > 32767) ins->envPP[i][0] = 32767; - if ((uint16_t)ins->envVP[i][1] > 64) ins->envVP[i][1] = 64; - if ((uint16_t)ins->envPP[i][1] > 63) ins->envPP[i][1] = 63; + currSample += deltaLUT[nibbles >> 4]; + *dataPtr++ = currSample; } } diff --git a/src/rtmidi/rtmidi_c.cpp b/src/rtmidi/rtmidi_c.cpp @@ -3,6 +3,11 @@ #include "rtmidi_c.h" #include "RtMidi.h" +// 8bb: hide POSIX warnings +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + /* Compile-time assertions that will break if the enums are changed in * the future without synchronizing them properly. If you get (g++) * "error: ‘StaticAssert<b>::StaticAssert() [with bool b = false]’ is @@ -128,7 +133,7 @@ unsigned int rtmidi_get_port_count (RtMidiPtr device) } catch (const RtMidiError & err) { device->ok = false; device->msg = err.what (); - return -1; + return (unsigned int)-1; } } diff --git a/src/scopes/ft2_scope_macros.h b/src/scopes/ft2_scope_macros.h @@ -0,0 +1,256 @@ +#pragma once + +#include <stdint.h> +#include "ft2_scopes.h" + +/* ----------------------------------------------------------------------- */ +/* SCOPE DRAWING MACROS */ +/* ----------------------------------------------------------------------- */ + +#define SCOPE_REGS_NO_LOOP \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t color = video.palette[PAL_PATTEXT]; \ + const uint32_t width = x + w; \ + int32_t sample; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; + +#define SCOPE_REGS_LOOP \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const int32_t loopStart = s->loopStart; \ + const int32_t loopLength = s->loopLength; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t color = video.palette[PAL_PATTEXT]; \ + const uint32_t width = x + w; \ + int32_t sample; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; + +#define SCOPE_REGS_PINGPONG \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const int32_t loopStart = s->loopStart; \ + const int32_t loopLength = s->loopLength; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t color = video.palette[PAL_PATTEXT]; \ + const uint32_t width = x + w; \ + int32_t sample; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; \ + int32_t direction = s->direction; + +#define LINED_SCOPE_REGS_NO_LOOP \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t width = (x + w) - 1; \ + int32_t sample, sample2; \ + int32_t y1, y2; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; + +#define LINED_SCOPE_REGS_LOOP \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const int32_t loopStart = s->loopStart; \ + const int32_t loopLength = s->loopLength; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t width = (x + w) - 1; \ + int32_t sample, sample2; \ + int32_t y1, y2; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; + +#define LINED_SCOPE_REGS_PINGPONG \ + const int32_t volume = s->volume; \ + const int32_t sampleEnd = s->sampleEnd; \ + const int32_t loopStart = s->loopStart; \ + const int32_t loopLength = s->loopLength; \ + const uint32_t delta = (uint32_t)(s->delta >> (SCOPE_FRAC_BITS-10)); \ + const uint32_t width = (x + w) - 1; \ + int32_t sample, sample2; \ + int32_t y1, y2; \ + int32_t position = s->position; \ + uint32_t positionFrac = 0; \ + int32_t direction = s->direction; + +/* Note: Sample data already has a fixed tap samples at the end of the sample, +** so that an out-of-bounds read is OK and reads the correct interpolation tap. +*/ + +#define COLLECT_LERP_SAMPLES8 \ + sample = s->base8[position+0] << 8; \ + sample2 = s->base8[position+1] << 8; + +#define COLLECT_LERP_SAMPLES16 \ + sample = s->base16[position+0]; \ + sample2 = s->base16[position+1]; + +#define DO_LERP \ + const int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \ + sample2 -= sample; \ + sample2 = (sample2 * frac) >> 15; \ + sample += sample2; \ + +#define DO_LERP_BIDI \ + int32_t frac = (uint32_t)positionFrac >> 1; /* 0..32767 */ \ + if (direction == -1) frac ^= 32767; /* negate frac */ \ + sample2 -= sample; \ + sample2 = (sample2 * frac) >> 15; \ + sample += sample2; + +#define GET_LERP_SMP8 \ + COLLECT_LERP_SAMPLES8 \ + DO_LERP + +#define GET_LERP_SMP16 \ + COLLECT_LERP_SAMPLES16 \ + DO_LERP + +#define GET_LERP_SMP8_BIDI \ + COLLECT_LERP_SAMPLES8 \ + DO_LERP_BIDI + +#define GET_LERP_SMP16_BIDI \ + COLLECT_LERP_SAMPLES16 \ + DO_LERP_BIDI + +#define SCOPE_GET_SMP8 \ + if (s->active) \ + sample = (s->base8[position] * volume) >> 8; \ + else \ + sample = 0; + +#define SCOPE_GET_SMP16 \ + if (s->active) \ + sample = (s->base16[position] * volume) >> 16; \ + else \ + sample = 0; + +#define SCOPE_GET_LERP_SMP8 \ + if (s->active) \ + { \ + GET_LERP_SMP8 \ + sample = (sample * volume) >> 16; \ + } \ + else \ + { \ + sample = 0; \ + } + +#define SCOPE_GET_LERP_SMP16 \ + if (s->active) \ + { \ + GET_LERP_SMP16 \ + sample = (sample * volume) >> 16; \ + } \ + else \ + { \ + sample = 0; \ + } + +#define SCOPE_GET_LERP_SMP8_BIDI \ + if (s->active) \ + { \ + GET_LERP_SMP8_BIDI \ + sample = (sample * volume) >> 16; \ + } \ + else \ + { \ + sample = 0; \ + } + +#define SCOPE_GET_LERP_SMP16_BIDI \ + if (s->active) \ + { \ + GET_LERP_SMP16_BIDI \ + sample = (sample * volume) >> 16; \ + } \ + else \ + { \ + sample = 0; \ + } + +#define SCOPE_UPDATE_DRAWPOS \ + positionFrac += delta; \ + position += positionFrac >> 16; \ + positionFrac &= 0xFFFF; + +#define SCOPE_UPDATE_DRAWPOS_PINGPONG \ + positionFrac += delta; \ + position += (int32_t)(positionFrac >> 16) * direction; \ + positionFrac &= 0xFFFF; + +#define SCOPE_DRAW_SMP \ + video.frameBuffer[((lineY - sample) * SCREEN_W) + x] = color; + +#define LINED_SCOPE_PREPARE_SMP8 \ + SCOPE_GET_SMP8 \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_PREPARE_SMP16 \ + SCOPE_GET_SMP16 \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_PREPARE_LERP_SMP8 \ + SCOPE_GET_LERP_SMP8 \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_PREPARE_LERP_SMP16 \ + SCOPE_GET_LERP_SMP16 \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_PREPARE_LERP_SMP8_BIDI \ + SCOPE_GET_LERP_SMP8_BIDI \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_PREPARE_LERP_SMP16_BIDI \ + SCOPE_GET_LERP_SMP16_BIDI \ + y1 = lineY - sample; \ + SCOPE_UPDATE_DRAWPOS + +#define LINED_SCOPE_DRAW_SMP \ + y2 = lineY - sample; \ + scopeLine(x, y1, y2); \ + y1 = y2; \ + +#define SCOPE_HANDLE_POS_NO_LOOP \ + if (position >= sampleEnd) \ + s->active = false; + +#define SCOPE_HANDLE_POS_LOOP \ + if (position >= sampleEnd) \ + { \ + if (loopLength >= 2) \ + position = loopStart + ((position - sampleEnd) % loopLength); \ + else \ + position = loopStart; \ + } + +#define SCOPE_HANDLE_POS_PINGPONG \ + if (direction == -1 && position < loopStart) \ + { \ + direction = 1; /* change direction to forwards */ \ + \ + if (loopLength >= 2) \ + position = loopStart + ((loopStart - position - 1) % loopLength); \ + else \ + position = loopStart; \ + } \ + else if (position >= sampleEnd) \ + { \ + direction = -1; /* change direction to backwards */ \ + \ + if (loopLength >= 2) \ + position = (sampleEnd - 1) - ((position - sampleEnd) % loopLength); \ + else \ + position = sampleEnd - 1; \ + } diff --git a/src/scopes/ft2_scopedraw.c b/src/scopes/ft2_scopedraw.c @@ -0,0 +1,244 @@ +#include "../ft2_video.h" +#include "../ft2_palette.h" +#include "ft2_scopes.h" +#include "ft2_scopedraw.h" +#include "ft2_scope_macros.h" +#include "../ft2_cpu.h" + +static void scopeLine(int32_t x1, int32_t y1, int32_t y2); + +/* ----------------------------------------------------------------------- */ +/* SCOPE DRAWING ROUTINES */ +/* ----------------------------------------------------------------------- */ + +static void scopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_NO_LOOP + + for (; x < width; x++) + { + SCOPE_GET_SMP8 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_NO_LOOP + } +} + +static void scopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_LOOP + + for (; x < width; x++) + { + SCOPE_GET_SMP8 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_LOOP + } +} + +static void scopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_PINGPONG + + for (; x < width; x++) + { + SCOPE_GET_SMP8 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS_PINGPONG + SCOPE_HANDLE_POS_PINGPONG + } +} + +static void scopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_NO_LOOP + + for (; x < width; x++) + { + SCOPE_GET_SMP16 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_NO_LOOP + } +} + +static void scopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_LOOP + + for (; x < width; x++) + { + SCOPE_GET_SMP16 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_LOOP + } +} + +static void scopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + SCOPE_REGS_PINGPONG + + for (; x < width; x++) + { + SCOPE_GET_SMP16 + SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS_PINGPONG + SCOPE_HANDLE_POS_PINGPONG + } +} + +/* ----------------------------------------------------------------------- */ +/* INTERPOLATED SCOPE DRAWING ROUTINES */ +/* ----------------------------------------------------------------------- */ + +static void linedScopeDrawNoLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_NO_LOOP + LINED_SCOPE_PREPARE_LERP_SMP8 + SCOPE_HANDLE_POS_NO_LOOP + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP8 + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_NO_LOOP + } +} + +static void linedScopeDrawLoop_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_LOOP + LINED_SCOPE_PREPARE_LERP_SMP8 + SCOPE_HANDLE_POS_LOOP + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP8 + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_LOOP + } +} + +static void linedScopeDrawPingPong_8bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_PINGPONG + LINED_SCOPE_PREPARE_LERP_SMP8_BIDI + SCOPE_HANDLE_POS_PINGPONG + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP8_BIDI + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS_PINGPONG + SCOPE_HANDLE_POS_PINGPONG + } +} + +static void linedScopeDrawNoLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_NO_LOOP + LINED_SCOPE_PREPARE_LERP_SMP16 + SCOPE_HANDLE_POS_NO_LOOP + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP16 + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_NO_LOOP + } +} + +static void linedScopeDrawLoop_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_LOOP + LINED_SCOPE_PREPARE_LERP_SMP16 + SCOPE_HANDLE_POS_LOOP + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP16 + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS + SCOPE_HANDLE_POS_LOOP + } +} + +static void linedScopeDrawPingPong_16bit(scope_t *s, uint32_t x, uint32_t lineY, uint32_t w) +{ + LINED_SCOPE_REGS_PINGPONG + LINED_SCOPE_PREPARE_LERP_SMP16_BIDI + SCOPE_HANDLE_POS_PINGPONG + + for (; x < width; x++) + { + SCOPE_GET_LERP_SMP16_BIDI + LINED_SCOPE_DRAW_SMP + SCOPE_UPDATE_DRAWPOS_PINGPONG + SCOPE_HANDLE_POS_PINGPONG + } +} + +// ----------------------------------------------------------------------- + +static void scopeLine(int32_t x1, int32_t y1, int32_t y2) +{ + const int32_t dy = y2 - y1; + const int32_t sy = SGN(dy); + const uint32_t color = video.palette[PAL_PATTEXT]; + const int32_t pitch = sy * SCREEN_W; + + uint32_t *dst32 = &video.frameBuffer[(y1 * SCREEN_W) + x1]; + + *dst32 = color; // set first pixel + + int32_t ay = ABS(dy); + if (ay <= 1) + { + if (ay != 0) + dst32 += pitch; + + *++dst32 = color; + return; + } + + int32_t d = 1 - ay; + + ay <<= 1; + while (y1 != y2) + { + if (d >= 0) + { + d -= ay; + dst32++; + } + + y1 += sy; + d += 2; + + dst32 += pitch; + *dst32 = color; + } +} + +// ----------------------------------------------------------------------- + +const scopeDrawRoutine scopeDrawRoutineTable[12] = +{ + (scopeDrawRoutine)scopeDrawNoLoop_8bit, + (scopeDrawRoutine)scopeDrawLoop_8bit, + (scopeDrawRoutine)scopeDrawPingPong_8bit, + (scopeDrawRoutine)scopeDrawNoLoop_16bit, + (scopeDrawRoutine)scopeDrawLoop_16bit, + (scopeDrawRoutine)scopeDrawPingPong_16bit, + (scopeDrawRoutine)linedScopeDrawNoLoop_8bit, + (scopeDrawRoutine)linedScopeDrawLoop_8bit, + (scopeDrawRoutine)linedScopeDrawPingPong_8bit, + (scopeDrawRoutine)linedScopeDrawNoLoop_16bit, + (scopeDrawRoutine)linedScopeDrawLoop_16bit, + (scopeDrawRoutine)linedScopeDrawPingPong_16bit +}; diff --git a/src/ft2_scopedraw.h b/src/scopes/ft2_scopedraw.h diff --git a/src/scopes/ft2_scopes.c b/src/scopes/ft2_scopes.c @@ -0,0 +1,587 @@ +// for finding memory leaks in debug mode with Visual Studio +#if defined _DEBUG && defined _MSC_VER +#include <crtdbg.h> +#endif + +#include <stdint.h> +#include <stdbool.h> +#include <math.h> // modf() +#ifndef _WIN32 +#include <unistd.h> // usleep() +#endif +#include "../ft2_header.h" +#include "../ft2_events.h" +#include "../ft2_config.h" +#include "../ft2_audio.h" +#include "../ft2_gui.h" +#include "../ft2_midi.h" +#include "../ft2_bmp.h" +#include "../ft2_mouse.h" +#include "../ft2_video.h" +#include "../ft2_tables.h" +#include "../ft2_structs.h" +#include "ft2_scopes.h" +#include "ft2_scopedraw.h" + +static volatile bool scopesUpdatingFlag, scopesDisplayingFlag; +static uint32_t scopeTimeLen, scopeTimeLenFrac; +static uint64_t timeNext64, timeNext64Frac; +static volatile scope_t scope[MAX_CHANNELS]; +static SDL_Thread *scopeThread; + +lastChInstr_t lastChInstr[MAX_CHANNELS]; // global + +int32_t getSamplePosition(uint8_t ch) +{ + if (ch >= song.numChannels) + return -1; + + volatile scope_t *sc = &scope[ch]; + + // cache some stuff + volatile bool active = sc->active; + volatile int32_t position = sc->position; + volatile int32_t sampleEnd = sc->sampleEnd; + + if (!active || sampleEnd == 0) + return -1; + + if (position >= 0 && position < sampleEnd) + return position; + + return -1; // not active or overflown +} + +void stopAllScopes(void) +{ + // wait for scopes to finish updating + while (scopesUpdatingFlag); + + volatile scope_t *sc = scope; + for (int32_t i = 0; i < MAX_CHANNELS; i++, sc++) + sc->active = false; + + // wait for scope displaying to be done (safety) + while (scopesDisplayingFlag); +} + +// toggle mute +static void setChannel(int32_t chNr, bool on) +{ + channel_t *ch = &channel[chNr]; + + ch->channelOff = !on; + if (ch->channelOff) + { + ch->efx = 0; + ch->efxData = 0; + ch->realVol = 0; + ch->outVol = 0; + ch->oldVol = 0; + ch->fFinalVol = 0.0f; + ch->outPan = 128; + ch->oldPan = 128; + ch->finalPan = 128; + ch->status = IS_Vol; + + ch->envSustainActive = false; // non-FT2 bug fix for stuck piano keys + } + + scope[chNr].wasCleared = false; +} + +static void drawScopeNumber(uint16_t scopeXOffs, uint16_t scopeYOffs, uint8_t chNr, bool outline) +{ + scopeXOffs++; + scopeYOffs++; + chNr++; + + if (outline) + { + if (chNr < 10) // one digit? + { + charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + chNr); + } + else + { + charOutOutlined(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]); + charOutOutlined(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]); + } + } + else + { + if (chNr < 10) // one digit? + { + charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, '0' + chNr); + } + else + { + charOut(scopeXOffs, scopeYOffs, PAL_MOUSEPT, chDecTab1[chNr]); + charOut(scopeXOffs + 7, scopeYOffs, PAL_MOUSEPT, chDecTab2[chNr]); + } + } +} + +static void redrawScope(int32_t ch) +{ + int32_t i; + + int32_t chansPerRow = (uint32_t)song.numChannels >> 1; + int32_t chanLookup = chansPerRow - 1; + const uint16_t *scopeLens = scopeLenTab[chanLookup]; + + // get x,y,len for scope according to channel (we must do it this way since 'len' can differ!) + + uint16_t x = 2; + uint16_t y = 94; + + uint16_t scopeLen = 0; // prevent compiler warning + for (i = 0; i < song.numChannels; i++) + { + scopeLen = scopeLens[i]; + + if (i == chansPerRow) // did we reach end of row? + { + // yes, go one row down + x = 2; + y += 39; + } + + if (i == ch) + break; + + // adjust position to next channel + x += scopeLen + 3; + } + + drawFramework(x, y, scopeLen + 2, 38, FRAMEWORK_TYPE2); + + // draw mute graphics if channel is muted + if (!editor.chnMode[i]) + { + const uint16_t muteGfxLen = scopeMuteBMP_Widths[chanLookup]; + const uint16_t muteGfxX = x + ((scopeLen - muteGfxLen) >> 1); + + blitFastClipX(muteGfxX, y + 6, bmp.scopeMute+scopeMuteBMP_Offs[chanLookup], 162, scopeMuteBMP_Heights[chanLookup], muteGfxLen); + + if (config.ptnChnNumbers) + drawScopeNumber(x + 1, y + 1, (uint8_t)i, true); + } + + scope[ch].wasCleared = false; +} + +void refreshScopes(void) +{ + for (int32_t i = 0; i < MAX_CHANNELS; i++) + scope[i].wasCleared = false; +} + +static void channelMode(int32_t chn) +{ + int32_t i; + + assert(chn < song.numChannels); + + bool m = mouse.leftButtonPressed && !mouse.rightButtonPressed; + bool m2 = mouse.rightButtonPressed && mouse.leftButtonPressed; + + if (m2) + { + bool test = false; + for (i = 0; i < song.numChannels; i++) + { + if (i != chn && !editor.chnMode[i]) + test = true; + } + + if (test) + { + for (i = 0; i < song.numChannels; i++) + editor.chnMode[i] = true; + } + else + { + for (i = 0; i < song.numChannels; i++) + editor.chnMode[i] = (i == chn); + } + } + else if (m) + { + editor.chnMode[chn] ^= 1; + } + else + { + if (editor.chnMode[chn]) + { + config.multiRecChn[chn] ^= 1; + } + else + { + config.multiRecChn[chn] = true; + editor.chnMode[chn] = true; + m = true; + } + } + + for (i = 0; i < song.numChannels; i++) + setChannel(i, editor.chnMode[i]); + + if (m2) + { + for (i = 0; i < song.numChannels; i++) + redrawScope(i); + } + else + { + redrawScope(chn); + } +} + +bool testScopesMouseDown(void) +{ + int32_t i; + + if (!ui.scopesShown) + return false; + + if (mouse.y >= 95 && mouse.y <= 169 && mouse.x >= 3 && mouse.x <= 288) + { + if (mouse.y > 130 && mouse.y < 134) + return true; + + int32_t chansPerRow = (uint32_t)song.numChannels >> 1; + const uint16_t *scopeLens = scopeLenTab[chansPerRow-1]; + + // find out if we clicked inside a scope + uint16_t x = 3; + for (i = 0; i < chansPerRow; i++) + { + if (mouse.x >= x && mouse.x < x+scopeLens[i]) + break; + + x += scopeLens[i]+3; + } + + if (i == chansPerRow) + return true; // scope framework was clicked instead + + int32_t chanToToggle = i; + if (mouse.y >= 134) // second row of scopes? + chanToToggle += chansPerRow; // yes, increase lookup offset + + channelMode(chanToToggle); + return true; + } + + return false; +} + +static void scopeTrigger(int32_t ch, const sample_t *s, int32_t playOffset) +{ + volatile scope_t tempState; + volatile scope_t *sc = &scope[ch]; + + int32_t length = s->length; + int32_t loopStart = s->loopStart; + int32_t loopLength = s->loopLength; + int32_t loopEnd = s->loopStart + s->loopLength; + uint8_t loopType = GET_LOOPTYPE(s->flags); + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + + if (s->dataPtr == NULL || length < 1) + { + sc->active = false; // shut down scope (illegal parameters) + return; + } + + tempState = *sc; // get copy of current scope state + + if (loopLength < 1) // disable loop if loopLength is below 1 + loopType = 0; + + if (sample16Bit) + tempState.base16 = (const int16_t *)s->dataPtr; + else + tempState.base8 = s->dataPtr; + + tempState.sample16Bit = sample16Bit; + tempState.loopType = loopType; + tempState.direction = 1; // forwards + tempState.sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd; + tempState.loopStart = loopStart; + tempState.loopLength = loopLength; + tempState.position = playOffset; + tempState.positionFrac = 0; + + // if position overflows (f.ex. through 9xx command), shut down scopes + if (tempState.position >= tempState.sampleEnd) + { + sc->active = false; + return; + } + + tempState.active = true; + + /* Update live scope now. + ** In theory it -can- be written to in the middle of a cached read, + ** then the read thread writes its own non-updated cached copy back and + ** the trigger never happens. So far I have never seen it happen, + ** so it's probably very rare. Yes, this is not good coding... + */ + + *sc = tempState; // set new scope state +} + +static void updateScopes(void) +{ + scopesUpdatingFlag = true; + + volatile scope_t *sc = scope; + for (int32_t i = 0; i < song.numChannels; i++, sc++) + { + volatile scope_t s = *sc; // get copy of current scope state + if (!s.active) + continue; // scope is not active + + // scope position update + + s.positionFrac += s.delta; + const uint32_t wholeSamples = (uint32_t)(s.positionFrac >> SCOPE_FRAC_BITS); + s.positionFrac &= SCOPE_FRAC_MASK; + + if (s.direction == 1) + s.position += wholeSamples; // forwards + else + s.position -= wholeSamples; // backwards + + // handle loop wrapping or sample end + if (s.direction == -1 && s.position < s.loopStart) // sampling backwards (definitely pingpong loop) + { + s.direction = 1; // change direction to forwards + + if (s.loopLength >= 2) + s.position = s.loopStart + ((s.loopStart - s.position - 1) % s.loopLength); + else + s.position = s.loopStart; + + assert(s.position >= s.loopStart && s.position < s.sampleEnd); + } + else if (s.position >= s.sampleEnd) + { + uint32_t loopOverflowVal; + + if (s.loopLength >= 2) + loopOverflowVal = (s.position - s.sampleEnd) % s.loopLength; + else + loopOverflowVal = 0; + + if (s.loopType == LOOP_DISABLED) + { + s.active = false; + } + else if (s.loopType == LOOP_FORWARD) + { + s.position = s.loopStart + loopOverflowVal; + assert(s.position >= s.loopStart && s.position < s.sampleEnd); + } + else // pingpong loop + { + s.direction = -1; // change direction to backwards + s.position = (s.sampleEnd - 1) - loopOverflowVal; + assert(s.position >= s.loopStart && s.position < s.sampleEnd); + } + } + assert(s.position >= 0); + + *sc = s; // set new scope state + } + scopesUpdatingFlag = false; +} + +void drawScopes(void) +{ + scopesDisplayingFlag = true; + int32_t chansPerRow = (uint32_t)song.numChannels >> 1; + + const uint16_t *scopeLens = scopeLenTab[chansPerRow-1]; + uint16_t scopeXOffs = 3; + uint16_t scopeYOffs = 95; + int16_t scopeLineY = 112; + + for (int32_t i = 0; i < song.numChannels; i++) + { + // if we reached the last scope on the row, go to first scope on the next row + if (i == chansPerRow) + { + scopeXOffs = 3; + scopeYOffs = 134; + scopeLineY = 151; + } + + const uint16_t scopeDrawLen = scopeLens[i]; + if (!editor.chnMode[i]) // scope muted (mute graphics blit()'ed elsewhere) + { + scopeXOffs += scopeDrawLen+3; // align x to next scope + continue; + } + + const scope_t s = scope[i]; // cache scope to lower thread race condition issues + if (s.active && s.volume > 0 && !audio.locked) + { + // scope is active + scope[i].wasCleared = false; + + // clear scope background + clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT); + + // draw scope + bool linedScopesFlag = !!(config.specialFlags & LINED_SCOPES); + scopeDrawRoutineTable[(linedScopesFlag * 6) + (s.sample16Bit * 3) + s.loopType](&s, scopeXOffs, scopeLineY, scopeDrawLen); + } + else + { + // scope is inactive + volatile scope_t *sc = &scope[i]; + if (!sc->wasCleared) + { + // clear scope background + clearRect(scopeXOffs, scopeYOffs, scopeDrawLen, SCOPE_HEIGHT); + + // draw empty line + hLine(scopeXOffs, scopeLineY, scopeDrawLen, PAL_PATTEXT); + + sc->wasCleared = true; + } + } + + // draw channel numbering (if enabled) + if (config.ptnChnNumbers) + drawScopeNumber(scopeXOffs, scopeYOffs, (uint8_t)i, false); + + // draw rec. symbol (if enabled) + if (config.multiRecChn[i]) + blit(scopeXOffs + 1, scopeYOffs + 31, bmp.scopeRec, 13, 4); + + scopeXOffs += scopeDrawLen+3; // align x to next scope + } + + scopesDisplayingFlag = false; +} + +void drawScopeFramework(void) +{ + drawFramework(0, 92, 291, 81, FRAMEWORK_TYPE1); + for (int32_t i = 0; i < song.numChannels; i++) + redrawScope(i); +} + +void handleScopesFromChQueue(chSyncData_t *chSyncData, uint8_t *scopeUpdateStatus) +{ + volatile scope_t *sc = scope; + syncedChannel_t *ch = chSyncData->channels; + for (int32_t i = 0; i < song.numChannels; i++, sc++, ch++) + { + const uint8_t status = scopeUpdateStatus[i]; + + if (status & IS_Vol) + sc->volume = ch->scopeVolume; + + if (status & IS_Period) + sc->delta = ch->scopeDelta; + + if (status & IS_Trigger) + { + if (instr[ch->instrNum] != NULL) + { + scopeTrigger(i, &instr[ch->instrNum]->smp[ch->smpNum], ch->smpStartPos); + + // set some stuff used by Smp. Ed. for sampling position line + + if (ch->instrNum == 130 || (ch->instrNum == editor.curInstr && ch->smpNum == editor.curSmp)) + editor.curSmpChannel = (uint8_t)i; + + lastChInstr[i].instrNum = ch->instrNum; + lastChInstr[i].smpNum = ch->smpNum; + } + else + { + // empty instrument, shut down scope + scope[i].active = false; + lastChInstr[i].instrNum = 255; + lastChInstr[i].smpNum = 255; + } + } + } +} + +static int32_t SDLCALL scopeThreadFunc(void *ptr) +{ + // this is needed for scope stability (confirmed) + SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); + + // set next frame time + timeNext64 = SDL_GetPerformanceCounter() + scopeTimeLen; + timeNext64Frac = scopeTimeLenFrac; + + while (editor.programRunning) + { + editor.scopeThreadMutex = true; + updateScopes(); + editor.scopeThreadMutex = false; + + uint64_t time64 = SDL_GetPerformanceCounter(); + if (time64 < timeNext64) + { + time64 = timeNext64 - time64; + if (time64 > INT32_MAX) + time64 = INT32_MAX; + + const int32_t diff32 = (int32_t)time64; + + // convert and round to microseconds + const int32_t time32 = (int32_t)((diff32 * editor.dPerfFreqMulMicro) + 0.5); + + // delay until we have reached the next frame + if (time32 > 0) + usleep(time32); + } + + // update next tick time + timeNext64 += scopeTimeLen; + timeNext64Frac += scopeTimeLenFrac; + if (timeNext64Frac > UINT32_MAX) + { + timeNext64Frac &= UINT32_MAX; + timeNext64++; + } + } + + (void)ptr; + return true; +} + +bool initScopes(void) +{ + double dInt; + + // calculate scope time for performance counters and split into int/frac + double dFrac = modf(editor.dPerfFreq / SCOPE_HZ, &dInt); + + // integer part + scopeTimeLen = (int32_t)dInt; + + // fractional part (scaled to 0..2^32-1) + dFrac *= UINT32_MAX+1.0; + scopeTimeLenFrac = (uint32_t)dFrac; + + scopeThread = SDL_CreateThread(scopeThreadFunc, NULL, NULL); + if (scopeThread == NULL) + { + showErrorMsgBox("Couldn't create channel scope thread!"); + return false; + } + + SDL_DetachThread(scopeThread); + return true; +} diff --git a/src/scopes/ft2_scopes.h b/src/scopes/ft2_scopes.h @@ -0,0 +1,49 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "../ft2_header.h" +#include "../ft2_audio.h" +#include "../ft2_cpu.h" + +#define SCOPE_HEIGHT 36 + +#if CPU_64BIT +#define SCOPE_FRAC_BITS 32 +#else +/* Max safe amount of fracbits for uint32_t on scopes @ 64Hz. +** Since the scopes deltas are always high'ish, 13-bit +** fractional precision is OK. +*/ +#define SCOPE_FRAC_BITS 13 +#endif + +#define SCOPE_FRAC_SCALE ((intCPUWord_t)1 << SCOPE_FRAC_BITS) +#define SCOPE_FRAC_MASK (SCOPE_FRAC_SCALE-1) + +int32_t getSamplePosition(uint8_t ch); +void stopAllScopes(void); +void refreshScopes(void); +bool testScopesMouseDown(void); +void drawScopes(void); +void drawScopeFramework(void); +bool initScopes(void); + +// actual scope data +typedef struct scope_t +{ + volatile bool active; + const int8_t *base8; + const int16_t *base16; + bool wasCleared, sample16Bit; + uint8_t loopType; + int32_t volume, direction, loopStart, loopLength, sampleEnd, position; + uintCPUWord_t delta, positionFrac; +} scope_t; + +typedef struct lastChInstr_t +{ + uint8_t smpNum, instrNum; +} lastChInstr_t; + +extern lastChInstr_t lastChInstr[MAX_CHANNELS]; diff --git a/src/smploaders/ft2_load_aiff.c b/src/smploaders/ft2_load_aiff.c @@ -1,4 +1,9 @@ -// Apple AIFF sample loader +/* Apple AIFF sample loader +** +** Note: Vol/loop sanitation is done in the last stage +** of sample loading, so you don't need to do that here. +** Do NOT close the file handle! +*/ #include <stdio.h> #include <stdint.h> @@ -9,7 +14,7 @@ #include "../ft2_sysreqs.h" #include "../ft2_sample_loader.h" -static int32_t getAIFFRate(uint8_t *in); +static int32_t getAIFFSampleRate(uint8_t *in); static bool aiffIsStereo(FILE *f); // only ran on files that are confirmed to be AIFFs bool loadAIFF(FILE *f, uint32_t filesize) @@ -17,20 +22,16 @@ bool loadAIFF(FILE *f, uint32_t filesize) char compType[4]; int8_t *audioDataS8; uint8_t sampleRateBytes[10], *audioDataU8; - int16_t *audioDataS16, *audioDataS16_2, smp16; + int16_t *audioDataS16, smp16; uint16_t numChannels, bitDepth; - int32_t smp32, *audioDataS32; + int32_t *audioDataS32; uint32_t i, blockName, blockSize; uint32_t offset, len32; + sample_t *s = &tmpSmp; fseek(f, 8, SEEK_SET); fread(compType, 1, 4, f); rewind(f); - if (!memcmp(compType, "AIFC", 4)) - { - loaderMsgBox("Error loading sample: This AIFF type (AIFC) is not supported!"); - return false; - } if (filesize < 12) { @@ -97,24 +98,36 @@ bool loadAIFF(FILE *f, uint32_t filesize) return false; } - if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32) - { - loaderMsgBox("Error loading sample: Unsupported bitdepth!"); - return false; - } - // read compression type (if present) + bool signedSample = true; + bool floatSample = false; if (commLen > 18) { fread(&compType, 1, 4, f); - if (memcmp(compType, "NONE", 4) != 0) + + if (!memcmp(compType, "raw ", 4)) { - loaderMsgBox("Error loading sample: The sample is not supported or is invalid!"); + signedSample = false; + } + else if (!memcmp(compType, "FL32", 4) || !memcmp(compType, "fl32", 4) || !memcmp(compType, "FL64", 4) || !memcmp(compType, "fl64", 4)) + { + floatSample = true; + } + else + { + loaderMsgBox("Error loading sample: Unsupported AIFF type!"); return false; } } - uint32_t sampleRate = getAIFFRate(sampleRateBytes); + if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32 && + !(floatSample && bitDepth == 64) && !(floatSample && bitDepth == 32)) + { + loaderMsgBox("Error loading sample: Unsupported AIFF type!"); + return false; + } + + uint32_t sampleRate = getAIFFSampleRate(sampleRateBytes); // sample data chunk @@ -132,8 +145,6 @@ bool loadAIFF(FILE *f, uint32_t filesize) ssndLen -= 8; // don't include offset and blockSize datas uint32_t sampleLength = ssndLen; - if (sampleLength > MAX_SAMPLE_LEN) - sampleLength = MAX_SAMPLE_LEN; int16_t stereoSampleLoadMode = -1; if (aiffIsStereo(f)) @@ -141,23 +152,26 @@ bool loadAIFF(FILE *f, uint32_t filesize) // read sample data - if (bitDepth == 8) + if (!floatSample && bitDepth == 8) // 8-BIT INTEGER SAMPLE { - // 8-BIT SIGNED PCM - - if (!allocateTmpSmpData(&tmpSmp, sampleLength)) + if (!allocateSmpData(s, sampleLength, false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 1, f) != 1) + if (fread(s->dataPtr, sampleLength, 1, f) != 1) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataS8 = (int8_t *)tmpSmp.pek; + audioDataS8 = (int8_t *)s->dataPtr; + if (!signedSample) + { + for (i = 0; i < sampleLength; i++) + audioDataS8[i] ^= 0x80; + } // stereo conversion if (numChannels == 2) @@ -199,25 +213,23 @@ bool loadAIFF(FILE *f, uint32_t filesize) } } } - else if (bitDepth == 16) + else if (!floatSample && bitDepth == 16) // 16-BIT INTEGER SAMPLE { - // 16-BIT SIGNED PCM - - sampleLength /= 2; - if (!allocateTmpSmpData(&tmpSmp, sampleLength*2)) + sampleLength /= sizeof (int16_t); + if (!allocateSmpData(s, sampleLength * sizeof (int16_t), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 2, f) != 2) + if (fread(s->dataPtr, sampleLength, sizeof (int16_t), f) != sizeof (int16_t)) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - // fix endianness - audioDataS16 = (int16_t *)tmpSmp.pek; + // change endianness + audioDataS16 = (int16_t *)s->dataPtr; for (i = 0; i < sampleLength; i++) audioDataS16[i] = SWAP16(audioDataS16[i]); @@ -251,7 +263,7 @@ bool loadAIFF(FILE *f, uint32_t filesize) len32 = sampleLength - 1; for (i = 0; i < len32; i++) { - smp32 = (audioDataS16[(i * 2) + 0] + audioDataS16[(i * 2) + 1]) >> 1; + int32_t smp32 = (audioDataS16[(i * 2) + 0] + audioDataS16[(i * 2) + 1]) >> 1; audioDataS16[i] = (int16_t)smp32; } @@ -261,30 +273,27 @@ bool loadAIFF(FILE *f, uint32_t filesize) } } - sampleLength *= 2; - tmpSmp.typ |= 16; + s->flags |= SAMPLE_16BIT; } - else if (bitDepth == 24) + else if (!floatSample && bitDepth == 24) // 24-BIT INTEGER SAMPLE { - // 24-BIT SIGNED PCM - sampleLength /= 3; - if (!allocateTmpSmpData(&tmpSmp, (sampleLength * 4) * 2)) + if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(&tmpSmp.pek[sampleLength], sampleLength, 3, f) != 3) + if (fread(&s->dataPtr[sampleLength], sampleLength, 3, f) != 3) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataS32 = (int32_t *)tmpSmp.pek; + audioDataS32 = (int32_t *)s->dataPtr; // convert to 32-bit - audioDataU8 = (uint8_t *)tmpSmp.pek + sampleLength; + audioDataU8 = (uint8_t *)s->dataPtr + sampleLength; for (i = 0; i < sampleLength; i++) { audioDataS32[i] = (audioDataU8[0] << 24) | (audioDataU8[1] << 16) | (audioDataU8[2] << 8); @@ -336,36 +345,29 @@ bool loadAIFF(FILE *f, uint32_t filesize) normalizeSigned32Bit(audioDataS32, sampleLength); - // downscale to 16-bit (ultra fast method!) - - audioDataS16 = (int16_t *)tmpSmp.pek; - audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words - - for (i = 0; i < sampleLength; i++, audioDataS16_2++) - audioDataS16[i] = audioDataS16_2[i]; + audioDataS16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + audioDataS16[i] = audioDataS32[i] >> 16; - sampleLength *= 2; - tmpSmp.typ |= 16; + s->flags |= SAMPLE_16BIT; } - else if (bitDepth == 32) + else if (!floatSample && bitDepth == 32) // 32-BIT INTEGER SAMPLE { - // 32-BIT SIGNED PCM - - sampleLength /= 4; - if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4)) + sampleLength /= sizeof (int32_t); + if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 4, f) != 4) + if (fread(s->dataPtr, sampleLength, sizeof (int32_t), f) != sizeof (int32_t)) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - // fix endianness - audioDataS32 = (int32_t *)tmpSmp.pek; + // change endianness + audioDataS32 = (int32_t *)s->dataPtr; for (i = 0; i < sampleLength; i++) audioDataS32[i] = SWAP32(audioDataS32[i]); @@ -414,42 +416,188 @@ bool loadAIFF(FILE *f, uint32_t filesize) normalizeSigned32Bit(audioDataS32, sampleLength); - // downscale to 16-bit (ultra fast method!) + audioDataS16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + audioDataS16[i] = audioDataS32[i] >> 16; + + s->flags |= SAMPLE_16BIT; + } + else if (floatSample && bitDepth == 32) // 32-BIT FLOAT SAMPLE + { + sampleLength /= sizeof (float); + if (!allocateSmpData(s, sampleLength * sizeof (float), false)) + { + loaderMsgBox("Not enough memory!"); + return false; + } + + if (fread(s->dataPtr, sampleLength, sizeof (float), f) != sizeof (float)) + { + loaderMsgBox("General I/O error during loading! Is the file in use?"); + return false; + } + + // change endianness + audioDataS32 = (int32_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + audioDataS32[i] = SWAP32(audioDataS32[i]); + + float *fAudioDataFloat = (float *)s->dataPtr; + + // stereo conversion + if (numChannels == 2) + { + sampleLength /= 2; + switch (stereoSampleLoadMode) + { + case STEREO_SAMPLE_READ_LEFT: + { + // remove right channel data + for (i = 1; i < sampleLength; i++) + fAudioDataFloat[i] = fAudioDataFloat[(i * 2) + 0]; + } + break; + + case STEREO_SAMPLE_READ_RIGHT: + { + // remove left channel data + len32 = sampleLength - 1; + for (i = 0; i < len32; i++) + fAudioDataFloat[i] = fAudioDataFloat[(i * 2) + 1]; + + fAudioDataFloat[i] = 0.0f; + } + break; + + default: + case STEREO_SAMPLE_CONVERT: + { + // mix stereo to mono + len32 = sampleLength - 1; + for (i = 0; i < len32; i++) + fAudioDataFloat[i] = (fAudioDataFloat[(i * 2) + 0] + fAudioDataFloat[(i * 2) + 1]) * 0.5f; + + fAudioDataFloat[i] = 0.0f; + } + break; + } + } - audioDataS16 = (int16_t *)tmpSmp.pek; - audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words + normalize32BitFloatToSigned16Bit(fAudioDataFloat, sampleLength); - for (i = 0; i < sampleLength; i++, audioDataS16_2++) - audioDataS16[i] = audioDataS16_2[i]; + int16_t *ptr16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + { + const int32_t smp32 = (const int32_t)fAudioDataFloat[i]; + ptr16[i] = (int16_t)smp32; + } - sampleLength *= 2; - tmpSmp.typ |= 16; + s->flags |= SAMPLE_16BIT; } + else if (floatSample && bitDepth == 64) // 64-BIT FLOAT SAMPLE + { + sampleLength /= sizeof (double); + if (!allocateSmpData(s, sampleLength * sizeof (double), false)) + { + loaderMsgBox("Not enough memory!"); + return false; + } + + if (fread(s->dataPtr, sampleLength, sizeof (double), f) != sizeof (double)) + { + loaderMsgBox("General I/O error during loading! Is the file in use?"); + return false; + } - reallocateTmpSmpData(&tmpSmp, sampleLength); // readjust memory needed + // change endianness + int64_t *audioDataS64 = (int64_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + audioDataS64[i] = SWAP64(audioDataS64[i]); - tmpSmp.len = sampleLength; - tmpSmp.vol = 64; - tmpSmp.pan = 128; + double *dAudioDataDouble = (double *)s->dataPtr; - tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag); + // stereo conversion + if (numChannels == 2) + { + sampleLength /= 2; + switch (stereoSampleLoadMode) + { + case STEREO_SAMPLE_READ_LEFT: + { + // remove right channel data + for (i = 1; i < sampleLength; i++) + dAudioDataDouble[i] = dAudioDataDouble[(i * 2) + 0]; + } + break; + + case STEREO_SAMPLE_READ_RIGHT: + { + // remove left channel data + len32 = sampleLength - 1; + for (i = 0; i < len32; i++) + dAudioDataDouble[i] = dAudioDataDouble[(i * 2) + 1]; + + dAudioDataDouble[i] = 0.0; + } + break; + + default: + case STEREO_SAMPLE_CONVERT: + { + // mix stereo to mono + len32 = sampleLength - 1; + for (i = 0; i < len32; i++) + dAudioDataDouble[i] = (dAudioDataDouble[(i * 2) + 0] + dAudioDataDouble[(i * 2) + 1]) * 0.5; + + dAudioDataDouble[i] = 0.0; + } + break; + } + } + + normalize64BitFloatToSigned16Bit(dAudioDataDouble, sampleLength); + + int16_t *ptr16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + { + const int32_t smp32 = (const int32_t)dAudioDataDouble[i]; + ptr16[i] = (int16_t)smp32; + } + + s->flags |= SAMPLE_16BIT; + } + + if (sampleLength > MAX_SAMPLE_LEN) + sampleLength = MAX_SAMPLE_LEN; + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + reallocateSmpData(s, sampleLength, sample16Bit); // readjust memory needed + + s->length = sampleLength; + s->volume = 64; + s->panning = 128; + + tuneSample(s, sampleRate, audio.linearPeriodsFlag); return true; } -static int32_t getAIFFRate(uint8_t *in) +static int32_t getAIFFSampleRate(uint8_t *in) { - int32_t exp = (int32_t)(((in[0] & 0x7F) << 8) | in[1]); - uint32_t lo = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5]; - uint32_t hi = (in[6] << 24) | (in[7] << 16) | (in[8] << 8) | in[9]; + uint32_t mantissa = (in[2] << 24) | (in[3] << 16) | (in[4] << 8) | in[5]; + uint8_t exp = 30 - in[1]; - if (exp == 0 && lo == 0 && hi == 0) - return 0; + uint32_t lastMantissa = 0; + while (exp--) + { + lastMantissa = mantissa; + mantissa >>= 1; + } - exp -= 16383; + if (lastMantissa & 1) + mantissa++; - double dOut = ldexp(lo, -31 + exp) + ldexp(hi, -63 + exp); - return (int32_t)(dOut + 0.5); // rounded + return mantissa; } static bool aiffIsStereo(FILE *f) // only ran on files that are confirmed to be AIFFs diff --git a/src/smploaders/ft2_load_flac.c b/src/smploaders/ft2_load_flac.c @@ -0,0 +1,431 @@ +/* FLAC sample loader +** +** Note: Vol/loop sanitation is done in the last stage +** of sample loading, so you don't need to do that here. +** Do NOT close the file handle! +*/ + +#ifdef HAS_LIBFLAC + +// hide POSIX warning for fileno() +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#ifndef _WIN32 +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#endif +#include "../ft2_header.h" +#include "../ft2_audio.h" +#include "../ft2_sample_ed.h" +#include "../ft2_sysreqs.h" +#include "../ft2_sample_loader.h" +#include "../libflac/FLAC/stream_decoder.h" + +static bool sample16Bit; +static int16_t stereoSampleLoadMode = -1; +static uint32_t numChannels, bitDepth, sampleLength, sampleRate, samplesRead; +static sample_t *s; + +static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +static FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data); +static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data); +static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +bool loadFLAC(FILE *f, uint32_t filesize) +{ + s = &tmpSmp; + + s->volume = 64; + s->panning = 128; + + numChannels = 0; + bitDepth = 0; + sampleLength = 0; + sampleRate = 0; + samplesRead = 0; + + FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); + if (decoder == NULL) + { + loaderMsgBox("Error loading sample: Unable to allocate FLAC decoder!"); + goto error; + } + + FLAC__stream_decoder_set_metadata_respond_all(decoder); + + FLAC__StreamDecoderInitStatus initStatus = + FLAC__stream_decoder_init_stream + ( + decoder, + read_callback, seek_callback, + tell_callback, length_callback, + eof_callback, write_callback, + metadata_callback, error_callback, + f + ); + + if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) + { + loaderMsgBox("Error loading sample: Unable to initialize FLAC decoder!"); + goto error; + } + + if (!FLAC__stream_decoder_process_until_end_of_stream(decoder)) + { + loaderMsgBox("Error loading sample: Unable to decode FLAC!"); + goto error; + } + + FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + tuneSample(s, sampleRate, audio.linearPeriodsFlag); + + return true; + +error: + if (decoder != NULL) FLAC__stream_decoder_delete(decoder); + + return false; + + (void)filesize; +} + +static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + FILE *file = (FILE *)client_data; + if (*bytes > 0) + { + *bytes = fread(buffer, sizeof (FLAC__byte), *bytes, file); + if (ferror(file)) + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + else if (*bytes == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + else + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + else + { + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + + (void)decoder; +} + +static FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + FILE *file = (FILE *)client_data; + + if (absolute_byte_offset > INT32_MAX) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + + if (fseek(file, (int32_t)absolute_byte_offset, SEEK_SET) < 0) + return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; + else + return FLAC__STREAM_DECODER_SEEK_STATUS_OK; + + (void)decoder; +} + +static FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + FILE *file = (FILE *)client_data; + int32_t pos = ftell(file); + + if (pos < 0) + { + return FLAC__STREAM_DECODER_TELL_STATUS_ERROR; + } + else + { + *absolute_byte_offset = (FLAC__uint64)pos; + return FLAC__STREAM_DECODER_TELL_STATUS_OK; + } + + (void)decoder; +} + +static FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +{ + FILE *file = (FILE *)client_data; + struct stat filestats; + + if (fstat(fileno(file), &filestats) != 0) + { + return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; + } + else + { + *stream_length = (FLAC__uint64)filestats.st_size; + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; + } + + (void)decoder; +} + +static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data) +{ + FILE *file = (FILE *)client_data; + return feof(file) ? true : false; + + (void)decoder; +} + +static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO && metadata->data.stream_info.total_samples != 0) + { + bitDepth = metadata->data.stream_info.bits_per_sample; + numChannels = metadata->data.stream_info.channels; + sampleRate = metadata->data.stream_info.sample_rate; + + sample16Bit = (bitDepth != 8); + + int64_t tmp64 = metadata->data.stream_info.total_samples; + if (tmp64 > MAX_SAMPLE_LEN) + tmp64 = MAX_SAMPLE_LEN; + + sampleLength = (uint32_t)tmp64; + + s->length = sampleLength; + if (sample16Bit) + s->flags |= SAMPLE_16BIT; + + stereoSampleLoadMode = -1; + if (numChannels == 2) + stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample..."); + } + + // check for RIFF chunks (loop/vol/pan information) + else if (metadata->type == FLAC__METADATA_TYPE_APPLICATION && !memcmp(metadata->data.application.id, "riff", 4)) + { + const uint8_t *data = (const uint8_t *)metadata->data.application.data; + + uint32_t chunkID = *(uint32_t *)data; data += 4; + uint32_t chunkLen = *(uint32_t *)data; data += 4; + + if (chunkID == 0x61727478 && chunkLen >= 8) // "xtra" + { + uint32_t xtraFlags = *(uint32_t *)data; data += 4; + + // panning (0..256) + if (xtraFlags & 0x20) // set panning flag + { + uint16_t tmpPan = *(uint16_t *)data; + if (tmpPan > 255) + tmpPan = 255; + + s->panning = (uint8_t)tmpPan; + } + data += 2; + + // volume (0..256) + uint16_t tmpVol = *(uint16_t *)data; + if (tmpVol > 256) + tmpVol = 256; + + s->volume = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded) + } + + if (chunkID == 0x6C706D73 && chunkLen > 52) // "smpl" + { + data += 28; // seek to first wanted byte + + uint32_t numLoops = *(uint32_t *)data; data += 4; + if (numLoops == 1) + { + data += 4+4; // skip "samplerData" and "identifier" + + uint32_t loopType = *(uint32_t *)data; data += 4; + uint32_t loopStart = *(uint32_t *)data; data += 4; + uint32_t loopEnd = *(uint32_t *)data; data += 4; + + s->loopStart = loopStart; + s->loopLength = (loopEnd+1) - loopStart; + s->flags |= (loopType == 0) ? LOOP_FWD : LOOP_BIDI; + } + } + } + + else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) + { + uint32_t tmpSampleRate = 0, loopStart = 0, loopLength = 0; + for (uint32_t i = 0; i < metadata->data.vorbis_comment.num_comments; i++) + { + const char *tag = (const char *)metadata->data.vorbis_comment.comments[i].entry; + uint32_t length = metadata->data.vorbis_comment.comments[i].length; + + if (length > 6 && !memcmp(tag, "TITLE=", 6)) + { + length -= 6; + if (length > 22) + length = 22; + + memcpy(s->name, &tag[6], length); + s->name[22] = '\0'; + + smpFilenameSet = true; + } + + // the following tags haven't been tested! + else if (length > 11 && !memcmp(tag, "SAMPLERATE=", 11)) + { + tmpSampleRate = atoi(&tag[11]); + } + else if (length > 10 && !memcmp(tag, "LOOPSTART=", 10)) + { + loopLength = atoi(&tag[10]); + } + else if (length > 11 && !memcmp(tag, "LOOPLENGTH=", 11)) + { + loopLength = atoi(&tag[11]); + } + + if (loopLength > 0) + { + s->loopStart = loopLength; + s->loopLength = loopStart; + + DISABLE_LOOP(s->flags); + s->flags |= LOOP_FWD; + } + + if (tmpSampleRate > 0) + sampleRate = tmpSampleRate; + } + } + + (void)client_data; + (void)decoder; +} + +static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) +{ + if (sampleLength == 0 || numChannels == 0) + { + loaderMsgBox("Error loading sample: The sample is empty or corrupt!"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (numChannels > 2) + { + loaderMsgBox("Error loading sample: Only mono/stereo FLACs are supported!"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24) + { + loaderMsgBox("Error loading sample: Only FLACs with a bitdepth of 8/16/24 are supported!"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (frame->header.number.sample_number == 0) + { + if (!allocateSmpData(s, sampleLength, sample16Bit)) + { + loaderMsgBox("Error loading sample: Out of memory!"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + samplesRead = 0; + } + + uint32_t blockSize = frame->header.blocksize; + + const uint32_t samplesAllocated = sampleLength; + if (samplesRead+blockSize > samplesAllocated) + blockSize = samplesAllocated-samplesRead; + + if (stereoSampleLoadMode == STEREO_SAMPLE_CONVERT) // mix to mono + { + const int32_t *src32_L = buffer[0]; + const int32_t *src32_R = buffer[1]; + + switch (bitDepth) + { + case 8: + { + int8_t *dst8 = s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst8[i] = (int8_t)((src32_L[i] + src32_R[i]) >> 1); + } + break; + + case 16: + { + int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> 1); + } + break; + + case 24: + { + int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst16[i] = (int16_t)((src32_L[i] + src32_R[i]) >> (16+1)); + } + break; + + default: break; + } + } + else // mono sample + { + const int32_t *src32 = (stereoSampleLoadMode == STEREO_SAMPLE_READ_RIGHT) ? buffer[1] : buffer[0]; + + switch (bitDepth) + { + case 8: + { + int8_t *dst8 = s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst8[i] = (int8_t)src32[i]; + } + break; + + case 16: + { + int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst16[i] = (int16_t)src32[i]; + } + break; + + case 24: + { + int16_t *dst16 = (int16_t *)s->dataPtr + samplesRead; + for (uint32_t i = 0; i < blockSize; i++) + dst16[i] = (int16_t)(src32[i] >> 8); + } + break; + + default: break; + } + } + + samplesRead += blockSize; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + + (void)client_data; + (void)decoder; +} + +static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + (void)status; + (void)decoder; + (void)client_data; +} + +#endif diff --git a/src/smploaders/ft2_load_iff.c b/src/smploaders/ft2_load_iff.c @@ -1,4 +1,9 @@ -// IFF (Amiga/FT2) sample loader +/* IFF (Amiga/FT2) sample loader +** +** Note: Vol/loop sanitation is done in the last stage +** of sample loading, so you don't need to do that here. +** Do NOT close the file handle! +*/ #include <stdio.h> #include <stdint.h> @@ -12,7 +17,8 @@ bool loadIFF(FILE *f, uint32_t filesize) { char hdr[4+1]; - uint32_t sampleLength, sampleVol, sampleLoopStart, sampleLoopLength, sampleRate; + uint32_t length, volume, loopStart, loopLength, sampleRate; + sample_t *s = &tmpSmp; if (filesize < 12) { @@ -23,7 +29,7 @@ bool loadIFF(FILE *f, uint32_t filesize) fseek(f, 8, SEEK_SET); fread(hdr, 1, 4, f); hdr[4] = '\0'; - bool is16Bit = !strncmp(hdr, "16SV", 4); + bool sample16Bit = !strncmp(hdr, "16SV", 4); uint32_t vhdrPtr = 0, vhdrLen = 0; uint32_t bodyPtr = 0, bodyLen = 0; @@ -82,8 +88,8 @@ bool loadIFF(FILE *f, uint32_t filesize) bodyLen = filesize - bodyPtr; fseek(f, vhdrPtr, SEEK_SET); - fread(&sampleLoopStart, 4, 1, f); sampleLoopStart = SWAP32(sampleLoopStart); - fread(&sampleLoopLength, 4, 1, f); sampleLoopLength = SWAP32(sampleLoopLength); + fread(&loopStart, 4, 1, f); loopStart = SWAP32(loopStart); + fread(&loopLength, 4, 1, f); loopLength = SWAP32(loopLength); fseek(f, 4, SEEK_CUR); fread(&sampleRate, 2, 1, f); sampleRate = SWAP16(sampleRate); fseek(f, 1, SEEK_CUR); @@ -94,83 +100,61 @@ bool loadIFF(FILE *f, uint32_t filesize) return false; } - fread(&sampleVol, 4, 1, f); sampleVol = SWAP32(sampleVol); - if (sampleVol > 65535) - sampleVol = 65535; + fread(&volume, 4, 1, f); volume = SWAP32(volume); + if (volume > 65535) + volume = 65535; - sampleVol = (sampleVol + 512) / 1024; // rounded + volume = (volume + 512) / 1024; // rounded - sampleLength = bodyLen; - if (sampleLength > MAX_SAMPLE_LEN) - sampleLength = MAX_SAMPLE_LEN; - - if (sampleLoopStart >= MAX_SAMPLE_LEN || sampleLoopLength > MAX_SAMPLE_LEN) - { - sampleLoopStart = 0; - sampleLoopLength = 0; - } - - if (sampleLoopStart+sampleLoopLength > sampleLength) + length = bodyLen; + if (sample16Bit) { - sampleLoopStart = 0; - sampleLoopLength = 0; + length >>= 1; + loopStart >>= 1; + loopLength >>= 1; } - if (sampleLoopStart > sampleLength-2) - { - sampleLoopStart = 0; - sampleLoopLength = 0; - } + s->length = length; + if (s->length > MAX_SAMPLE_LEN) + s->length = MAX_SAMPLE_LEN; - if (!allocateTmpSmpData(&tmpSmp, sampleLength)) + if (!allocateSmpData(s, length, sample16Bit)) { loaderMsgBox("Not enough memory!"); return false; } fseek(f, bodyPtr, SEEK_SET); - if (fread(tmpSmp.pek, sampleLength, 1, f) != 1) + if (fread(s->dataPtr, length << sample16Bit, 1, f) != 1) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - tmpSmp.len = sampleLength; + s->loopStart = loopStart; + s->loopLength = loopLength; - if (sampleLoopLength > 2) - { - tmpSmp.repS = sampleLoopStart; - tmpSmp.repL = sampleLoopLength; - tmpSmp.typ |= 1; - } + if (s->loopLength > 0) + s->flags |= LOOP_FWD; - if (is16Bit) - { - tmpSmp.len &= 0xFFFFFFFE; - tmpSmp.repS &= 0xFFFFFFFE; - tmpSmp.repL &= 0xFFFFFFFE; - tmpSmp.typ |= 16; - } + if (sample16Bit) + s->flags |= SAMPLE_16BIT; - tmpSmp.vol = (uint8_t)sampleVol; - tmpSmp.pan = 128; + s->volume = (uint8_t)volume; + s->panning = 128; - tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag); + tuneSample(s, sampleRate, audio.linearPeriodsFlag); // set name if (namePtr != 0 && nameLen > 0) { fseek(f, namePtr, SEEK_SET); - if (nameLen > 21) - { - fread(tmpSmp.name, 1, 21, f); - tmpSmp.name[21] = '\0'; - } - else - { - memset(tmpSmp.name, 0, 22); - fread(tmpSmp.name, 1, nameLen, f); - } + + if (nameLen > 22) + nameLen = 22; + + fread(s->name, 1, nameLen, f); + s->name[22] = '\0'; smpFilenameSet = true; } diff --git a/src/smploaders/ft2_load_raw.c b/src/smploaders/ft2_load_raw.c @@ -1,4 +1,7 @@ -// RAW (header-less) sample loader +/* RAW (header-less) sample loader +** +** Note: Do NOT close the file handle! +*/ #include <stdio.h> #include <stdint.h> @@ -10,21 +13,23 @@ bool loadRAW(FILE *f, uint32_t filesize) { - if (!allocateTmpSmpData(&tmpSmp, filesize)) + sample_t *s = &tmpSmp; + + if (!allocateSmpData(s, filesize, false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, filesize, 1, f) != 1) + if (fread(s->dataPtr, filesize, 1, f) != 1) { okBoxThreadSafe(0, "System message", "General I/O error during loading! Is the file in use?"); return false; } - tmpSmp.len = filesize; - tmpSmp.vol = 64; - tmpSmp.pan = 128; + s->length = filesize; + s->volume = 64; + s->panning = 128; return true; } diff --git a/src/smploaders/ft2_load_wav.c b/src/smploaders/ft2_load_wav.c @@ -1,4 +1,9 @@ -// WAV sample loader +/* WAV sample loader +** +** Note: Vol/loop sanitation is done in the last stage +** of sample loading, so you don't need to do that here. +** Do NOT close the file handle! +*/ #include <stdio.h> #include <stdint.h> @@ -20,13 +25,14 @@ static bool wavIsStereo(FILE *f); bool loadWAV(FILE *f, uint32_t filesize) { uint8_t *audioDataU8; - int16_t *audioDataS16, *audioDataS16_2, *ptr16; + int16_t *audioDataS16, *ptr16; uint16_t audioFormat, numChannels, bitsPerSample; int32_t *audioDataS32; uint32_t i, sampleRate, sampleLength; uint32_t len32; float *fAudioDataFloat; double *dAudioDataDouble; + sample_t *s = &tmpSmp; if (filesize < 12) { @@ -172,28 +178,25 @@ bool loadWAV(FILE *f, uint32_t filesize) // ---- READ SAMPLE DATA ---- fseek(f, dataPtr, SEEK_SET); - if (sampleLength > MAX_SAMPLE_LEN) - sampleLength = MAX_SAMPLE_LEN; - int16_t stereoSampleLoadMode = -1; if (wavIsStereo(f)) stereoSampleLoadMode = loaderSysReq(5, "System request", "This is a stereo sample..."); if (bitsPerSample == 8) // 8-BIT INTEGER SAMPLE { - if (!allocateTmpSmpData(&tmpSmp, sampleLength)) + if (!allocateSmpData(s, sampleLength, false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 1, f) != 1) + if (fread(s->dataPtr, sampleLength, 1, f) != 1) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataU8 = (uint8_t *)tmpSmp.pek; + audioDataU8 = (uint8_t *)s->dataPtr; // stereo conversion if (numChannels == 2) @@ -236,24 +239,24 @@ bool loadWAV(FILE *f, uint32_t filesize) // convert from unsigned to signed for (i = 0; i < sampleLength; i++) - tmpSmp.pek[i] ^= 0x80; + s->dataPtr[i] ^= 0x80; } else if (bitsPerSample == 16) // 16-BIT INTEGER SAMPLE { sampleLength /= 2; - if (!allocateTmpSmpData(&tmpSmp, sampleLength*2)) + if (!allocateSmpData(s, sampleLength, true)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 2, f) != 2) + if (fread(s->dataPtr, sampleLength, 2, f) != 2) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataS16 = (int16_t *)tmpSmp.pek; + audioDataS16 = (int16_t *)s->dataPtr; // stereo conversion if (numChannels == 2) @@ -298,28 +301,27 @@ bool loadWAV(FILE *f, uint32_t filesize) } } - sampleLength *= 2; - tmpSmp.typ |= 16; // 16-bit + s->flags |= SAMPLE_16BIT; } else if (bitsPerSample == 24) // 24-BIT INTEGER SAMPLE { sampleLength /= 3; - if (!allocateTmpSmpData(&tmpSmp, (sampleLength * 4) * 2)) + if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(&tmpSmp.pek[sampleLength], sampleLength, 3, f) != 3) + if (fread(&s->dataPtr[sampleLength], sampleLength, 3, f) != 3) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataS32 = (int32_t *)tmpSmp.pek; + audioDataS32 = (int32_t *)s->dataPtr; // convert to 32-bit - audioDataU8 = (uint8_t *)tmpSmp.pek + sampleLength; + audioDataU8 = (uint8_t *)s->dataPtr + sampleLength; for (i = 0; i < sampleLength; i++) { audioDataS32[i] = (audioDataU8[2] << 24) | (audioDataU8[1] << 16) | (audioDataU8[0] << 8); @@ -373,33 +375,28 @@ bool loadWAV(FILE *f, uint32_t filesize) normalizeSigned32Bit(audioDataS32, sampleLength); - // downscale to 16-bit (ultra fast method!) - - audioDataS16 = (int16_t *)tmpSmp.pek; - audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words - - for (i = 0; i < sampleLength; i++, audioDataS16_2++) - audioDataS16[i] = audioDataS16_2[i]; + ptr16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + ptr16[i] = audioDataS32[i] >> 16; - sampleLength *= 2; - tmpSmp.typ |= 16; // 16-bit + s->flags |= SAMPLE_16BIT; } else if (audioFormat == WAV_FORMAT_PCM && bitsPerSample == 32) // 32-BIT INTEGER SAMPLE { - sampleLength /= 4; - if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4)) + sampleLength /= sizeof (int32_t); + if (!allocateSmpData(s, sampleLength * sizeof (int32_t), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 4, f) != 4) + if (fread(s->dataPtr, sampleLength, sizeof (int32_t), f) != sizeof (int32_t)) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - audioDataS32 = (int32_t *)tmpSmp.pek; + audioDataS32 = (int32_t *)s->dataPtr; // stereo conversion if (numChannels == 2) @@ -448,33 +445,28 @@ bool loadWAV(FILE *f, uint32_t filesize) normalizeSigned32Bit(audioDataS32, sampleLength); - // downscale to 16-bit (ultra fast method!) - - audioDataS16 = (int16_t *)tmpSmp.pek; - audioDataS16_2 = (int16_t *)tmpSmp.pek + 1; // yes, this is aligned to words - - for (i = 0; i < sampleLength; i++, audioDataS16_2++) - audioDataS16[i] = audioDataS16_2[i]; + ptr16 = (int16_t *)s->dataPtr; + for (i = 0; i < sampleLength; i++) + ptr16[i] = audioDataS32[i] >> 16; - sampleLength *= 2; - tmpSmp.typ |= 16; // 16-bit + s->flags |= SAMPLE_16BIT; } else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 32) // 32-BIT FLOATING POINT SAMPLE { - sampleLength /= 4; - if (!allocateTmpSmpData(&tmpSmp, sampleLength * 4)) + sampleLength /= sizeof (float); + if (!allocateSmpData(s, sampleLength * sizeof (float), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 4, f) != 4) + if (fread(s->dataPtr, sampleLength, sizeof (float), f) != sizeof (float)) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - fAudioDataFloat = (float *)tmpSmp.pek; + fAudioDataFloat = (float *)s->dataPtr; // stereo conversion if (numChannels == 2) @@ -517,29 +509,31 @@ bool loadWAV(FILE *f, uint32_t filesize) normalize32BitFloatToSigned16Bit(fAudioDataFloat, sampleLength); - ptr16 = (int16_t *)tmpSmp.pek; + ptr16 = (int16_t *)s->dataPtr; for (i = 0; i < sampleLength; i++) - ptr16[i] = (int16_t)fAudioDataFloat[i]; // should use SIMD if available + { + const int32_t smp32 = (const int32_t)fAudioDataFloat[i]; + ptr16[i] = (int16_t)smp32; + } - sampleLength *= 2; - tmpSmp.typ |= 16; // 16-bit + s->flags |= SAMPLE_16BIT; } else if (audioFormat == WAV_FORMAT_IEEE_FLOAT && bitsPerSample == 64) // 64-BIT FLOATING POINT SAMPLE { - sampleLength /= 8; - if (!allocateTmpSmpData(&tmpSmp, sampleLength * 8)) + sampleLength /= sizeof (double); + if (!allocateSmpData(s, sampleLength * sizeof (double), false)) { loaderMsgBox("Not enough memory!"); return false; } - if (fread(tmpSmp.pek, sampleLength, 8, f) != 8) + if (fread(s->dataPtr, sampleLength, sizeof (double), f) != sizeof (double)) { loaderMsgBox("General I/O error during loading! Is the file in use?"); return false; } - dAudioDataDouble = (double *)tmpSmp.pek; + dAudioDataDouble = (double *)s->dataPtr; // stereo conversion if (numChannels == 2) @@ -582,21 +576,27 @@ bool loadWAV(FILE *f, uint32_t filesize) normalize64BitFloatToSigned16Bit(dAudioDataDouble, sampleLength); - ptr16 = (int16_t *)tmpSmp.pek; + ptr16 = (int16_t *)s->dataPtr; for (i = 0; i < sampleLength; i++) - ptr16[i] = (int16_t)dAudioDataDouble[i]; // should use SIMD if available + { + const int32_t smp32 = (const int32_t)dAudioDataDouble[i]; + ptr16[i] = (int16_t)smp32; + } - sampleLength *= 2; - tmpSmp.typ |= 16; // 16-bit + s->flags |= SAMPLE_16BIT; } - reallocateTmpSmpData(&tmpSmp, sampleLength); // readjust memory needed + if (sampleLength > MAX_SAMPLE_LEN) + sampleLength = MAX_SAMPLE_LEN; + + bool sample16Bit = !!(s->flags & SAMPLE_16BIT); + reallocateSmpData(s, sampleLength, sample16Bit); // readjust memory needed - tuneSample(&tmpSmp, sampleRate, audio.linearPeriodsFlag); + tuneSample(s, sampleRate, audio.linearPeriodsFlag); - tmpSmp.vol = 64; - tmpSmp.pan = 128; - tmpSmp.len = sampleLength; + s->volume = 64; + s->panning = 128; + s->length = sampleLength; // ---- READ "smpl" chunk ---- if (smplPtr != 0 && smplLen > 52) @@ -612,19 +612,14 @@ bool loadWAV(FILE *f, uint32_t filesize) fread(&loopType, 4, 1, f); fread(&loopStart, 4, 1, f); - fread(&loopEnd, 4, 1, f); loopEnd++; - - if (tmpSmp.typ & 16) - { - loopStart *= 2; - loopEnd *= 2; - } + fread(&loopEnd, 4, 1, f); + loopEnd++; if (loopEnd <= sampleLength) { - tmpSmp.repS = loopStart; - tmpSmp.repL = loopEnd - loopStart; - tmpSmp.typ |= (loopType == 0) ? 1 : 2; + s->loopStart = loopStart; + s->loopLength = loopEnd - loopStart; + s->flags |= (loopType == 0) ? LOOP_FWD : LOOP_BIDI; } } } @@ -646,7 +641,7 @@ bool loadWAV(FILE *f, uint32_t filesize) if (tmpPan > 255) tmpPan = 255; - tmpSmp.pan = (uint8_t)tmpPan; + s->panning = (uint8_t)tmpPan; } else { @@ -659,7 +654,7 @@ bool loadWAV(FILE *f, uint32_t filesize) if (tmpVol > 256) tmpVol = 256; - tmpSmp.vol = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded) + s->volume = (uint8_t)((tmpVol + 2) / 4); // 0..256 -> 0..64 (rounded) } // --------------------------- @@ -667,20 +662,11 @@ bool loadWAV(FILE *f, uint32_t filesize) if (inamPtr != 0 && inamLen > 0) { fseek(f, inamPtr, SEEK_SET); + if (inamLen > 22) + inamLen = 22; - uint32_t len = 22; - if (len > inamLen) - len = inamLen; - - memset(tmpSmp.name, 0, sizeof (tmpSmp.name)); - for (i = 0; i < len; i++) - { - char chr = (char)fgetc(f); - if (chr == '\0') - break; - - tmpSmp.name[i] = chr; - } + fread(s->name, 1, inamLen, f); + s->name[22] = '\0'; smpFilenameSet = true; } diff --git a/vs2019_project/ft2-clone/16.wav b/vs2019_project/ft2-clone/16.wav Binary files differ. diff --git a/vs2019_project/ft2-clone/ft2-clone.vcxproj b/vs2019_project/ft2-clone/ft2-clone.vcxproj @@ -92,7 +92,7 @@ <Optimization>MaxSpeed</Optimization> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions> + <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions> <MultiProcessorCompilation>true</MultiProcessorCompilation> <IntrinsicFunctions>true</IntrinsicFunctions> <StringPooling>true</StringPooling> @@ -100,7 +100,6 @@ <BasicRuntimeChecks>Default</BasicRuntimeChecks> <FunctionLevelLinking>true</FunctionLevelLinking> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <FloatingPointModel>Fast</FloatingPointModel> <OmitFramePointers>true</OmitFramePointers> <BufferSecurityCheck>false</BufferSecurityCheck> <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet> @@ -108,6 +107,7 @@ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> <RuntimeTypeInfo>false</RuntimeTypeInfo> + <FloatingPointModel>Fast</FloatingPointModel> </ClCompile> <Link> <AdditionalLibraryDirectories> @@ -142,14 +142,13 @@ <Optimization>MaxSpeed</Optimization> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions> + <PreprocessorDefinitions>__WINDOWS_MM__;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions> <MultiProcessorCompilation>true</MultiProcessorCompilation> <IntrinsicFunctions>true</IntrinsicFunctions> <StringPooling>true</StringPooling> <MinimalRebuild>false</MinimalRebuild> <FunctionLevelLinking>true</FunctionLevelLinking> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> - <FloatingPointModel>Fast</FloatingPointModel> <OmitFramePointers>true</OmitFramePointers> <BasicRuntimeChecks>Default</BasicRuntimeChecks> <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion> @@ -157,6 +156,7 @@ <DebugInformationFormat>None</DebugInformationFormat> <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> <RuntimeTypeInfo>false</RuntimeTypeInfo> + <FloatingPointModel>Fast</FloatingPointModel> </ClCompile> <Link> <AdditionalLibraryDirectories> @@ -199,11 +199,11 @@ <WarningLevel>Level4</WarningLevel> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions> - <FloatingPointModel>Fast</FloatingPointModel> + <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions> <EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet> <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> <MultiProcessorCompilation>true</MultiProcessorCompilation> + <FloatingPointModel>Fast</FloatingPointModel> </ClCompile> <Link> <AdditionalLibraryDirectories> @@ -238,13 +238,13 @@ <WarningLevel>Level4</WarningLevel> <AdditionalIncludeDirectories> </AdditionalIncludeDirectories> - <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI</PreprocessorDefinitions> - <FloatingPointModel>Fast</FloatingPointModel> + <PreprocessorDefinitions>__WINDOWS_MM__;_CRTDBG_MAP_ALLOC;DEBUG;_DEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;HAS_MIDI;HAS_LIBFLAC</PreprocessorDefinitions> <StringPooling> </StringPooling> <OmitFramePointers>false</OmitFramePointers> <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> <MultiProcessorCompilation>true</MultiProcessorCompilation> + <FloatingPointModel>Fast</FloatingPointModel> </ClCompile> <Link> <AdditionalLibraryDirectories> @@ -318,8 +318,6 @@ <ClCompile Include="..\..\src\ft2_sample_ed.c" /> <ClCompile Include="..\..\src\ft2_sample_loader.c" /> <ClCompile Include="..\..\src\ft2_sample_saver.c" /> - <ClCompile Include="..\..\src\ft2_scopedraw.c" /> - <ClCompile Include="..\..\src\ft2_scopes.c" /> <ClCompile Include="..\..\src\ft2_scrollbars.c" /> <ClCompile Include="..\..\src\ft2_structs.c" /> <ClCompile Include="..\..\src\ft2_sysreqs.c" /> @@ -338,6 +336,19 @@ <ClCompile Include="..\..\src\gfxdata\ft2_bmp_instr.c" /> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_looppins.c" /> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_scopes.c" /> + <ClCompile Include="..\..\src\libflac\bitmath.c" /> + <ClCompile Include="..\..\src\libflac\bitreader.c" /> + <ClCompile Include="..\..\src\libflac\crc.c" /> + <ClCompile Include="..\..\src\libflac\fixed.c" /> + <ClCompile Include="..\..\src\libflac\format.c" /> + <ClCompile Include="..\..\src\libflac\lpc.c" /> + <ClCompile Include="..\..\src\libflac\md5.c" /> + <ClCompile Include="..\..\src\libflac\memory.c" /> + <ClCompile Include="..\..\src\libflac\metadata_iterators.c" /> + <ClCompile Include="..\..\src\libflac\metadata_object.c" /> + <ClCompile Include="..\..\src\libflac\stream_decoder.c" /> + <ClCompile Include="..\..\src\libflac\window.c" /> + <ClCompile Include="..\..\src\libflac\windows_unicode_filenames.c" /> <ClCompile Include="..\..\src\mixer\ft2_windowed_sinc.c" /> <ClCompile Include="..\..\src\mixer\ft2_mix.c" /> <ClCompile Include="..\..\src\mixer\ft2_center_mix.c" /> @@ -359,12 +370,11 @@ <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Sync</ExceptionHandling> <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Sync</ExceptionHandling> <ExceptionHandling Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Sync</ExceptionHandling> - <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">4245;4996;4267</DisableSpecificWarnings> - <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Release|x64'">4245;4996;4267</DisableSpecificWarnings> - <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">4245;4996;4267</DisableSpecificWarnings> - <DisableSpecificWarnings Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">4245;4996;4267</DisableSpecificWarnings> </ClCompile> + <ClCompile Include="..\..\src\scopes\ft2_scopedraw.c" /> + <ClCompile Include="..\..\src\scopes\ft2_scopes.c" /> <ClCompile Include="..\..\src\smploaders\ft2_load_aiff.c" /> + <ClCompile Include="..\..\src\smploaders\ft2_load_flac.c" /> <ClCompile Include="..\..\src\smploaders\ft2_load_iff.c" /> <ClCompile Include="..\..\src\smploaders\ft2_load_raw.c" /> <ClCompile Include="..\..\src\smploaders\ft2_load_wav.c" /> @@ -376,6 +386,7 @@ <ClInclude Include="..\..\src\ft2_bmp.h" /> <ClInclude Include="..\..\src\ft2_checkboxes.h" /> <ClInclude Include="..\..\src\ft2_config.h" /> + <ClInclude Include="..\..\src\ft2_cpu.h" /> <ClInclude Include="..\..\src\ft2_diskop.h" /> <ClInclude Include="..\..\src\ft2_edit.h" /> <ClInclude Include="..\..\src\ft2_events.h" /> @@ -402,7 +413,6 @@ <ClInclude Include="..\..\src\ft2_sample_loader.h" /> <ClInclude Include="..\..\src\ft2_sample_saver.h" /> <ClInclude Include="..\..\src\ft2_scopedraw.h" /> - <ClInclude Include="..\..\src\ft2_scopes.h" /> <ClInclude Include="..\..\src\ft2_scrollbars.h" /> <ClInclude Include="..\..\src\ft2_structs.h" /> <ClInclude Include="..\..\src\ft2_sysreqs.h" /> @@ -419,6 +429,9 @@ <ClInclude Include="..\..\src\mixer\ft2_silence_mix.h" /> <ClInclude Include="..\..\src\rtmidi\RtMidi.h" /> <ClInclude Include="..\..\src\rtmidi\rtmidi_c.h" /> + <ClInclude Include="..\..\src\scopes\ft2_scopedraw.h" /> + <ClInclude Include="..\..\src\scopes\ft2_scopes.h" /> + <ClInclude Include="..\..\src\scopes\ft2_scope_macros.h" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\src\ft2-clone.rc" /> diff --git a/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters b/vs2019_project/ft2-clone/ft2-clone.vcxproj.filters @@ -11,7 +11,6 @@ <ClCompile Include="..\..\src\ft2_edit.c" /> <ClCompile Include="..\..\src\ft2_events.c" /> <ClCompile Include="..\..\src\ft2_gui.c" /> - <ClCompile Include="..\..\src\ft2_help.c" /> <ClCompile Include="..\..\src\ft2_inst_ed.c" /> <ClCompile Include="..\..\src\ft2_keyboard.c" /> <ClCompile Include="..\..\src\ft2_main.c" /> @@ -31,8 +30,6 @@ <ClCompile Include="..\..\src\ft2_sample_loader.c" /> <ClCompile Include="..\..\src\ft2_sample_saver.c" /> <ClCompile Include="..\..\src\ft2_sampling.c" /> - <ClCompile Include="..\..\src\ft2_scopedraw.c" /> - <ClCompile Include="..\..\src\ft2_scopes.c" /> <ClCompile Include="..\..\src\ft2_scrollbars.c" /> <ClCompile Include="..\..\src\ft2_structs.c" /> <ClCompile Include="..\..\src\ft2_sysreqs.c" /> @@ -49,31 +46,31 @@ <Filter>rtmidi</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_fonts.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_gui.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_instr.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_logo.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_looppins.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_midi.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_mouse.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_nibbles.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\gfxdata\ft2_bmp_scopes.c"> - <Filter>graphics</Filter> + <Filter>libflac\graphics</Filter> </ClCompile> <ClCompile Include="..\..\src\mixer\ft2_mix.c"> <Filter>mixer</Filter> @@ -117,6 +114,55 @@ <ClCompile Include="..\..\src\smploaders\ft2_load_wav.c"> <Filter>smploaders</Filter> </ClCompile> + <ClCompile Include="..\..\src\smploaders\ft2_load_flac.c"> + <Filter>smploaders</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\bitmath.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\bitreader.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\crc.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\fixed.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\format.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\lpc.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\md5.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\memory.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\metadata_iterators.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\metadata_object.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\stream_decoder.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\libflac\window.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\ft2_help.c" /> + <ClCompile Include="..\..\src\libflac\windows_unicode_filenames.c"> + <Filter>libflac</Filter> + </ClCompile> + <ClCompile Include="..\..\src\scopes\ft2_scopedraw.c"> + <Filter>scopes</Filter> + </ClCompile> + <ClCompile Include="..\..\src\scopes\ft2_scopes.c"> + <Filter>scopes</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\src\rtmidi\RtMidi.h"> @@ -221,9 +267,6 @@ <ClInclude Include="..\..\src\ft2_scopedraw.h"> <Filter>headers</Filter> </ClInclude> - <ClInclude Include="..\..\src\ft2_scopes.h"> - <Filter>headers</Filter> - </ClInclude> <ClInclude Include="..\..\src\ft2_scrollbars.h"> <Filter>headers</Filter> </ClInclude> @@ -266,14 +309,23 @@ <ClInclude Include="..\..\src\mixer\ft2_windowed_sinc.h"> <Filter>mixer</Filter> </ClInclude> + <ClInclude Include="..\..\src\scopes\ft2_scope_macros.h"> + <Filter>scopes</Filter> + </ClInclude> + <ClInclude Include="..\..\src\scopes\ft2_scopedraw.h"> + <Filter>scopes</Filter> + </ClInclude> + <ClInclude Include="..\..\src\scopes\ft2_scopes.h"> + <Filter>scopes</Filter> + </ClInclude> + <ClInclude Include="..\..\src\ft2_cpu.h"> + <Filter>headers</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Filter Include="headers"> <UniqueIdentifier>{559f399e-331e-4688-8a7d-328ca6739456}</UniqueIdentifier> </Filter> - <Filter Include="graphics"> - <UniqueIdentifier>{c6fad604-509b-4072-b181-d47835f08428}</UniqueIdentifier> - </Filter> <Filter Include="mixer"> <UniqueIdentifier>{5c40c417-c4bb-4cf2-b71b-c557bf0a86cd}</UniqueIdentifier> </Filter> @@ -286,6 +338,15 @@ <Filter Include="rtmidi"> <UniqueIdentifier>{95e882a1-e589-4684-a3cb-48e0a1d073aa}</UniqueIdentifier> </Filter> + <Filter Include="libflac"> + <UniqueIdentifier>{13e9047f-28ee-414f-9e23-4ad54deb281e}</UniqueIdentifier> + </Filter> + <Filter Include="scopes"> + <UniqueIdentifier>{8ac888cf-e1a5-4633-bcca-240a8eae2e23}</UniqueIdentifier> + </Filter> + <Filter Include="libflac\graphics"> + <UniqueIdentifier>{c6fad604-509b-4072-b181-d47835f08428}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\src\ft2-clone.rc" />