Full Code of libretro/libretro-common for AI

master e2e3eccfd245 cached
434 files
4.4 MB
1.2M tokens
1 requests
Download .txt
Showing preview only (4,733K chars total). Download the full file or copy to clipboard to get everything.
Repository: libretro/libretro-common
Branch: master
Commit: e2e3eccfd245
Files: 434
Total size: 4.4 MB

Directory structure:
gitextract_6fnfu5rl/

├── .gitignore
├── Makefile.test
├── audio/
│   ├── audio_mix.c
│   ├── audio_mixer.c
│   ├── conversion/
│   │   ├── float_to_s16.c
│   │   ├── float_to_s16_neon.S
│   │   ├── float_to_s16_neon.c
│   │   ├── mono_to_stereo_float.c
│   │   ├── s16_to_float.c
│   │   ├── s16_to_float_neon.S
│   │   ├── s16_to_float_neon.c
│   │   └── stereo_to_mono_float.c
│   ├── dsp_filter.c
│   ├── dsp_filters/
│   │   ├── BassBoost.dsp
│   │   ├── ChipTune-Lowpass.dsp
│   │   ├── ChipTuneEnhance.dsp
│   │   ├── Chorus.dsp
│   │   ├── Crystalizer.dsp
│   │   ├── EQ.dsp
│   │   ├── Echo.dsp
│   │   ├── EchoReverb.dsp
│   │   ├── HighShelfDampen.dsp
│   │   ├── IIR.dsp
│   │   ├── LowPassCPS.dsp
│   │   ├── Makefile
│   │   ├── Mono.dsp
│   │   ├── Panning.dsp
│   │   ├── Phaser.dsp
│   │   ├── Reverb.dsp
│   │   ├── Tremolo.dsp
│   │   ├── Vibrato.dsp
│   │   ├── WahWah.dsp
│   │   ├── chorus.c
│   │   ├── configure
│   │   ├── crystalizer.c
│   │   ├── echo.c
│   │   ├── eq.c
│   │   ├── fft/
│   │   │   ├── fft.c
│   │   │   └── fft.h
│   │   ├── iir.c
│   │   ├── link.T
│   │   ├── panning.c
│   │   ├── phaser.c
│   │   ├── reverb.c
│   │   ├── tremolo.c
│   │   ├── vibrato.c
│   │   └── wahwah.c
│   └── resampler/
│       ├── audio_resampler.c
│       └── drivers/
│           ├── nearest_resampler.c
│           ├── sinc_resampler.c
│           └── sinc_resampler_neon.S
├── cdrom/
│   └── cdrom.c
├── compat/
│   ├── compat_fnmatch.c
│   ├── compat_getopt.c
│   ├── compat_ifaddrs.c
│   ├── compat_posix_string.c
│   ├── compat_snprintf.c
│   ├── compat_strcasestr.c
│   ├── compat_strl.c
│   ├── compat_strldup.c
│   ├── compat_vscprintf.c
│   └── fopen_utf8.c
├── crt/
│   ├── include/
│   │   └── string.h
│   └── string.c
├── dynamic/
│   └── dylib.c
├── encodings/
│   ├── encoding_base64.c
│   ├── encoding_crc32.c
│   └── encoding_utf.c
├── features/
│   └── features_cpu.c
├── file/
│   ├── archive_file.c
│   ├── archive_file_7z.c
│   ├── archive_file_zlib.c
│   ├── archive_file_zstd.c
│   ├── config_file.c
│   ├── config_file_userdata.c
│   ├── file_path.c
│   ├── file_path_io.c
│   ├── nbio/
│   │   ├── nbio_intf.c
│   │   ├── nbio_linux.c
│   │   ├── nbio_stdio.c
│   │   ├── nbio_unixmmap.c
│   │   └── nbio_windowsmmap.c
│   └── retro_dirent.c
├── formats/
│   ├── bmp/
│   │   ├── rbmp.c
│   │   └── rbmp_encode.c
│   ├── cdfs/
│   │   └── cdfs.c
│   ├── image_texture.c
│   ├── image_transfer.c
│   ├── jpeg/
│   │   └── rjpeg.c
│   ├── json/
│   │   └── rjson.c
│   ├── libchdr/
│   │   ├── libchdr_bitstream.c
│   │   ├── libchdr_cdrom.c
│   │   ├── libchdr_chd.c
│   │   ├── libchdr_flac.c
│   │   ├── libchdr_flac_codec.c
│   │   ├── libchdr_huffman.c
│   │   ├── libchdr_lzma.c
│   │   ├── libchdr_zlib.c
│   │   └── libchdr_zstd.c
│   ├── logiqx_dat/
│   │   └── logiqx_dat.c
│   ├── m3u/
│   │   └── m3u_file.c
│   ├── png/
│   │   ├── rpng.c
│   │   ├── rpng_encode.c
│   │   └── rpng_internal.h
│   ├── tga/
│   │   └── rtga.c
│   ├── wav/
│   │   └── rwav.c
│   ├── webp/
│   │   └── rwebp.c
│   └── xml/
│       └── rxml.c
├── gfx/
│   ├── gl_capabilities.c
│   └── scaler/
│       ├── pixconv.c
│       ├── scaler.c
│       ├── scaler_filter.c
│       └── scaler_int.c
├── glsym/
│   ├── README.md
│   ├── glgen.py
│   ├── glsym_es2.c
│   ├── glsym_es3.c
│   ├── glsym_gl.c
│   ├── rglgen.c
│   ├── rglgen.py
│   └── xglgen.py
├── hash/
│   └── lrc_hash.c
├── include/
│   ├── array/
│   │   ├── rbuf.h
│   │   └── rhmap.h
│   ├── audio/
│   │   ├── audio_mix.h
│   │   ├── audio_mixer.h
│   │   ├── audio_resampler.h
│   │   ├── conversion/
│   │   │   ├── dual_mono.h
│   │   │   ├── float_to_s16.h
│   │   │   └── s16_to_float.h
│   │   └── dsp_filter.h
│   ├── boolean.h
│   ├── cdrom/
│   │   └── cdrom.h
│   ├── clamping.h
│   ├── compat/
│   │   ├── apple_compat.h
│   │   ├── fnmatch.h
│   │   ├── fopen_utf8.h
│   │   ├── getopt.h
│   │   ├── ifaddrs.h
│   │   ├── intrinsics.h
│   │   ├── msvc/
│   │   │   └── stdint.h
│   │   ├── msvc.h
│   │   ├── posix_string.h
│   │   ├── strcasestr.h
│   │   ├── strl.h
│   │   └── zlib/
│   │       ├── zconf.h
│   │       └── zlib.h
│   ├── defines/
│   │   ├── cocoa_defines.h
│   │   ├── d3d_defines.h
│   │   ├── gx_defines.h
│   │   ├── ps3_defines.h
│   │   ├── ps4_defines.h
│   │   └── psp_defines.h
│   ├── dynamic/
│   │   └── dylib.h
│   ├── encodings/
│   │   ├── base64.h
│   │   ├── crc32.h
│   │   ├── utf.h
│   │   └── win32.h
│   ├── fastcpy.h
│   ├── features/
│   │   └── features_cpu.h
│   ├── file/
│   │   ├── archive_file.h
│   │   ├── config_file.h
│   │   ├── config_file_userdata.h
│   │   ├── file_path.h
│   │   └── nbio.h
│   ├── filters.h
│   ├── formats/
│   │   ├── cdfs.h
│   │   ├── image.h
│   │   ├── logiqx_dat.h
│   │   ├── m3u_file.h
│   │   ├── rbmp.h
│   │   ├── rjpeg.h
│   │   ├── rjson.h
│   │   ├── rjson_helpers.h
│   │   ├── rpng.h
│   │   ├── rtga.h
│   │   ├── rwav.h
│   │   ├── rwebp.h
│   │   └── rxml.h
│   ├── gfx/
│   │   ├── gl_capabilities.h
│   │   ├── math/
│   │   │   ├── matrix_3x3.h
│   │   │   ├── matrix_4x4.h
│   │   │   ├── vector_2.h
│   │   │   ├── vector_3.h
│   │   │   └── vector_4.h
│   │   ├── scaler/
│   │   │   ├── filter.h
│   │   │   ├── pixconv.h
│   │   │   ├── scaler.h
│   │   │   └── scaler_int.h
│   │   └── video_frame.h
│   ├── glsym/
│   │   ├── glsym.h
│   │   ├── glsym_es2.h
│   │   ├── glsym_es3.h
│   │   ├── glsym_gl.h
│   │   ├── rglgen.h
│   │   ├── rglgen_headers.h
│   │   ├── rglgen_private_headers.h
│   │   └── switch/
│   │       ├── nx_gl.h
│   │       └── nx_glsym.h
│   ├── libchdr/
│   │   ├── bitstream.h
│   │   ├── cdrom.h
│   │   ├── chd.h
│   │   ├── chdconfig.h
│   │   ├── coretypes.h
│   │   ├── flac.h
│   │   ├── huffman.h
│   │   ├── libchdr_zlib.h
│   │   ├── libchdr_zstd.h
│   │   ├── lzma.h
│   │   └── minmax.h
│   ├── libco.h
│   ├── libretro.h
│   ├── libretro_d3d.h
│   ├── libretro_d3d11.h
│   ├── libretro_d3d12.h
│   ├── libretro_dspfilter.h
│   ├── libretro_gskit_ps2.h
│   ├── libretro_vulkan.h
│   ├── lists/
│   │   ├── dir_list.h
│   │   ├── file_list.h
│   │   ├── linked_list.h
│   │   ├── nested_list.h
│   │   └── string_list.h
│   ├── lrc_hash.h
│   ├── math/
│   │   ├── complex.h
│   │   ├── float_minmax.h
│   │   └── fxp.h
│   ├── media/
│   │   └── media_detect_cd.h
│   ├── memalign.h
│   ├── memmap.h
│   ├── net/
│   │   ├── net_compat.h
│   │   ├── net_http.h
│   │   ├── net_http_parse.h
│   │   ├── net_ifinfo.h
│   │   ├── net_socket.h
│   │   └── net_socket_ssl.h
│   ├── playlists/
│   │   └── label_sanitization.h
│   ├── queues/
│   │   ├── fifo_queue.h
│   │   ├── generic_queue.h
│   │   ├── message_queue.h
│   │   └── task_queue.h
│   ├── retro_assert.h
│   ├── retro_atomic.h
│   ├── retro_common.h
│   ├── retro_common_api.h
│   ├── retro_dirent.h
│   ├── retro_endianness.h
│   ├── retro_environment.h
│   ├── retro_inline.h
│   ├── retro_math.h
│   ├── retro_miscellaneous.h
│   ├── retro_spsc.h
│   ├── retro_timers.h
│   ├── rthreads/
│   │   ├── async_job.h
│   │   ├── rthreads.h
│   │   └── tpool.h
│   ├── streams/
│   │   ├── chd_stream.h
│   │   ├── file_stream.h
│   │   ├── file_stream_transforms.h
│   │   ├── interface_stream.h
│   │   ├── memory_stream.h
│   │   ├── network_stream.h
│   │   ├── rzip_stream.h
│   │   ├── stdin_stream.h
│   │   └── trans_stream.h
│   ├── string/
│   │   └── stdstring.h
│   ├── time/
│   │   └── rtime.h
│   ├── utils/
│   │   └── md5.h
│   ├── vfs/
│   │   ├── vfs.h
│   │   ├── vfs_implementation.h
│   │   ├── vfs_implementation_cdrom.h
│   │   └── vfs_implementation_saf.h
│   └── vulkan/
│       └── vulkan_symbol_wrapper.h
├── libco/
│   ├── aarch64.c
│   ├── amd64.c
│   ├── armeabi.c
│   ├── fiber.c
│   ├── genode.cpp
│   ├── libco.c
│   ├── ppc.c
│   ├── ps2.c
│   ├── ps3.S
│   ├── psp1.c
│   ├── psp2.c
│   ├── scefiber.c
│   ├── sjlj.c
│   ├── ucontext.c
│   └── x86.c
├── lists/
│   ├── dir_list.c
│   ├── file_list.c
│   ├── linked_list.c
│   ├── nested_list.c
│   ├── string_list.c
│   └── vector_list.c
├── media/
│   └── media_detect_cd.c
├── memmap/
│   ├── memalign.c
│   └── memmap.c
├── net/
│   ├── cacert.h
│   ├── net_compat.c
│   ├── net_http.c
│   ├── net_http_parse.c
│   ├── net_ifinfo.c
│   ├── net_socket.c
│   ├── net_socket_ssl_bear.c
│   └── net_socket_ssl_mbed.c
├── playlists/
│   └── label_sanitization.c
├── queues/
│   ├── fifo_queue.c
│   ├── generic_queue.c
│   ├── message_queue.c
│   ├── retro_spsc.c
│   └── task_queue.c
├── rthreads/
│   ├── ctr_pthread.h
│   ├── gx_pthread.h
│   ├── psp_pthread.h
│   ├── rthreads.c
│   ├── tpool.c
│   └── xenon_sdl_threads.c
├── samples/
│   ├── atomic/
│   │   ├── retro_atomic_extern_c_linkage/
│   │   │   ├── Makefile
│   │   │   └── retro_atomic_extern_c_linkage_test.cpp
│   │   └── retro_atomic_test/
│   │       ├── Makefile
│   │       └── retro_atomic_test.c
│   ├── compat/
│   │   ├── fnmatch/
│   │   │   ├── Makefile
│   │   │   └── compat_fnmatch_test.c
│   │   └── snprintf/
│   │       ├── Makefile
│   │       └── snprintf_test.c
│   ├── core_options/
│   │   ├── README.md
│   │   ├── example_categories/
│   │   │   ├── conversion_scripts/
│   │   │   │   ├── core_option_regex.py
│   │   │   │   └── v1_to_v2_converter.py
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   ├── example_default/
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   ├── example_hide_option/
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   └── example_translation/
│   │       ├── .github/
│   │       │   └── workflows/
│   │       │       ├── crowdin_initial_setup.yml
│   │       │       ├── crowdin_source_upload.yml
│   │       │       └── crowdin_translation_sync.yml
│   │       ├── instructions.txt
│   │       ├── intl/
│   │       │   ├── .gitignore
│   │       │   ├── activate.py
│   │       │   ├── core_option_regex.py
│   │       │   ├── core_option_translation.py
│   │       │   ├── crowdin.yaml
│   │       │   ├── crowdin_prep.py
│   │       │   ├── crowdin_source_upload.py
│   │       │   ├── crowdin_translate.py
│   │       │   ├── crowdin_translation_download.py
│   │       │   ├── download_workflow.py
│   │       │   ├── initial_sync.py
│   │       │   ├── remove_initial_cycle.py
│   │       │   ├── upload_workflow.py
│   │       │   └── v1_to_v2_converter.py
│   │       ├── libretro_core_options.h
│   │       └── libretro_core_options_intl.h
│   ├── encodings/
│   │   └── base64/
│   │       ├── Makefile
│   │       └── unbase64_test.c
│   ├── file/
│   │   ├── archive_file/
│   │   │   ├── Makefile
│   │   │   └── archive_zip_test.c
│   │   ├── archive_zstd/
│   │   │   ├── Makefile
│   │   │   └── archive_zstd_test.c
│   │   ├── config_file/
│   │   │   ├── Makefile
│   │   │   └── config_file_test.c
│   │   ├── file_path/
│   │   │   ├── Makefile
│   │   │   ├── fill_pathname_test.c
│   │   │   └── path_resolve_realpath_test.c
│   │   ├── nbio/
│   │   │   ├── Makefile
│   │   │   └── nbio_test.c
│   │   ├── vfs/
│   │   │   ├── Makefile
│   │   │   └── vfs_read_overflow_test.c
│   │   └── vfs_cdrom/
│   │       ├── Makefile
│   │       └── cdrom_cuesheet_overflow_test.c
│   ├── formats/
│   │   ├── bmp/
│   │   │   ├── Makefile
│   │   │   └── rbmp_test.c
│   │   ├── cdfs/
│   │   │   ├── Makefile
│   │   │   └── cdfs_dir_record_test.c
│   │   ├── json/
│   │   │   ├── Makefile
│   │   │   └── rjson_test.c
│   │   ├── png/
│   │   │   ├── Makefile
│   │   │   ├── rpng_chunk_overflow_test.c
│   │   │   ├── rpng_roundtrip_test.c
│   │   │   └── rpng_test.c
│   │   ├── tga/
│   │   │   ├── Makefile
│   │   │   └── rtga_test.c
│   │   └── xml/
│   │       ├── Makefile
│   │       └── rxml_test.c
│   ├── net/
│   │   ├── Makefile
│   │   ├── net_http_parse_test.c
│   │   ├── net_http_test.c
│   │   ├── net_ifinfo_test.c
│   │   └── udp-test.c
│   ├── queues/
│   │   ├── retro_spsc_test/
│   │   │   ├── Makefile
│   │   │   └── retro_spsc_test.c
│   │   └── task_queue_title_error_test/
│   │       ├── Makefile
│   │       └── task_queue_title_error_test.c
│   ├── rthreads/
│   │   └── tpool_wait_test/
│   │       ├── Makefile
│   │       └── tpool_wait_test.c
│   ├── streams/
│   │   ├── chd/
│   │   │   ├── Makefile
│   │   │   └── chd_meta_overflow_test.c
│   │   └── rzip/
│   │       ├── Makefile
│   │       ├── rzip.c
│   │       └── rzip_chunk_size_test.c
│   └── string/
│       ├── strlcpy_append/
│       │   ├── Makefile
│       │   └── strlcpy_append_test.c
│       └── word_wrap_overflow_test/
│           ├── Makefile
│           └── word_wrap_overflow_test.c
├── streams/
│   ├── chd_stream.c
│   ├── file_stream.c
│   ├── file_stream_transforms.c
│   ├── interface_stream.c
│   ├── memory_stream.c
│   ├── network_stream.c
│   ├── rzip_stream.c
│   ├── stdin_stream.c
│   ├── trans_stream.c
│   ├── trans_stream_pipe.c
│   └── trans_stream_zlib.c
├── string/
│   └── stdstring.c
├── test/
│   ├── formats/
│   │   └── test_rpng.c
│   ├── hash/
│   │   └── test_hash.c
│   ├── lists/
│   │   └── test_linked_list.c
│   ├── queues/
│   │   └── test_generic_queue.c
│   ├── string/
│   │   └── test_stdstring.c
│   └── utils/
│       └── test_utils.c
├── time/
│   └── rtime.c
├── utils/
│   ├── md5.c
│   └── sha1.c
├── vfs/
│   ├── saf/
│   │   └── src/
│   │       └── com/
│   │           └── libretro/
│   │               └── common/
│   │                   └── vfs/
│   │                       └── VfsImplementationSaf.java
│   ├── vfs_implementation.c
│   ├── vfs_implementation_cdrom.c
│   ├── vfs_implementation_saf.c
│   ├── vfs_implementation_smb.c
│   ├── vfs_implementation_smb.h
│   └── vfs_implementation_uwp.cpp
└── vulkan/
    └── vulkan_symbol_wrapper.c

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
glsm/
*.[od]
*.dll
*.so
*.dylib
*.exe


================================================
FILE: Makefile.test
================================================

OBJDIR = ../obj-unix

TEST_UNIT_CFLAGS = $(CFLAGS) -Iinclude $(LDFLAGS) -lcheck $(LIBCHECK_CFLAGS) -Werror -Wdeclaration-after-statement -fsanitize=address -fsanitize=undefined -ftest-coverage -fprofile-arcs -ggdb

TEST_GENERIC_QUEUE = test/queues/test_generic_queue
TEST_GENERIC_QUEUE_SRC = test/queues/test_generic_queue.c queues/generic_queue.c

TEST_LINKED_LIST = test/lists/test_linked_list
TEST_LINKED_LIST_SRC = test/lists/test_linked_list.c lists/linked_list.c

TEST_STDSTRING = test/string/test_stdstring
TEST_STDSTRING_SRC = test/string/test_stdstring.c string/stdstring.c encodings/encoding_utf.c \
		     compat/compat_strl.c

TEST_UTILS = test/utils/test_utils
TEST_UTILS_SRC = test/utils/test_utils.c utils/md5.c encodings/encoding_crc32.c \
		streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
		compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c

TEST_HASH = test/hash/test_hash
TEST_HASH_SRC = test/hash/test_hash.c hash/lrc_hash.c \
		streams/file_stream.c vfs/vfs_implementation.c file/file_path.c \
		compat/compat_strl.c time/rtime.c string/stdstring.c encodings/encoding_utf.c

TEST_RPNG = test/formats/test_rpng
TEST_RPNG_SRC = test/formats/test_rpng.c formats/png/rpng.c \
		streams/trans_stream.c streams/trans_stream_zlib.c \
		streams/trans_stream_pipe.c
TEST_RPNG_LIBS = -lz

all:
	# Build and execute tests in order, to avoid coverage file collision
	# string
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_STDSTRING_SRC) -o $(TEST_STDSTRING)
	$(TEST_STDSTRING)
	lcov -c -d . -o `dirname $(TEST_STDSTRING)`/coverage.info
	# utils
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_UTILS_SRC) -o $(TEST_UTILS)
	$(TEST_UTILS)
	lcov -c -d . -o `dirname $(TEST_UTILS)`/coverage.info
	# utils
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_HASH_SRC) -o $(TEST_HASH)
	$(TEST_HASH)
	lcov -c -d . -o `dirname $(TEST_HASH)`/coverage.info
	# list
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_LINKED_LIST_SRC) -o $(TEST_LINKED_LIST)
	$(TEST_LINKED_LIST)
	lcov -c -d . -o `dirname $(TEST_LINKED_LIST)`/coverage.info
	# queue
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_GENERIC_QUEUE_SRC) -o $(TEST_GENERIC_QUEUE)
	$(TEST_GENERIC_QUEUE)
	lcov -c -d . -o `dirname $(TEST_GENERIC_QUEUE)`/coverage.info
	# rpng
	$(CC) $(TEST_UNIT_CFLAGS) $(TEST_RPNG_SRC) $(TEST_RPNG_LIBS) -o $(TEST_RPNG)
	$(TEST_RPNG)
	lcov -c -d . -o `dirname $(TEST_RPNG)`/coverage.info
	
	lcov -o test/coverage.info \
	     -a test/utils/coverage.info \
	     -a test/string/coverage.info \
	     -a test/lists/coverage.info \
	     -a test/queues/coverage.info \
	     -a test/formats/coverage.info
	genhtml -o test/coverage/ test/coverage.info

clean:
	rm -f *.gcda *.gcno



================================================
FILE: audio/audio_mix.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (audio_mix.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <memalign.h>

#include <retro_environment.h>

#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif

#include <retro_miscellaneous.h>
#include <audio/audio_mix.h>
#include <streams/file_stream.h>
#include <audio/conversion/float_to_s16.h>
#include <audio/conversion/s16_to_float.h>

void audio_mix_volume_C(float *s, const float *in, float vol, size_t len)
{
   size_t i;
   for (i = 0; i < len; i++)
      s[i] += in[i] * vol;
}

#ifdef __SSE2__
void audio_mix_volume_SSE2(float *s, const float *in, float vol, size_t len)
{
   size_t i, remaining_samples;
   __m128 volume = _mm_set1_ps(vol);

   for (i = 0; i + 16 <= len; i += 16, s += 16, in += 16)
   {
      unsigned j;
      __m128 input[4];
      __m128 additive[4];

      input[0]    = _mm_loadu_ps(s +  0);
      input[1]    = _mm_loadu_ps(s +  4);
      input[2]    = _mm_loadu_ps(s +  8);
      input[3]    = _mm_loadu_ps(s + 12);

      additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in +  0));
      additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in +  4));
      additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in +  8));
      additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12));

      for (j = 0; j < 4; j++)
         _mm_storeu_ps(s + 4 * j, _mm_add_ps(input[j], additive[j]));
   }

   remaining_samples = len - i;

   for (i = 0; i < remaining_samples; i++)
      s[i] += in[i] * vol;
}
#endif

void audio_mix_free_chunk(audio_chunk_t *chunk)
{
   if (!chunk)
      return;

#ifdef HAVE_RWAV
   if (chunk->rwav && chunk->rwav->samples)
   {
      /* rwav_free only frees the samples */
      rwav_free(chunk->rwav);
      free(chunk->rwav);
   }
#endif

   if (chunk->buf)
      free(chunk->buf);

   if (chunk->upsample_buf)
      memalign_free(chunk->upsample_buf);

   if (chunk->float_buf)
      memalign_free(chunk->float_buf);

   if (chunk->float_resample_buf)
      memalign_free(chunk->float_resample_buf);

   if (chunk->resample_buf)
      memalign_free(chunk->resample_buf);

   if (chunk->resampler && chunk->resampler_data)
      chunk->resampler->free(chunk->resampler_data);

   free(chunk);
}

audio_chunk_t* audio_mix_load_wav_file(const char *path, int sample_rate,
      const char *resampler_ident, enum resampler_quality quality)
{
#ifdef HAVE_RWAV
   int sample_size;
   int64_t len                = 0;
   void *buf                  = NULL;
   audio_chunk_t *chunk       = (audio_chunk_t*)malloc(sizeof(*chunk));

   if (!chunk)
      return NULL;

   chunk->buf                 = NULL;
   chunk->upsample_buf        = NULL;
   chunk->float_buf           = NULL;
   chunk->float_resample_buf  = NULL;
   chunk->resample_buf        = NULL;
   chunk->len                 = 0;
   chunk->resample_len        = 0;
   chunk->sample_rate         = sample_rate;
   chunk->resample            = false;
   chunk->resampler           = NULL;
   chunk->resampler_data      = NULL;
   chunk->ratio               = 0.00f;
   chunk->rwav                = (rwav_t*)malloc(sizeof(rwav_t));

   if (!chunk->rwav)
      goto error;

   chunk->rwav->bitspersample = 0;
   chunk->rwav->numchannels   = 0;
   chunk->rwav->samplerate    = 0;
   chunk->rwav->numsamples    = 0;
   chunk->rwav->subchunk2size = 0;
   chunk->rwav->samples       = NULL;

   if (!filestream_read_file(path, &buf, &len))
      goto error;

   chunk->buf                 = buf;
   chunk->len                 = len;

   if (rwav_load(chunk->rwav, chunk->buf, chunk->len) == RWAV_ITERATE_ERROR)
      goto error;

   /* numsamples does not know or care about
    * multiple channels, but we need space for 2 */
   chunk->upsample_buf        = (int16_t*)memalign_alloc(128,
         chunk->rwav->numsamples * 2 * sizeof(int16_t));

   sample_size                = chunk->rwav->bitspersample / 8;

   if (sample_size == 1)
   {
      unsigned i;

      if (chunk->rwav->numchannels == 1)
      {
         for (i = 0; i < chunk->rwav->numsamples; i++)
         {
            uint8_t *sample                  = (
                  (uint8_t*)chunk->rwav->samples) + i;

            chunk->upsample_buf[i * 2]       =
               (int16_t)((sample[0] - 128) << 8);
            chunk->upsample_buf[(i * 2) + 1] =
               (int16_t)((sample[0] - 128) << 8);
         }
      }
      else if (chunk->rwav->numchannels == 2)
      {
         for (i = 0; i < chunk->rwav->numsamples; i++)
         {
            uint8_t *sample                  = (
                  (uint8_t*)chunk->rwav->samples) +
               (i * 2);

            chunk->upsample_buf[i * 2]       =
               (int16_t)((sample[0] - 128) << 8);
            chunk->upsample_buf[(i * 2) + 1] =
               (int16_t)((sample[1] - 128) << 8);
         }
      }
   }
   else if (sample_size == 2)
   {
      if (chunk->rwav->numchannels == 1)
      {
         unsigned i;

         for (i = 0; i < chunk->rwav->numsamples; i++)
         {
            int16_t sample                   = ((int16_t*)
                  chunk->rwav->samples)[i];

            chunk->upsample_buf[i * 2]       = sample;
            chunk->upsample_buf[(i * 2) + 1] = sample;
         }
      }
      else if (chunk->rwav->numchannels == 2)
         memcpy(chunk->upsample_buf, chunk->rwav->samples,
               chunk->rwav->subchunk2size);
   }
   else if (sample_size != 2)
   {
      /* we don't support any other sample size besides 8 and 16-bit yet */
      goto error;
   }

   if (sample_rate != (int)chunk->rwav->samplerate)
   {
      chunk->resample = true;
      chunk->ratio    = (double)sample_rate / chunk->rwav->samplerate;

      retro_resampler_realloc(&chunk->resampler_data,
            &chunk->resampler,
            resampler_ident,
            quality,
            chunk->ratio);

      if (chunk->resampler && chunk->resampler_data)
      {
         struct resampler_data info;

         chunk->float_buf          = (float*)memalign_alloc(128,
               chunk->rwav->numsamples * 2 *
               chunk->ratio * sizeof(float));

         /* why is *3 needed instead of just *2? Does the
          * sinc driver require more space than we know about? */
         chunk->float_resample_buf = (float*)memalign_alloc(128,
               chunk->rwav->numsamples * 3 *
               chunk->ratio * sizeof(float));

         convert_s16_to_float(chunk->float_buf,
               chunk->upsample_buf, chunk->rwav->numsamples * 2, 1.0);

         info.data_in       = (const float*)chunk->float_buf;
         info.data_out      = chunk->float_resample_buf;
         /* a 'frame' consists of two channels, so we set this
          * to the number of samples irrespective of channel count */
         info.input_frames  = chunk->rwav->numsamples;
         info.output_frames = 0;
         info.ratio         = chunk->ratio;

         chunk->resampler->process(chunk->resampler_data, &info);

         /* number of output_frames does not increase with
          * multiple channels, but assume we need space for 2 */
         chunk->resample_buf = (int16_t*)memalign_alloc(128,
               info.output_frames * 2 * sizeof(int16_t));
         chunk->resample_len = info.output_frames;
         convert_float_to_s16(chunk->resample_buf,
               chunk->float_resample_buf, info.output_frames * 2);
      }
   }

   return chunk;

error:
   audio_mix_free_chunk(chunk);
#endif
   return NULL;
}

size_t audio_mix_get_chunk_num_samples(audio_chunk_t *chunk)
{
   if (!chunk)
      return 0;

#ifdef HAVE_RWAV
   if (chunk->rwav)
   {
      if (chunk->resample)
         return chunk->resample_len;
      return chunk->rwav->numsamples;
   }
#endif

   /* no other filetypes supported yet */
   return 0;
}

/**
 * audio_mix_get_chunk_sample:
 * @chunk              : audio chunk instance
 * @channel            : channel of the sample (0=left, 1=right)
 * @index              : index of the sample
 *
 * Get a sample from an audio chunk.
 *
 * Returns: A signed 16-bit audio sample.
 **/
int16_t audio_mix_get_chunk_sample(audio_chunk_t *chunk,
      unsigned channel, size_t index)
{
   if (!chunk)
      return 0;

#ifdef HAVE_RWAV
   if (chunk->rwav)
   {
      int sample_size    = chunk->rwav->bitspersample / 8;
      int16_t sample_out = 0;

      /* 0 is the first/left channel */
      uint8_t *sample    = NULL;

      if (chunk->resample)
         sample = (uint8_t*)chunk->resample_buf +
            (sample_size * index * chunk->rwav->numchannels)
            + (channel * sample_size);
      else
         sample = (uint8_t*)chunk->upsample_buf +
            (sample_size * index * chunk->rwav->numchannels)
            + (channel * sample_size);

      sample_out = (int16_t)*sample;

      return sample_out;
   }
#endif

   /* no other filetypes supported yet */
   return 0;
}

int16_t* audio_mix_get_chunk_samples(audio_chunk_t *chunk)
{
   if (!chunk)
      return 0;

#ifdef HAVE_RWAV
   if (chunk->rwav)
   {
      int16_t *sample;

      if (chunk->resample)
         sample = chunk->resample_buf;
      else
         sample = chunk->upsample_buf;

      return sample;
   }
#endif

   return NULL;
}

int audio_mix_get_chunk_num_channels(audio_chunk_t *chunk)
{
   if (!chunk)
      return 0;

#ifdef HAVE_RWAV
   if (chunk->rwav)
      return chunk->rwav->numchannels;
#endif

   /* don't support other formats yet */
   return 0;
}


================================================
FILE: audio/audio_mixer.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (audio_mixer.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif

#include <audio/audio_mixer.h>
#include <audio/audio_resampler.h>

#ifdef HAVE_RWAV
#include <formats/rwav.h>
#endif
#include <memalign.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_STB_VORBIS
#define STB_VORBIS_NO_PUSHDATA_API
#define STB_VORBIS_NO_STDIO
#define STB_VORBIS_NO_CRT

#include <stb/stb_vorbis.h>
#endif

#ifdef HAVE_DR_FLAC
#include <retro_inline.h>
#define DR_FLAC_IMPLEMENTATION
#define DRFLAC_API static INLINE
#include <dr/dr_flac.h>
#endif

#ifdef HAVE_DR_MP3
#define DR_MP3_IMPLEMENTATION
#include <retro_assert.h>
#define DRMP3_ASSERT(expression) retro_assert(expression)
#include <dr/dr_mp3.h>
#endif

#ifdef HAVE_IBXM
#include <ibxm/ibxm.h>
#endif

#ifdef HAVE_THREADS
#include <rthreads/rthreads.h>
#define AUDIO_MIXER_LOCK(voice)   slock_lock(voice->lock)
#define AUDIO_MIXER_UNLOCK(voice) slock_unlock(voice->lock)
#else
#define AUDIO_MIXER_LOCK(voice)   do {} while(0)
#define AUDIO_MIXER_UNLOCK(voice) do {} while(0)
#endif

#define AUDIO_MIXER_MAX_VOICES      8
#define AUDIO_MIXER_TEMP_BUFFER 8192

struct audio_mixer_sound
{
   enum audio_mixer_type type;
   void* user_data;

   union
   {
      struct
      {
         /* wav */
         const float* pcm;
         unsigned frames;
      } wav;

#ifdef HAVE_STB_VORBIS
      struct
      {
         /* ogg */
         const void* data;
         unsigned size;
      } ogg;
#endif

#ifdef HAVE_DR_FLAC
      struct
      {
          /* flac */
         const void* data;
         unsigned size;
      } flac;
#endif

#ifdef HAVE_DR_MP3
      struct
      {
          /* mp */
         const void* data;
         unsigned size;
      } mp3;
#endif

#ifdef HAVE_IBXM
      struct
      {
         /* mod/s3m/xm */
         const void* data;
         unsigned size;
      } mod;
#endif
   } types;
};

struct audio_mixer_voice
{
   struct
   {
      struct
      {
         unsigned position;
      } wav;

#ifdef HAVE_STB_VORBIS
      struct
      {
         stb_vorbis *stream;
         void       *resampler_data;
         const retro_resampler_t *resampler;
         float      *buffer;
         unsigned    position;
         unsigned    samples;
         unsigned    buf_samples;
         float       ratio;
      } ogg;
#endif

#ifdef HAVE_DR_FLAC
      struct
      {
         float*      buffer;
         drflac      *stream;
         void        *resampler_data;
         const retro_resampler_t *resampler;
         unsigned    position;
         unsigned    samples;
         unsigned    buf_samples;
         float       ratio;
      } flac;
#endif

#ifdef HAVE_DR_MP3
      struct
      {
         drmp3       stream;
         void        *resampler_data;
         const retro_resampler_t *resampler;
         float*      buffer;
         unsigned    position;
         unsigned    samples;
         unsigned    buf_samples;
         float       ratio;
      } mp3;
#endif

#ifdef HAVE_IBXM
      struct
      {
         int*              buffer;
         struct replay*    stream;
         struct module*    module;
         unsigned          position;
         unsigned          samples;
         unsigned          buf_samples;
      } mod;
#endif
   } types;
   audio_mixer_sound_t *sound;
   audio_mixer_stop_cb_t stop_cb;
   unsigned type;
   float    volume;
   bool     repeat;
#ifdef HAVE_THREADS
   slock_t *lock;
#endif
};

/* TODO/FIXME - static globals */
static struct audio_mixer_voice s_voices[AUDIO_MIXER_MAX_VOICES] = {0};
static unsigned s_rate = 0;

static void audio_mixer_release(audio_mixer_voice_t* voice);

#ifdef HAVE_RWAV
static bool wav_to_float(const rwav_t* wav, float** pcm, size_t len)
{
   size_t i;
   /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes */
   float *f           = (float*)memalign_alloc(16,
         ((len + 15) & ~15) * sizeof(float));

   if (!f)
      return false;

   *pcm = f;

   if (wav->bitspersample == 8)
   {
      float sample      = 0.0f;
      const uint8_t *u8 = (const uint8_t*)wav->samples;

      if (wav->numchannels == 1)
      {
         for (i = wav->numsamples; i != 0; i--)
         {
            sample = (float)*u8++ / 255.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
            *f++   = sample;
         }
      }
      else if (wav->numchannels == 2)
      {
         for (i = wav->numsamples; i != 0; i--)
         {
            sample = (float)*u8++ / 255.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
            sample = (float)*u8++ / 255.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
         }
      }
   }
   else
   {
      /* TODO/FIXME note to leiradel - can we use audio/conversion/s16_to_float
       * functions here? */

      float sample       = 0.0f;
      const int16_t *s16 = (const int16_t*)wav->samples;

      if (wav->numchannels == 1)
      {
         for (i = wav->numsamples; i != 0; i--)
         {
            sample = (float)((int)*s16++ + 32768) / 65535.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
            *f++   = sample;
         }
      }
      else if (wav->numchannels == 2)
      {
         for (i = wav->numsamples; i != 0; i--)
         {
            sample = (float)((int)*s16++ + 32768) / 65535.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
            sample = (float)((int)*s16++ + 32768) / 65535.0f;
            sample = sample * 2.0f - 1.0f;
            *f++   = sample;
         }
      }
   }

   return true;
}

static bool one_shot_resample(const float* in, size_t samples_in,
      unsigned rate, const char *resampler_ident, enum resampler_quality quality,
      float** out, size_t* samples_out)
{
   struct resampler_data info;
   void* data                         = NULL;
   const retro_resampler_t* resampler = NULL;
   float ratio                        = (double)s_rate / (double)rate;

   if (!retro_resampler_realloc(&data, &resampler,
         resampler_ident, quality, ratio))
      return false;

   /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
    * add 16 more samples in the formula below just as safeguard, because
    * resampler->process sometimes reports more output samples than the
    * formula below calculates. Ideally, audio resamplers should have a
    * function to return the number of samples they will output given a
    * count of input samples. */
   *samples_out                       = (size_t)(samples_in * ratio);
   *out                               = (float*)memalign_alloc(16,
         (((*samples_out + 16) + 15) & ~15) * sizeof(float));

   if (*out == NULL)
      return false;

   info.data_in                       = in;
   info.data_out                      = *out;
   info.input_frames                  = samples_in / 2;
   info.output_frames                 = 0;
   info.ratio                         = ratio;

   resampler->process(data, &info);
   resampler->free(data);
   return true;
}
#endif

void audio_mixer_init(unsigned rate)
{
   unsigned i;

   s_rate = rate;

   for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
   {
      audio_mixer_voice_t *voice = &s_voices[i];

      voice->type = AUDIO_MIXER_TYPE_NONE;
#ifdef HAVE_THREADS
      if (!voice->lock)
         voice->lock = slock_new();
#endif
   }
}

void audio_mixer_done(void)
{
   unsigned i;

   for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++)
   {
      audio_mixer_voice_t *voice = &s_voices[i];

      AUDIO_MIXER_LOCK(voice);
      audio_mixer_release(voice);
      AUDIO_MIXER_UNLOCK(voice);
#ifdef HAVE_THREADS
      slock_free(voice->lock);
      voice->lock = NULL;
#endif
   }
}

audio_mixer_sound_t* audio_mixer_load_wav(void *buffer, int32_t size,
      const char *resampler_ident, enum resampler_quality quality)
{
#ifdef HAVE_RWAV
   /* WAV data */
   rwav_t wav;
   /* WAV samples converted to float */
   float* pcm                 = NULL;
   size_t samples             = 0;
   /* Result */
   audio_mixer_sound_t* sound = NULL;

   wav.bitspersample          = 0;
   wav.numchannels            = 0;
   wav.samplerate             = 0;
   wav.numsamples             = 0;
   wav.subchunk2size          = 0;
   wav.samples                = NULL;

   if ((rwav_load(&wav, buffer, size)) != RWAV_ITERATE_DONE)
      return NULL;

   samples       = wav.numsamples * 2;

   if (!wav_to_float(&wav, &pcm, samples))
      return NULL;

   if (wav.samplerate != s_rate)
   {
      float* resampled           = NULL;

      if (!one_shot_resample(pcm, samples, wav.samplerate,
            resampler_ident, quality,
            &resampled, &samples))
         return NULL;

      memalign_free((void*)pcm);
      pcm = resampled;
   }

   sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));

   if (!sound)
   {
      memalign_free((void*)pcm);
      return NULL;
   }

   sound->type             = AUDIO_MIXER_TYPE_WAV;
   sound->types.wav.frames = (unsigned)(samples / 2);
   sound->types.wav.pcm    = pcm;

   rwav_free(&wav);

   return sound;
#else
   return NULL;
#endif
}

audio_mixer_sound_t* audio_mixer_load_ogg(void *buffer, int32_t size)
{
#ifdef HAVE_STB_VORBIS
   audio_mixer_sound_t* sound;

   if (!buffer || size <= 0)
      return NULL;

   sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));

   if (!sound)
      return NULL;

   sound->type           = AUDIO_MIXER_TYPE_OGG;
   sound->types.ogg.size = size;
   sound->types.ogg.data = buffer;

   return sound;
#else
   return NULL;
#endif
}

audio_mixer_sound_t* audio_mixer_load_flac(void *buffer, int32_t size)
{
#ifdef HAVE_DR_FLAC
   audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));

   if (!sound)
      return NULL;

   sound->type           = AUDIO_MIXER_TYPE_FLAC;
   sound->types.flac.size = size;
   sound->types.flac.data = buffer;

   return sound;
#else
   return NULL;
#endif
}

audio_mixer_sound_t* audio_mixer_load_mp3(void *buffer, int32_t size)
{
#ifdef HAVE_DR_MP3
   audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));

   if (!sound)
      return NULL;

   sound->type           = AUDIO_MIXER_TYPE_MP3;
   sound->types.mp3.size = size;
   sound->types.mp3.data = buffer;

   return sound;
#else
   return NULL;
#endif
}

audio_mixer_sound_t* audio_mixer_load_mod(void *buffer, int32_t size)
{
#ifdef HAVE_IBXM
   audio_mixer_sound_t* sound = (audio_mixer_sound_t*)calloc(1, sizeof(*sound));

   if (!sound)
      return NULL;

   sound->type           = AUDIO_MIXER_TYPE_MOD;
   sound->types.mod.size = size;
   sound->types.mod.data = buffer;

   return sound;
#else
   return NULL;
#endif
}

void audio_mixer_destroy(audio_mixer_sound_t* sound)
{
   void *handle = NULL;
   if (!sound)
      return;

   switch (sound->type)
   {
      case AUDIO_MIXER_TYPE_WAV:
         handle = (void*)sound->types.wav.pcm;
         if (handle)
            memalign_free(handle);
         break;
      case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
         handle = (void*)sound->types.ogg.data;
         if (handle)
            free(handle);
#endif
         break;
      case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
         handle = (void*)sound->types.mod.data;
         if (handle)
            free(handle);
#endif
         break;
      case AUDIO_MIXER_TYPE_FLAC:
#ifdef HAVE_DR_FLAC
         handle = (void*)sound->types.flac.data;
         if (handle)
            free(handle);
#endif
         break;
      case AUDIO_MIXER_TYPE_MP3:
#ifdef HAVE_DR_MP3
         handle = (void*)sound->types.mp3.data;
         if (handle)
            free(handle);
#endif
         break;
      case AUDIO_MIXER_TYPE_NONE:
         break;
   }

   free(sound);
}

static bool audio_mixer_play_wav(audio_mixer_sound_t* sound,
      audio_mixer_voice_t* voice, bool repeat, float volume,
      audio_mixer_stop_cb_t stop_cb)
{
   voice->types.wav.position = 0;
   return true;
}

#ifdef HAVE_STB_VORBIS
static bool audio_mixer_play_ogg(
      audio_mixer_sound_t* sound,
      audio_mixer_voice_t* voice,
      bool repeat, float volume,
      const char *resampler_ident,
      enum resampler_quality quality,
      audio_mixer_stop_cb_t stop_cb)
{
   stb_vorbis_info info;
   int res                         = 0;
   float ratio                     = 1.0f;
   unsigned samples                = 0;
   void *ogg_buffer                = NULL;
   void *resampler_data            = NULL;
   const retro_resampler_t* resamp = NULL;
   stb_vorbis *stb_vorbis          = stb_vorbis_open_memory(
         (const unsigned char*)sound->types.ogg.data,
         sound->types.ogg.size, &res, NULL);

   if (!stb_vorbis)
      return false;

   info                    = stb_vorbis_get_info(stb_vorbis);

   if (info.sample_rate != s_rate)
   {
      ratio = (double)s_rate / (double)info.sample_rate;

      if (!retro_resampler_realloc(&resampler_data,
               &resamp, resampler_ident, quality,
               ratio))
         goto error;
   }

   /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
    * add 16 more samples in the formula below just as safeguard, because
    * resampler->process sometimes reports more output samples than the
    * formula below calculates. Ideally, audio resamplers should have a
    * function to return the number of samples they will output given a
    * count of input samples. */
   samples                         = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio);
   ogg_buffer                      = (float*)memalign_alloc(16,
         (((samples + 16) + 15) & ~15) * sizeof(float));

   if (!ogg_buffer)
   {
      if (resamp && resampler_data)
         resamp->free(resampler_data);
      goto error;
   }

   voice->types.ogg.resampler      = resamp;
   voice->types.ogg.resampler_data = resampler_data;
   voice->types.ogg.buffer         = (float*)ogg_buffer;
   voice->types.ogg.buf_samples    = samples;
   voice->types.ogg.ratio          = ratio;
   voice->types.ogg.stream         = stb_vorbis;
   voice->types.ogg.position       = 0;
   voice->types.ogg.samples        = 0;

   return true;

error:
   stb_vorbis_close(stb_vorbis);
   return false;
}

static void audio_mixer_release_ogg(audio_mixer_voice_t* voice)
{
   if (voice->types.ogg.stream)
      stb_vorbis_close(voice->types.ogg.stream);
   if (voice->types.ogg.resampler && voice->types.ogg.resampler_data)
      voice->types.ogg.resampler->free(voice->types.ogg.resampler_data);
   if (voice->types.ogg.buffer)
      memalign_free(voice->types.ogg.buffer);
}

#endif

#ifdef HAVE_IBXM
static bool audio_mixer_play_mod(
      audio_mixer_sound_t* sound,
      audio_mixer_voice_t* voice,
      bool repeat, float volume,
      audio_mixer_stop_cb_t stop_cb)
{
   struct data data;
   char message[64];
   int buf_samples               = 0;
   int samples                   = 0;
   void *mod_buffer              = NULL;
   struct module* module         = NULL;
   struct replay* replay         = NULL;

   data.buffer                   = (char*)sound->types.mod.data;
   data.length                   = sound->types.mod.size;
   module                        = module_load(&data, message);

   if (!module)
   {
      printf("audio_mixer_play_mod module_load() failed with error: %s\n", message);
      goto error;
   }

   if (voice->types.mod.module)
      dispose_module(voice->types.mod.module);

   voice->types.mod.module = module;

   replay = new_replay(module, s_rate, 1);

   if (!replay)
   {
      printf("audio_mixer_play_mod new_replay() failed\n");
      goto error;
   }

   buf_samples = calculate_mix_buf_len(s_rate);
   mod_buffer  = memalign_alloc(16, ((buf_samples + 15) & ~15) * sizeof(int));

   if (!mod_buffer)
   {
      printf("audio_mixer_play_mod cannot allocate mod_buffer !\n");
      goto error;
   }

   samples = replay_calculate_duration(replay);

   if (!samples)
   {
      printf("audio_mixer_play_mod cannot retrieve duration !\n");
      goto error;
   }

   voice->types.mod.buffer         = (int*)mod_buffer;
   voice->types.mod.buf_samples    = buf_samples;
   voice->types.mod.stream         = replay;
   voice->types.mod.position       = 0;
   voice->types.mod.samples        = 0; /* samples; */

   return true;

error:
   if (mod_buffer)
      memalign_free(mod_buffer);
   if (module)
      dispose_module(module);
   return false;

}

static void audio_mixer_release_mod(audio_mixer_voice_t* voice)
{
   if (voice->types.mod.stream)
      dispose_replay(voice->types.mod.stream);
   if (voice->types.mod.buffer)
      memalign_free(voice->types.mod.buffer);
}
#endif

#ifdef HAVE_DR_FLAC
static bool audio_mixer_play_flac(
      audio_mixer_sound_t* sound,
      audio_mixer_voice_t* voice,
      bool repeat, float volume,
      const char *resampler_ident,
      enum resampler_quality quality,
      audio_mixer_stop_cb_t stop_cb)
{
   float ratio                     = 1.0f;
   unsigned samples                = 0;
   void *flac_buffer                = NULL;
   void *resampler_data            = NULL;
   const retro_resampler_t* resamp = NULL;
   drflac *dr_flac          = drflac_open_memory((const unsigned char*)sound->types.flac.data, sound->types.flac.size, NULL);

   if (!dr_flac)
      return false;

   /* The downstream mixer (audio_mixer_mix_flac) requests
    * AUDIO_MIXER_TEMP_BUFFER / 2 frames into a stack buffer
    * sized AUDIO_MIXER_TEMP_BUFFER floats.  drflac writes
    * frame_count * channel_count floats, so this only fits
    * exactly for stereo.  Mono fits but the downstream
    * accounting is wrong (per existing comment); >2 channels
    * overflows the stack buffer (e.g. 8-channel FLAC writes
    * 4 * AUDIO_MIXER_TEMP_BUFFER floats = 4x the buffer).
    * Reject anything that isn't stereo here rather than risk a
    * stack overflow during mix.  Mono should be fixed
    * separately by adjusting the mixer's per-channel
    * accounting. */
   if (dr_flac->channels != 2)
   {
      drflac_close(dr_flac);
      return false;
   }

   if (dr_flac->sampleRate != s_rate)
   {
      ratio = (double)s_rate / (double)(dr_flac->sampleRate);

      if (!retro_resampler_realloc(&resampler_data,
               &resamp, resampler_ident, quality,
               ratio))
         goto error;
   }

   /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
    * add 16 more samples in the formula below just as safeguard, because
    * resampler->process sometimes reports more output samples than the
    * formula below calculates. Ideally, audio resamplers should have a
    * function to return the number of samples they will output given a
    * count of input samples. */
   samples                         = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio);
   flac_buffer                     = (float*)memalign_alloc(16,
         (((samples + 16) + 15) & ~15) * sizeof(float));

   if (!flac_buffer)
   {
      if (resamp && resamp->free)
         resamp->free(resampler_data);
      goto error;
   }

   voice->types.flac.resampler      = resamp;
   voice->types.flac.resampler_data = resampler_data;
   voice->types.flac.buffer         = (float*)flac_buffer;
   voice->types.flac.buf_samples    = samples;
   voice->types.flac.ratio          = ratio;
   voice->types.flac.stream         = dr_flac;
   voice->types.flac.position       = 0;
   voice->types.flac.samples        = 0;

   return true;

error:
   drflac_close(dr_flac);
   return false;
}

static void audio_mixer_release_flac(audio_mixer_voice_t* voice)
{
   if (voice->types.flac.stream)
      drflac_close(voice->types.flac.stream);
   if (voice->types.flac.resampler && voice->types.flac.resampler_data)
      voice->types.flac.resampler->free(voice->types.flac.resampler_data);
   if (voice->types.flac.buffer)
      memalign_free(voice->types.flac.buffer);
}
#endif

#ifdef HAVE_DR_MP3
static bool audio_mixer_play_mp3(
      audio_mixer_sound_t* sound,
      audio_mixer_voice_t* voice,
      bool repeat, float volume,
      const char *resampler_ident,
      enum resampler_quality quality,
      audio_mixer_stop_cb_t stop_cb)
{
   float ratio                     = 1.0f;
   unsigned samples                = 0;
   void *mp3_buffer                = NULL;
   void *resampler_data            = NULL;
   const retro_resampler_t* resamp = NULL;
   bool res;

   res = drmp3_init_memory(&voice->types.mp3.stream, (const unsigned char*)sound->types.mp3.data, sound->types.mp3.size, NULL);

   if (!res)
      return false;

   if (voice->types.mp3.stream.sampleRate != s_rate)
   {
      ratio = (double)s_rate / (double)(voice->types.mp3.stream.sampleRate);

      if (!retro_resampler_realloc(&resampler_data,
               &resamp, resampler_ident, quality,
               ratio))
         goto error;
   }

   /* Allocate on a 16-byte boundary, and pad to a multiple of 16 bytes. We
    * add 16 more samples in the formula below just as safeguard, because
    * resampler->process sometimes reports more output samples than the
    * formula below calculates. Ideally, audio resamplers should have a
    * function to return the number of samples they will output given a
    * count of input samples. */
   samples                         = (unsigned)(AUDIO_MIXER_TEMP_BUFFER * ratio);
   mp3_buffer                      = (float*)memalign_alloc(16,
         (((samples + 16) + 15) & ~15) * sizeof(float));

   if (!mp3_buffer)
   {
      if (resamp && resampler_data)
         resamp->free(resampler_data);
      goto error;
   }

   voice->types.mp3.resampler      = resamp;
   voice->types.mp3.resampler_data = resampler_data;
   voice->types.mp3.buffer         = (float*)mp3_buffer;
   voice->types.mp3.buf_samples    = samples;
   voice->types.mp3.ratio          = ratio;
   voice->types.mp3.position       = 0;
   voice->types.mp3.samples        = 0;

   return true;

error:
   drmp3_uninit(&voice->types.mp3.stream);
   return false;
}

static void audio_mixer_release_mp3(audio_mixer_voice_t* voice)
{
   if (voice->types.mp3.resampler && voice->types.mp3.resampler_data)
      voice->types.mp3.resampler->free(voice->types.mp3.resampler_data);
   if (voice->types.mp3.buffer)
      memalign_free(voice->types.mp3.buffer);
   if (voice->types.mp3.stream.pData)
      drmp3_uninit(&voice->types.mp3.stream);
}

#endif

audio_mixer_voice_t* audio_mixer_play(audio_mixer_sound_t* sound,
      bool repeat, float volume,
      const char *resampler_ident,
      enum resampler_quality quality,
      audio_mixer_stop_cb_t stop_cb)
{
   unsigned i;
   bool res                   = false;
   audio_mixer_voice_t* voice = s_voices;

   if (!sound)
      return NULL;

   for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
   {
      if (voice->type != AUDIO_MIXER_TYPE_NONE)
         continue;

      AUDIO_MIXER_LOCK(voice);

      if (voice->type != AUDIO_MIXER_TYPE_NONE)
      {
         AUDIO_MIXER_UNLOCK(voice);
         continue;
      }

      /* claim the voice, also helps with cleanup on error */
      voice->type = sound->type;

      switch (sound->type)
      {
         case AUDIO_MIXER_TYPE_WAV:
            res = audio_mixer_play_wav(sound, voice, repeat, volume, stop_cb);
            break;
         case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
            res = audio_mixer_play_ogg(sound, voice, repeat, volume,
                  resampler_ident, quality, stop_cb);
#endif
            break;
         case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
            res = audio_mixer_play_mod(sound, voice, repeat, volume, stop_cb);
#endif
            break;
         case AUDIO_MIXER_TYPE_FLAC:
#ifdef HAVE_DR_FLAC
            res = audio_mixer_play_flac(sound, voice, repeat, volume,
                  resampler_ident, quality, stop_cb);
#endif
            break;
         case AUDIO_MIXER_TYPE_MP3:
#ifdef HAVE_DR_MP3
            res = audio_mixer_play_mp3(sound, voice, repeat, volume,
                  resampler_ident, quality, stop_cb);
#endif
            break;
         case AUDIO_MIXER_TYPE_NONE:
            break;
      }

      break;
   }

   if (res)
   {
      voice->repeat   = repeat;
      voice->volume   = volume;
      voice->sound    = sound;
      voice->stop_cb  = stop_cb;
      AUDIO_MIXER_UNLOCK(voice);
   }
   else
   {
      if (i < AUDIO_MIXER_MAX_VOICES)
      {
         audio_mixer_release(voice);
         AUDIO_MIXER_UNLOCK(voice);
      }
      voice = NULL;
   }

   return voice;
}

/* Need to hold lock for voice.  */
static void audio_mixer_release(audio_mixer_voice_t* voice)
{
   if (!voice)
      return;

   switch (voice->type)
   {
#ifdef HAVE_STB_VORBIS
      case AUDIO_MIXER_TYPE_OGG:
         audio_mixer_release_ogg(voice);
         break;
#endif
#ifdef HAVE_IBXM
      case AUDIO_MIXER_TYPE_MOD:
         audio_mixer_release_mod(voice);
         break;
#endif
#ifdef HAVE_DR_FLAC
      case AUDIO_MIXER_TYPE_FLAC:
         audio_mixer_release_flac(voice);
         break;
#endif
#ifdef HAVE_DR_MP3
      case AUDIO_MIXER_TYPE_MP3:
         audio_mixer_release_mp3(voice);
         break;
#endif
      default:
         break;
   }

   memset(&voice->types, 0, sizeof(voice->types));
   voice->type = AUDIO_MIXER_TYPE_NONE;
}

void audio_mixer_stop(audio_mixer_voice_t* voice)
{
   audio_mixer_stop_cb_t stop_cb = NULL;
   audio_mixer_sound_t* sound    = NULL;

   if (voice)
   {
      AUDIO_MIXER_LOCK(voice);
      stop_cb     = voice->stop_cb;
      sound       = voice->sound;

      audio_mixer_release(voice);

      AUDIO_MIXER_UNLOCK(voice);

      if (stop_cb)
         stop_cb(sound, AUDIO_MIXER_SOUND_STOPPED);
   }
}

static void audio_mixer_mix_wav(float* buffer, size_t num_frames,
      audio_mixer_voice_t* voice,
      float volume)
{
   int i;
   unsigned buf_free                = (unsigned)(num_frames * 2);
   const audio_mixer_sound_t* sound = voice->sound;
   unsigned pcm_available           = sound->types.wav.frames
      * 2 - voice->types.wav.position;
   const float* pcm                 = sound->types.wav.pcm +
      voice->types.wav.position;

again:
   if (pcm_available < buf_free)
   {
      for (i = pcm_available; i != 0; i--)
         *buffer++ += *pcm++ * volume;

      if (voice->repeat)
      {
         if (voice->stop_cb)
            voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);

         buf_free                  -= pcm_available;
         pcm_available              = sound->types.wav.frames * 2;
         pcm                        = sound->types.wav.pcm;
         voice->types.wav.position  = 0;
         goto again;
      }

      if (voice->stop_cb)
         voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);

      audio_mixer_release(voice);
   }
   else
   {
      for (i = buf_free; i != 0; i--)
         *buffer++ += *pcm++ * volume;

      voice->types.wav.position += buf_free;
   }
}

#ifdef HAVE_STB_VORBIS
static void audio_mixer_mix_ogg(float* buffer, size_t num_frames,
      audio_mixer_voice_t* voice,
      float volume)
{
   int i;
   float* temp_buffer = NULL;
   unsigned buf_free                = (unsigned)(num_frames * 2);
   unsigned temp_samples            = 0;
   float* pcm                       = NULL;

   if (!voice->types.ogg.stream)
      return;

   if (voice->types.ogg.position == voice->types.ogg.samples)
   {
again:
      if (temp_buffer == NULL)
         temp_buffer = (float*)malloc(AUDIO_MIXER_TEMP_BUFFER * sizeof(float));

      temp_samples = stb_vorbis_get_samples_float_interleaved(
            voice->types.ogg.stream, 2, temp_buffer,
            AUDIO_MIXER_TEMP_BUFFER) * 2;

      if (temp_samples == 0)
      {
         if (voice->repeat)
         {
            if (voice->stop_cb)
               voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);

            stb_vorbis_seek_start(voice->types.ogg.stream);
            goto again;
         }

         if (voice->stop_cb)
            voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);

         audio_mixer_release(voice);
         goto cleanup;
      }

      if (voice->types.ogg.resampler)
      {
         struct resampler_data info;
         info.data_in = temp_buffer;
         info.data_out = voice->types.ogg.buffer;
         info.input_frames = temp_samples / 2;
         info.output_frames = 0;
         info.ratio = voice->types.ogg.ratio;

         voice->types.ogg.resampler->process(
               voice->types.ogg.resampler_data, &info);
      }
      else
         memcpy(voice->types.ogg.buffer, temp_buffer,
               temp_samples * sizeof(float));

      voice->types.ogg.position = 0;
      voice->types.ogg.samples  = voice->types.ogg.buf_samples;
   }

   pcm = voice->types.ogg.buffer + voice->types.ogg.position;

   if (voice->types.ogg.samples < buf_free)
   {
      for (i = voice->types.ogg.samples; i != 0; i--)
         *buffer++ += *pcm++ * volume;

      buf_free -= voice->types.ogg.samples;
      goto again;
   }

   for (i = buf_free; i != 0; --i )
      *buffer++ += *pcm++ * volume;

   voice->types.ogg.position += buf_free;
   voice->types.ogg.samples  -= buf_free;

cleanup:
   if (temp_buffer != NULL)
      free(temp_buffer);
}
#endif

#ifdef HAVE_IBXM
static void audio_mixer_mix_mod(float* buffer, size_t num_frames,
      audio_mixer_voice_t* voice,
      float volume)
{
   int i;
   float samplef                    = 0.0f;
   unsigned temp_samples            = 0;
   unsigned buf_free                = (unsigned)(num_frames * 2);
   int* pcm                         = NULL;

   if (voice->types.mod.samples == 0)
   {
again:
      temp_samples = replay_get_audio(
            voice->types.mod.stream, voice->types.mod.buffer, 0 ) * 2;

      if (temp_samples == 0)
      {
         if (voice->repeat)
         {
            if (voice->stop_cb)
               voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);

            replay_seek( voice->types.mod.stream, 0);
            goto again;
         }

         if (voice->stop_cb)
            voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);

         audio_mixer_release(voice);
         return;
      }

      voice->types.mod.position = 0;
      voice->types.mod.samples  = temp_samples;
   }
   pcm = voice->types.mod.buffer + voice->types.mod.position;

   if (voice->types.mod.samples < buf_free)
   {
      for (i = voice->types.mod.samples; i != 0; i--)
      {
         samplef     = ((float)(*pcm++) + 32768.0f) / 65535.0f;
         samplef     = samplef * 2.0f - 1.0f;
         *buffer++  += samplef * volume;
      }

      buf_free -= voice->types.mod.samples;
      goto again;
   }

   for (i = buf_free; i != 0; --i )
   {
      samplef     = ((float)(*pcm++) + 32768.0f) / 65535.0f;
      samplef     = samplef * 2.0f - 1.0f;
      *buffer++  += samplef * volume;
   }

   voice->types.mod.position += buf_free;
   voice->types.mod.samples  -= buf_free;
}
#endif

#ifdef HAVE_DR_FLAC
static void audio_mixer_mix_flac(float* buffer, size_t num_frames,
      audio_mixer_voice_t* voice,
      float volume)
{
   int i;
   struct resampler_data info;
   float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 };
   unsigned buf_free                = (unsigned)(num_frames * 2);
   unsigned temp_samples            = 0;
   float *pcm                       = NULL;

   if (voice->types.flac.position == voice->types.flac.samples)
   {
again:
      /* drflac_read_pcm_frames_f32 takes a frame count and
       * writes frame_count * channel_count floats into the
       * output buffer.  Request at most AUDIO_MIXER_TEMP_BUFFER
       * / 2 frames so a stereo FLAC fills temp_buffer[AUDIO_
       * MIXER_TEMP_BUFFER] exactly, then multiply the return
       * value by 2 to convert frame count to interleaved float
       * count.  This matches the convention the downstream code
       * (info.input_frames = temp_samples / 2, memcpy length
       * of temp_samples * sizeof(float)) and the sibling mp3
       * and ogg mix paths already use.
       *
       * Pre-patch this passed AUDIO_MIXER_TEMP_BUFFER as the
       * frame count without the '/ 2' and stored the return as
       * 'temp_samples' without the '* 2'.  For a stereo FLAC
       * (by far the most common case) drflac wrote 2 *
       * AUDIO_MIXER_TEMP_BUFFER = 16384 floats into a 8192-
       * float stack buffer - a 32 KiB stack overflow.  Any
       * stereo FLAC asset played through the mixer (cheevo
       * unlock sounds, menu BGM, user-loaded content via the
       * audio mixer playlist, etc.) corrupted the stack on
       * every mix tick.  The downstream 'temp_samples / 2'
       * and memcpy length were also off by 2x under the old
       * convention, but the stack overflow hit first.
       *
       * For mono FLAC: the '* 2' overstates the float count by
       * 2x, causing the memcpy / resampler to read
       * uninitialised stack past the actual data.  That matches
       * the pre-existing implicit stereo-only assumption of
       * the FLAC/MP3/OGG mixer paths (voice->types.flac.
       * buf_samples is sized as TEMP_BUFFER * ratio, with no
       * per-channel adjustment) rather than introducing new
       * mono handling here.  Fixing mono playback is a separate
       * change. */
      temp_samples = (unsigned)drflac_read_pcm_frames_f32(
            voice->types.flac.stream,
            AUDIO_MIXER_TEMP_BUFFER / 2, temp_buffer) * 2;
      if (temp_samples == 0)
      {
         if (voice->repeat)
         {
            if (voice->stop_cb)
               voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);

            drflac_seek_to_pcm_frame(voice->types.flac.stream,0);
            goto again;
         }

         if (voice->stop_cb)
            voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);

         audio_mixer_release(voice);
         return;
      }

      info.data_in              = temp_buffer;
      info.data_out             = voice->types.flac.buffer;
      info.input_frames         = temp_samples / 2;
      info.output_frames        = 0;
      info.ratio                = voice->types.flac.ratio;

      if (voice->types.flac.resampler)
         voice->types.flac.resampler->process(
               voice->types.flac.resampler_data, &info);
      else
         memcpy(voice->types.flac.buffer, temp_buffer, temp_samples * sizeof(float));
      voice->types.flac.position = 0;
      voice->types.flac.samples  = voice->types.flac.buf_samples;
   }

   pcm = voice->types.flac.buffer + voice->types.flac.position;

   if (voice->types.flac.samples < buf_free)
   {
      for (i = voice->types.flac.samples; i != 0; i--)
         *buffer++ += *pcm++ * volume;

      buf_free -= voice->types.flac.samples;
      goto again;
   }

   for (i = buf_free; i != 0; --i )
      *buffer++ += *pcm++ * volume;

   voice->types.flac.position += buf_free;
   voice->types.flac.samples  -= buf_free;
}
#endif

#ifdef HAVE_DR_MP3
static void audio_mixer_mix_mp3(float* buffer, size_t num_frames,
      audio_mixer_voice_t* voice,
      float volume)
{
   int i;
   struct resampler_data info;
   float temp_buffer[AUDIO_MIXER_TEMP_BUFFER] = { 0 };
   unsigned buf_free                = (unsigned)(num_frames * 2);
   unsigned temp_samples            = 0;
   float* pcm                       = NULL;

   if (voice->types.mp3.position == voice->types.mp3.samples)
   {
again:
      temp_samples = (unsigned)drmp3_read_f32(
            &voice->types.mp3.stream,
            AUDIO_MIXER_TEMP_BUFFER / 2, temp_buffer) * 2;

      if (temp_samples == 0)
      {
         if (voice->repeat)
         {
            if (voice->stop_cb)
               voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_REPEATED);

            drmp3_seek_to_frame(&voice->types.mp3.stream,0);
            goto again;
         }

         if (voice->stop_cb)
            voice->stop_cb(voice->sound, AUDIO_MIXER_SOUND_FINISHED);

         audio_mixer_release(voice);
         return;
      }

      info.data_in              = temp_buffer;
      info.data_out             = voice->types.mp3.buffer;
      info.input_frames         = temp_samples / 2;
      info.output_frames        = 0;
      info.ratio                = voice->types.mp3.ratio;

      if (voice->types.mp3.resampler)
         voice->types.mp3.resampler->process(
               voice->types.mp3.resampler_data, &info);
      else
         memcpy(voice->types.mp3.buffer, temp_buffer,
               temp_samples * sizeof(float));
      voice->types.mp3.position = 0;
      voice->types.mp3.samples  = voice->types.mp3.buf_samples;
   }

   pcm = voice->types.mp3.buffer + voice->types.mp3.position;

   if (voice->types.mp3.samples < buf_free)
   {
      for (i = voice->types.mp3.samples; i != 0; i--)
         *buffer++ += *pcm++ * volume;

      buf_free -= voice->types.mp3.samples;
      goto again;
   }

   for (i = buf_free; i != 0; --i )
      *buffer++ += *pcm++ * volume;

   voice->types.mp3.position += buf_free;
   voice->types.mp3.samples  -= buf_free;
}
#endif

void audio_mixer_mix(float* buffer, size_t num_frames,
      float volume_override, bool override)
{
   unsigned i;
   size_t j                   = 0;
   float* sample              = NULL;
   audio_mixer_voice_t* voice = s_voices;

   for (i = 0; i < AUDIO_MIXER_MAX_VOICES; i++, voice++)
   {
      float volume;

      AUDIO_MIXER_LOCK(voice);

      volume = (override) ? volume_override : voice->volume;

      switch (voice->type)
      {
         case AUDIO_MIXER_TYPE_WAV:
            audio_mixer_mix_wav(buffer, num_frames, voice, volume);
            break;
         case AUDIO_MIXER_TYPE_OGG:
#ifdef HAVE_STB_VORBIS
            audio_mixer_mix_ogg(buffer, num_frames, voice, volume);
#endif
            break;
         case AUDIO_MIXER_TYPE_MOD:
#ifdef HAVE_IBXM
            audio_mixer_mix_mod(buffer, num_frames, voice, volume);
#endif
            break;
         case AUDIO_MIXER_TYPE_FLAC:
#ifdef HAVE_DR_FLAC
            audio_mixer_mix_flac(buffer, num_frames, voice, volume);
#endif
            break;
            case AUDIO_MIXER_TYPE_MP3:
#ifdef HAVE_DR_MP3
            audio_mixer_mix_mp3(buffer, num_frames, voice, volume);
#endif
            break;
         case AUDIO_MIXER_TYPE_NONE:
            break;
      }

      AUDIO_MIXER_UNLOCK(voice);
   }

   for (j = 0, sample = buffer; j < num_frames * 2; j++, sample++)
   {
      if (*sample < -1.0f)
         *sample = -1.0f;
      else if (*sample > 1.0f)
         *sample = 1.0f;
   }
}

float audio_mixer_voice_get_volume(audio_mixer_voice_t *voice)
{
   if (!voice)
      return 0.0f;

   return voice->volume;
}

void audio_mixer_voice_set_volume(audio_mixer_voice_t *voice, float val)
{
   if (!voice)
      return;

   AUDIO_MIXER_LOCK(voice);
   voice->volume = val;
   AUDIO_MIXER_UNLOCK(voice);
}


================================================
FILE: audio/conversion/float_to_s16.c
================================================
/* Copyright  (C) 2010-2021 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (float_to_s16.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include <stdint.h>
#include <stddef.h>

#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif

#include <features/features_cpu.h>
#include <audio/conversion/float_to_s16.h>

#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
static bool float_to_s16_neon_enabled = false;
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
void convert_float_s16_asm(int16_t *s, const float *in, size_t len);
#else
#include <arm_neon.h>
#endif

void convert_float_to_s16(int16_t *s, const float *in, size_t len)
{
   size_t i           = 0;
   if (float_to_s16_neon_enabled)
   {
      float        gf = (1<<15);
      float32x4_t vgf = {gf, gf, gf, gf};
      while (len >= 8)
      {
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
         size_t aligned_samples = len & ~7;
         if (aligned_samples)
            convert_float_s16_asm(s, in, aligned_samples);

         s        += aligned_samples;
         in       += aligned_samples;
         samples  -= aligned_samples;
         i         = 0;
#else
         int16x4x2_t oreg;
         int32x4x2_t creg;
         float32x4x2_t inreg = vld2q_f32(in);
         creg.val[0]         = vcvtq_s32_f32(vmulq_f32(inreg.val[0], vgf));
         creg.val[1]         = vcvtq_s32_f32(vmulq_f32(inreg.val[1], vgf));
         oreg.val[0]         = vqmovn_s32(creg.val[0]);
         oreg.val[1]         = vqmovn_s32(creg.val[1]);
         vst2_s16(s, oreg);
         in      += 8;
         s       += 8;
         len     -= 8;
#endif
      }
   }

   for (; i < len; i++)
   {
      int32_t val = (int32_t)(in[i] * 0x8000);
      s[i]        = (val > 0x7FFF) ? 0x7FFF :
         (val < -0x8000 ? -0x8000 : (int16_t)val);
   }
}

void convert_float_to_s16_init_simd(void)
{
   uint64_t cpu = cpu_features_get();

   if (cpu & RETRO_SIMD_NEON)
      float_to_s16_neon_enabled = true;
}
#else
void convert_float_to_s16(int16_t *s, const float *in, size_t len)
{
   size_t i          = 0;
#if defined(__SSE2__)
   __m128 factor     = _mm_set1_ps((float)0x8000);
   /* Initialize a 4D vector with 32768.0 for its elements */

   for (i = 0; i + 8 <= len; i += 8, in += 8, s += 8)
   { /* Skip forward 8 samples at a time... */
      __m128 input_a = _mm_loadu_ps(in + 0); /* Create a 4-float vector from the next four samples... */
      __m128 input_b = _mm_loadu_ps(in + 4); /* ...and another from the *next* next four. */
      __m128 res_a   = _mm_mul_ps(input_a, factor);
      __m128 res_b   = _mm_mul_ps(input_b, factor); /* Multiply these samples by 32768 */
      __m128i ints_a = _mm_cvtps_epi32(res_a);
      __m128i ints_b = _mm_cvtps_epi32(res_b); /* Convert the samples to 32-bit integers */
      __m128i packed = _mm_packs_epi32(ints_a, ints_b); /* Then convert them to 16-bit ints, clamping to [-32768, 32767] */

      _mm_storeu_si128((__m128i *)s, packed); /* Then put the result in the output array */
   }

   len               = len - i;
   i                 = 0;
   /* If there are any stray samples at the end, we need to convert them
    * (maybe the original array didn't contain a multiple of 8 samples) */
#elif defined(__ALTIVEC__)
   int samples_in    = len;

   /* Unaligned loads/store is a bit expensive,
    * so we optimize for the good path (very likely). */
   if (((uintptr_t)s & 15) + ((uintptr_t)in & 15) == 0)
   {
      size_t i;
      for (i = 0; i + 8 <= len; i += 8, in += 8, s += 8)
      {
         vector float       input0 = vec_ld( 0, in);
         vector float       input1 = vec_ld(16, in);
         vector signed int result0 = vec_cts(input0, 15);
         vector signed int result1 = vec_cts(input1, 15);
         vec_st(vec_packs(result0, result1), 0, s);
      }

      samples_in    -= i;
   }

   len               = samples_in;
   i                 = 0;
#elif defined(_MIPS_ARCH_ALLEGREX)
#ifdef DEBUG
   /* Make sure the buffers are 16 byte aligned, this should be
    * the default behaviour of malloc in the PSPSDK.
    * Assume alignment. */
   retro_assert(((uintptr_t)in  & 0xf) == 0);
   retro_assert(((uintptr_t)s & 0xf) == 0);
#endif

   for (i = 0; i + 8 <= len; i += 8)
   {
      __asm__ (
            ".set    push                 \n"
            ".set    noreorder            \n"

            "lv.q    c100,  0(%0)         \n"
            "lv.q    c110,  16(%0)        \n"

            "vf2in.q c100, c100, 31       \n"
            "vf2in.q c110, c110, 31       \n"
            "vi2s.q  c100, c100           \n"
            "vi2s.q  c102, c110           \n"

            "sv.q    c100,  0(%1)         \n"

            ".set    pop                  \n"
            :: "r"(in + i), "r"(s + i));
   }
#endif

   /* This loop converts stray samples to the right format,
    * but it's also a fallback in case no SIMD instructions are available. */
   for (; i < len; i++)
   {
      int32_t val    = (int32_t)(in[i] * 0x8000);
      s[i]           = (val > 0x7FFF)
         ? 0x7FFF
         : (val < -0x8000 ? -0x8000 : (int16_t)val);
   }
}

void convert_float_to_s16_init_simd(void) { }
#endif


================================================
FILE: audio/conversion/float_to_s16_neon.S
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (float_to_s16_neon.S).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)

#ifndef __MACH__
.arm
#endif

.align 4
.globl convert_float_s16_asm
#ifndef __MACH__
.type convert_float_s16_asm, %function
#endif
.globl _convert_float_s16_asm
#ifndef __MACH__
.type _convert_float_s16_asm, %function
#endif
# convert_float_s16_asm(int16_t *out, const float *in, size_t samples)
convert_float_s16_asm:
_convert_float_s16_asm:
   # Hacky way to get a constant of 2^15.
   # ((2^4)^2)^2 * 0.5 = 2^15
   vmov.f32 q8, #16.0
   vmov.f32 q9, #0.5
   vmul.f32 q8, q8, q8
   vmul.f32 q8, q8, q8
   vmul.f32 q8, q8, q9

1:
   # Preload here?
   vld1.f32 {q0-q1}, [r1]!

   vmul.f32 q0, q0, q8
   vmul.f32 q1, q1, q8

   vcvt.s32.f32 q0, q0
   vcvt.s32.f32 q1, q1

   vqmovn.s32 d4, q0
   vqmovn.s32 d5, q1

   vst1.f32 {d4-d5}, [r0]!

   # Guaranteed to get samples in multiples of 8.
   subs r2, r2, #8
   bne 1b

   bx lr

#endif


================================================
FILE: audio/conversion/float_to_s16_neon.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (float_to_s16_neon.S).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)

#if defined(__thumb__)
#define DECL_ARMMODE(x) "  .align 2\n" "  .global " x "\n" "  .thumb\n" "  .thumb_func\n" "  .type " x ", %function\n" x ":\n"
#else
#define DECL_ARMMODE(x) "  .align 4\n" "  .global " x "\n" "  .arm\n" x ":\n"
#endif

asm(
    DECL_ARMMODE("convert_float_s16_asm")
    DECL_ARMMODE("_convert_float_s16_asm")
    "# convert_float_s16_asm(int16_t *s, const float *in, size_t len)\n"
    "   # Hacky way to get a constant of 2^15.\n"
    "   # ((2^4)^2)^2 * 0.5 = 2^15\n"
    "   vmov.f32 q8, #16.0\n"
    "   vmov.f32 q9, #0.5\n"
    "   vmul.f32 q8, q8, q8\n"
    "   vmul.f32 q8, q8, q8\n"
    "   vmul.f32 q8, q8, q9\n"
    "\n"
    "1:\n"
    "   # Preload here?\n"
    "   vld1.f32 {q0-q1}, [r1]!\n"
    "\n"
    "   vmul.f32 q0, q0, q8\n"
    "   vmul.f32 q1, q1, q8\n"
    "\n"
    "   vcvt.s32.f32 q0, q0\n"
    "   vcvt.s32.f32 q1, q1\n"
    "\n"
    "   vqmovn.s32 d4, q0\n"
    "   vqmovn.s32 d5, q1\n"
    "\n"
    "   vst1.f32 {d4-d5}, [r0]!\n"
    "\n"
    "   # Guaranteed to get samples in multiples of 8.\n"
    "   subs r2, r2, #8\n"
    "   bne 1b\n"
    "\n"
    "   bx lr\n"
    "\n"
    );
#endif


================================================
FILE: audio/conversion/mono_to_stereo_float.c
================================================
/* Copyright  (C) 2010-2023 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (mono_to_stereo.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include <stdint.h>
#include <stddef.h>

#include <audio/conversion/dual_mono.h>

/* TODO: Use SIMD instructions to make this faster (or show that it's not needed) */
void convert_to_dual_mono_float(float *s, const float *in, size_t len)
{
   unsigned i = 0;

   if (!s || !in || !len)
      return;

   for (; i < len; i++)
   {
      s[i * 2]     = in[i];
      s[i * 2 + 1] = in[i];
   }
}

/* Why is there no equivalent for int16_t samples?
 * No inherent reason, I just didn't need one.
 * If you do, open a pull request. */


================================================
FILE: audio/conversion/s16_to_float.c
================================================
/* Copyright  (C) 2010-2021 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (s16_to_float.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif

#include <boolean.h>
#include <features/features_cpu.h>
#include <audio/conversion/s16_to_float.h>

#if (defined(__ARM_NEON__) || defined(HAVE_NEON))
static bool s16_to_float_neon_enabled = false;

#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
/* Avoid potential hard-float/soft-float ABI issues. */
void convert_s16_float_asm(float *s, const int16_t *in,
      size_t len, const float *gain);
#else
#include <arm_neon.h>
#endif

void convert_s16_to_float(float *s,
      const int16_t *in, size_t len, float gain)
{
   unsigned i      = 0;

   if (s16_to_float_neon_enabled)
   {
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
      size_t aligned_samples = len & ~7;
      if (aligned_samples)
         convert_s16_float_asm(s, in, aligned_samples, &gain);

      /* Could do all conversion in ASM, but keep it simple for now. */
      s                 += aligned_samples;
      in                += aligned_samples;
      len               -= aligned_samples;
      i                  = 0;
#else
      float        gf    = gain / (1 << 15);
      float32x4_t vgf    = {gf, gf, gf, gf};
      while (len >= 8)
      {
         float32x4x2_t oreg;
         int16x4x2_t inreg   = vld2_s16(in);
         int32x4_t      p1   = vmovl_s16(inreg.val[0]);
         int32x4_t      p2   = vmovl_s16(inreg.val[1]);
         oreg.val[0]         = vmulq_f32(vcvtq_f32_s32(p1), vgf);
         oreg.val[1]         = vmulq_f32(vcvtq_f32_s32(p2), vgf);
         vst2q_f32(s, oreg);
         in                 += 8;
         s                  += 8;
         len                -= 8;
      }
#endif
   }

   gain /= 0x8000;

   for (; i < len; i++)
      s[i] = (float)in[i] * gain;
}

void convert_s16_to_float_init_simd(void)
{
   uint64_t cpu = cpu_features_get();

   if (cpu & RETRO_SIMD_NEON)
      s16_to_float_neon_enabled = true;
}
#else
void convert_s16_to_float(float *s,
      const int16_t *in, size_t len, float gain)
{
   unsigned i      = 0;

#if defined(__SSE2__)
   float fgain   = gain / UINT32_C(0x80000000);
   __m128 factor = _mm_set1_ps(fgain);

   for (i = 0; i + 8 <= len; i += 8, in += 8, s += 8)
   {
      __m128i input    = _mm_loadu_si128((const __m128i *)in);
      __m128i regs_l   = _mm_unpacklo_epi16(_mm_setzero_si128(), input);
      __m128i regs_r   = _mm_unpackhi_epi16(_mm_setzero_si128(), input);
      __m128 output_l  = _mm_mul_ps(_mm_cvtepi32_ps(regs_l), factor);
      __m128 output_r  = _mm_mul_ps(_mm_cvtepi32_ps(regs_r), factor);

      _mm_storeu_ps(s + 0, output_l);
      _mm_storeu_ps(s + 4, output_r);
   }

   len     = len - i;
   i       = 0;
#elif defined(__ALTIVEC__)
   size_t samples_in = len;

   /* Unaligned loads/store is a bit expensive, so we
    * optimize for the good path (very likely). */
   if (((uintptr_t)s & 15) + ((uintptr_t)in & 15) == 0)
   {
      const vector float gain_vec = { gain, gain , gain, gain };
      const vector float zero_vec = { 0.0f, 0.0f, 0.0f, 0.0f};

      for (i = 0; i + 8 <= len; i += 8, in += 8, s += 8)
      {
         vector signed short input = vec_ld(0, in);
         vector signed int hi      = vec_unpackh(input);
         vector signed int lo      = vec_unpackl(input);
         vector float out_hi       = vec_madd(vec_ctf(hi, 15), gain_vec, zero_vec);
         vector float out_lo       = vec_madd(vec_ctf(lo, 15), gain_vec, zero_vec);

         vec_st(out_hi,  0, s);
         vec_st(out_lo, 16, s);
      }

      samples_in -= i;
   }

   len     = samples_in;
   i       = 0;
#endif

   gain   /= 0x8000;

#if defined(_MIPS_ARCH_ALLEGREX)
#ifdef DEBUG
   /* Make sure the buffer is 16 byte aligned, this should be the
    * default behaviour of malloc in the PSPSDK.
    * Only the output buffer can be assumed to be 16-byte aligned. */
   retro_assert(((uintptr_t)s & 0xf) == 0);
#endif

   __asm__ (
         ".set    push                    \n"
         ".set    noreorder               \n"
         "mtv     %0, s200                \n"
         ".set    pop                     \n"
         ::"r"(gain));

   for (i = 0; i + 16 <= len; i += 16)
   {
      __asm__ (
            ".set    push                 \n"
            ".set    noreorder            \n"

            "lv.s    s100,  0(%0)         \n"
            "lv.s    s101,  4(%0)         \n"
            "lv.s    s110,  8(%0)         \n"
            "lv.s    s111, 12(%0)         \n"
            "lv.s    s120, 16(%0)         \n"
            "lv.s    s121, 20(%0)         \n"
            "lv.s    s130, 24(%0)         \n"
            "lv.s    s131, 28(%0)         \n"

            "vs2i.p  c100, c100           \n"
            "vs2i.p  c110, c110           \n"
            "vs2i.p  c120, c120           \n"
            "vs2i.p  c130, c130           \n"

            "vi2f.q  c100, c100, 16       \n"
            "vi2f.q  c110, c110, 16       \n"
            "vi2f.q  c120, c120, 16       \n"
            "vi2f.q  c130, c130, 16       \n"

            "vmscl.q e100, e100, s200     \n"

            "sv.q    c100,  0(%1)         \n"
            "sv.q    c110, 16(%1)         \n"
            "sv.q    c120, 32(%1)         \n"
            "sv.q    c130, 48(%1)         \n"

            ".set    pop                  \n"
            :: "r"(in + i), "r"(s + i));
   }
#endif

   for (; i < len; i++)
      s[i] = (float)in[i] * gain;
}

void convert_s16_to_float_init_simd(void) { }
#endif



================================================
FILE: audio/conversion/s16_to_float_neon.S
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (s16_to_float_neon.S).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)

#ifndef __MACH__
.arm
#endif

.align 4
.globl convert_s16_float_asm
#ifndef __MACH__
.type convert_s16_float_asm, %function
#endif
.globl _convert_s16_float_asm
#ifndef __MACH__
.type _convert_s16_float_asm, %function
#endif
# convert_s16_float_asm(float *out, const int16_t *in, size_t samples, const float *gain)
convert_s16_float_asm:
_convert_s16_float_asm:
   # Hacky way to get a constant of 2^-15.
   # Might be faster to just load a constant from memory.
   # It's just done once however ...
   vmov.f32 q8, #0.25
   vmul.f32 q8, q8, q8
   vmul.f32 q8, q8, q8
   vmul.f32 q8, q8, q8
   vadd.f32 q8, q8, q8

   # Apply gain
   vld1.f32 {d6[0]}, [r3]
   vmul.f32 q8, q8, d6[0]

1:
   # Preload here?
   vld1.s16 {q0}, [r1]!

   # Widen to 32-bit
   vmovl.s16 q1, d0
   vmovl.s16 q2, d1

   # Convert to float
   vcvt.f32.s32 q1, q1
   vcvt.f32.s32 q2, q2

   vmul.f32 q1, q1, q8
   vmul.f32 q2, q2, q8

   vst1.f32 {q1-q2}, [r0]!

   # Guaranteed to get samples in multiples of 8.
   subs r2, r2, #8
   bne 1b

   bx lr

#endif


================================================
FILE: audio/conversion/s16_to_float_neon.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (s16_to_float_neon.S).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_ASM_OPTIMIZATIONS)

#if defined(__thumb__)
#define DECL_ARMMODE(x) "  .align 2\n" "  .global " x "\n" "  .thumb\n" "  .thumb_func\n" "  .type " x ", %function\n" x ":\n"
#else
#define DECL_ARMMODE(x) "  .align 4\n" "  .global " x "\n" "  .arm\n" x ":\n"
#endif

asm(
    DECL_ARMMODE("convert_s16_float_asm")
    DECL_ARMMODE("_convert_s16_float_asm")
    "# convert_s16_float_asm(float *s, const int16_t *in, size_t len, const float *gain)\n"
    "   # Hacky way to get a constant of 2^-15.\n"
    "   # Might be faster to just load a constant from memory.\n"
    "   # It's just done once however ...\n"
    "   vmov.f32 q8, #0.25\n"
    "   vmul.f32 q8, q8, q8\n"
    "   vmul.f32 q8, q8, q8\n"
    "   vmul.f32 q8, q8, q8\n"
    "   vadd.f32 q8, q8, q8\n"
    "\n"
    "   # Apply gain\n"
    "   vld1.f32 {d6[0]}, [r3]\n"
    "   vmul.f32 q8, q8, d6[0]\n"
    "\n"
    "1:\n"
    "   # Preload here?\n"
    "   vld1.s16 {q0}, [r1]!\n"
    "\n"
    "   # Widen to 32-bit\n"
    "   vmovl.s16 q1, d0\n"
    "   vmovl.s16 q2, d1\n"
    "\n"
    "   # Convert to float\n"
    "   vcvt.f32.s32 q1, q1\n"
    "   vcvt.f32.s32 q2, q2\n"
    "\n"
    "   vmul.f32 q1, q1, q8\n"
    "   vmul.f32 q2, q2, q8\n"
    "\n"
    "   vst1.f32 {q1-q2}, [r0]!\n"
    "\n"
    "   # Guaranteed to get samples in multiples of 8.\n"
    "   subs r2, r2, #8\n"
    "   bne 1b\n"
    "\n"
    "   bx lr\n"
    "\n"
    );
#endif


================================================
FILE: audio/conversion/stereo_to_mono_float.c
================================================
/* Copyright  (C) 2010-2023 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (mono_to_stereo.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include <stdint.h>
#include <stddef.h>

#include <audio/conversion/dual_mono.h>

/* TODO: Use SIMD instructions to make this faster (or show that it's not needed) */
void convert_to_mono_float_left(float *out, const float *in, size_t frames)
{
   unsigned i = 0;

   if (!out || !in || !frames)
      return;

   for (; i < frames; i++)
   {
      out[i] = in[i * 2];
   }
}

/* Why is there no equivalent for int16_t samples?
 * No inherent reason, I just didn't need one.
 * If you do, open a pull request.
 * Same goes for the lack of a convert_to_mono_float_right;
 * I didn't need one, so I didn't write one. */

================================================
FILE: audio/dsp_filter.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (dsp_filter.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>

#include <retro_miscellaneous.h>

#include <compat/posix_string.h>
#include <dynamic/dylib.h>

#include <file/file_path.h>
#include <file/config_file_userdata.h>
#include <features/features_cpu.h>
#include <lists/string_list.h>
#include <string/stdstring.h>
#include <libretro_dspfilter.h>

#include <audio/dsp_filter.h>

struct retro_dsp_plug
{
#ifdef HAVE_DYLIB
   dylib_t lib;
#endif
   const struct dspfilter_implementation *impl;
};

struct retro_dsp_instance
{
   const struct dspfilter_implementation *impl;
   void *impl_data;
};

struct retro_dsp_filter
{
   config_file_t *conf;

   struct retro_dsp_plug *plugs;
   unsigned num_plugs;

   struct retro_dsp_instance *instances;
   unsigned num_instances;
};

static const struct dspfilter_implementation *find_implementation(
      retro_dsp_filter_t *dsp, const char *ident)
{
   unsigned i;
   for (i = 0; i < dsp->num_plugs; i++)
   {
      if (string_is_equal(dsp->plugs[i].impl->short_ident, ident))
         return dsp->plugs[i].impl;
   }

   return NULL;
}

static const struct dspfilter_config dspfilter_config = {
   config_userdata_get_float,
   config_userdata_get_int,
   config_userdata_get_float_array,
   config_userdata_get_int_array,
   config_userdata_get_string,
   config_userdata_free,
};

static bool create_filter_graph(retro_dsp_filter_t *dsp, float sample_rate)
{
   unsigned i;
   struct retro_dsp_instance *instances = NULL;
   unsigned filters                     = 0;

   if (!config_get_uint(dsp->conf, "filters", &filters))
      return false;

   instances = (struct retro_dsp_instance*)calloc(filters, sizeof(*instances));
   if (!instances)
      return false;

   dsp->instances     = instances;
   dsp->num_instances = filters;

   for (i = 0; i < filters; i++)
   {
      struct config_file_userdata userdata;
      struct dspfilter_info info;
      char key[64];
      char name[64];

      key[0] = name[0] = '\0';

      info.input_rate  = sample_rate;

      snprintf(key, sizeof(key), "filter%u", i);

      if (!config_get_array(dsp->conf, key, name, sizeof(name)))
         return false;

      dsp->instances[i].impl = find_implementation(dsp, name);
      if (!dsp->instances[i].impl)
         return false;

      userdata.conf = dsp->conf;
      /* Index-specific configs take priority over ident-specific. */
      userdata.prefix[0] = key;
      userdata.prefix[1] = dsp->instances[i].impl->short_ident;

      dsp->instances[i].impl_data = dsp->instances[i].impl->init(&info,
            &dspfilter_config, &userdata);
      if (!dsp->instances[i].impl_data)
         return false;
   }

   return true;
}

#if defined(HAVE_FILTERS_BUILTIN)
extern const struct dspfilter_implementation *chorus_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *delta_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *echo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *eq_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *iir_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *panning_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *phaser_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *reverb_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *tremolo_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *vibrato_dspfilter_get_implementation(dspfilter_simd_mask_t mask);
extern const struct dspfilter_implementation *wahwah_dspfilter_get_implementation(dspfilter_simd_mask_t mask);

static const dspfilter_get_implementation_t dsp_plugs_builtin[] = {
   chorus_dspfilter_get_implementation,
   delta_dspfilter_get_implementation,
   echo_dspfilter_get_implementation,
   eq_dspfilter_get_implementation,
   iir_dspfilter_get_implementation,
   panning_dspfilter_get_implementation,
   phaser_dspfilter_get_implementation,
   reverb_dspfilter_get_implementation,
   tremolo_dspfilter_get_implementation,
   vibrato_dspfilter_get_implementation,
   wahwah_dspfilter_get_implementation,
};

static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
   unsigned i;
   dspfilter_simd_mask_t mask   = (dspfilter_simd_mask_t)cpu_features_get();
   struct retro_dsp_plug *plugs = (struct retro_dsp_plug*)
      calloc(ARRAY_SIZE(dsp_plugs_builtin), sizeof(*plugs));

   if (!plugs)
      return false;

   dsp->plugs     = plugs;
   dsp->num_plugs = ARRAY_SIZE(dsp_plugs_builtin);

   for (i = 0; i < ARRAY_SIZE(dsp_plugs_builtin); i++)
   {
      dsp->plugs[i].impl = dsp_plugs_builtin[i](mask);
      if (!dsp->plugs[i].impl)
         return false;
   }

   return true;
}
#elif defined(HAVE_DYLIB)
static bool append_plugs(retro_dsp_filter_t *dsp, struct string_list *list)
{
   unsigned i;
   dspfilter_simd_mask_t mask = (dspfilter_simd_mask_t)cpu_features_get();
   unsigned list_size         = list ? (unsigned)list->size : 0;

   for (i = 0; i < list_size; i++)
   {
      dspfilter_get_implementation_t cb;
      const struct dspfilter_implementation *impl = NULL;
      struct retro_dsp_plug *new_plugs            = NULL;
      dylib_t lib                                 =
         dylib_load(list->elems[i].data);

      if (!lib)
         continue;

      cb = (dspfilter_get_implementation_t)dylib_proc(lib, "dspfilter_get_implementation");
      if (!cb)
      {
         dylib_close(lib);
         continue;
      }

      impl = cb(mask);
      if (!impl)
      {
         dylib_close(lib);
         continue;
      }

      if (impl->api_version != DSPFILTER_API_VERSION)
      {
         dylib_close(lib);
         continue;
      }

      new_plugs = (struct retro_dsp_plug*)
         realloc(dsp->plugs, sizeof(*dsp->plugs) * (dsp->num_plugs + 1));
      if (!new_plugs)
      {
         dylib_close(lib);
         return false;
      }

      /* Found plug. */

      dsp->plugs = new_plugs;
      dsp->plugs[dsp->num_plugs].lib = lib;
      dsp->plugs[dsp->num_plugs].impl = impl;
      dsp->num_plugs++;
   }

   return true;
}
#endif

retro_dsp_filter_t *retro_dsp_filter_new(
      const char *filter_config,
      void *string_data,
      float sample_rate)
{
   config_file_t *conf           = NULL;
   struct string_list *plugs     = NULL;
   retro_dsp_filter_t *dsp       = (retro_dsp_filter_t*)calloc(1, sizeof(*dsp));

   if (!dsp)
      return NULL;

   if (!(conf = config_file_new_from_path_to_string(filter_config)))
      goto error;

   dsp->conf = conf;

   if (string_data)
      plugs = (struct string_list*)string_data;

#if defined(HAVE_DYLIB) || defined(HAVE_FILTERS_BUILTIN)
   if (!append_plugs(dsp, plugs))
      goto error;
#endif

   if (plugs)
      string_list_free(plugs);
   plugs = NULL;

   if (!create_filter_graph(dsp, sample_rate))
      goto error;

   return dsp;

error:
   if (plugs)
      string_list_free(plugs);
   retro_dsp_filter_free(dsp);
   return NULL;
}

void retro_dsp_filter_free(retro_dsp_filter_t *dsp)
{
   unsigned i;
   if (!dsp)
      return;

   for (i = 0; i < dsp->num_instances; i++)
   {
      if (dsp->instances[i].impl_data && dsp->instances[i].impl)
         dsp->instances[i].impl->free(dsp->instances[i].impl_data);
   }
   free(dsp->instances);

#ifdef HAVE_DYLIB
   for (i = 0; i < dsp->num_plugs; i++)
   {
      if (dsp->plugs[i].lib)
         dylib_close(dsp->plugs[i].lib);
   }
   free(dsp->plugs);
#endif

   if (dsp->conf)
      config_file_free(dsp->conf);

   free(dsp);
}

void retro_dsp_filter_process(retro_dsp_filter_t *dsp,
      struct retro_dsp_data *data)
{
   unsigned i;
   struct dspfilter_output output = {0};
   struct dspfilter_input input   = {0};

   output.samples = data->input;
   output.frames  = data->input_frames;

   for (i = 0; i < dsp->num_instances; i++)
   {
      input.samples = output.samples;
      input.frames  = output.frames;
      dsp->instances[i].impl->process(
            dsp->instances[i].impl_data, &output, &input);
   }

   data->output        = output.samples;
   data->output_frames = output.frames;
}


================================================
FILE: audio/dsp_filters/BassBoost.dsp
================================================
filters = 2
filter0 = iir
filter1 = panning

iir_gain = 10.0
iir_type = BBOOST
iir_frequency = 200.0

# Avoids clipping.
panning_left_mix = "0.3 0.0"
panning_right_mix = "0.0 0.3"


================================================
FILE: audio/dsp_filters/ChipTune-Lowpass.dsp
================================================
filters = 1
filter0 = iir

iir_frequency = 8600.0
iir_quality = 0.707
iir_gain = 6.0
iir_type = LPF


================================================
FILE: audio/dsp_filters/ChipTuneEnhance.dsp
================================================
filters = 4
filter0 = eq
filter1 = reverb
filter2 = iir
filter3 = panning

eq_frequencies = "32 64 125 250 500 1000 2000 4000 8000 16000 20000"
eq_gains = "6 9 12 7 6 5 7 9 11 6 0"

# Reverb - slight reverb
 reverb_drytime = 0.5
 reverb_wettime = 0.15
 reverb_damping = 0.8
 reverb_roomwidth = 0.25
 reverb_roomsize = 0.25

# IIR - filters out some harsh sounds on the upper end
iir_type = RIAA_CD

# Panning - cut the volume a bit
panning_left_mix = "0.75 0.0"
panning_right_mix = "0.0 0.75"


================================================
FILE: audio/dsp_filters/Chorus.dsp
================================================
filters = 1
filter0 = chorus

# Controls the base delay of the chorus (milliseconds).
# chorus_delay_ms = 25.0
#
# Controls the depth of the delay. The delay will vary between delay_ms +/- depth_ms.
# chorus_depth_ms = 1.0
#
# Frequency of LFO which controls delay.
# chorus_lfo_freq = 0.5
#
# Controls dry/wet-ness of effect. 1.0 = full chorus, 0.0 = no chorus.
# chorus_drywet = 0.8


================================================
FILE: audio/dsp_filters/Crystalizer.dsp
================================================
filters = 1
filter0 = crystalizer
# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.
crystalizer_intensity = 5.0


================================================
FILE: audio/dsp_filters/EQ.dsp
================================================
filters = 1
filter0 = eq

# Defaults

# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0

# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8

# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.

# By default, this filter has a flat frequency response.

# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch


================================================
FILE: audio/dsp_filters/Echo.dsp
================================================
filters = 1
filter0 = echo

# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) and feedback factors.
# Echo output from all channels can be fed back into each other to create a somewhat reverb-like effect if desired.

# Defaults, 200 ms delay echo with feedback:
# Delay in ms. Takes an array with multiple channels.
# echo_delay = "200"
# Feedback factor for echo.
# echo_feedback = "0.5"
# Overall echo amplification. If too high, the echo becomes unstable due to feedback.
# echo_amp = "0.2"

# Reverby preset.
# echo_delay    = " 60  80 120 172 200 320 380"
# echo_feedback = "0.5 0.5 0.4 0.3 0.5 0.3 0.2"

# echo_amp = "0.12"


================================================
FILE: audio/dsp_filters/EchoReverb.dsp
================================================
filters = 2
filter0 = echo
filter1 = reverb

echo_delay = "200"
echo_feedback = "0.6"
echo_amp = "0.25"

reverb_roomwidth = 0.75
reverb_roomsize = 0.75
reverb_damping = 1.0
reverb_wettime = 0.3


================================================
FILE: audio/dsp_filters/HighShelfDampen.dsp
================================================
filters = 1
filter0 = iir

iir_gain = -12.0
iir_type = HSH
iir_frequency = 8000.0


================================================
FILE: audio/dsp_filters/IIR.dsp
================================================
filters = 1
filter0 = iir

# Defaults.
#iir_frequency = 1024.0
#iir_quality = 0.707
#iir_gain = 0.0
#iir_type = LPF

# Filter types:
# LPF: Low-pass
# HPF: High-pass
# BPCSGF: Band-pass #1
# BPZPGF: Band-pass #2
# APF: Allpass
# NOTCH: Notch filter
# RIAA_phono: RIAA record/tape deemphasis
# PEQ: peaking band EQ
# BBOOST: Bassboost
# LSH: Low-shelf
# HSH: High-shelf
# RIAA_CD: CD de-emphasis


================================================
FILE: audio/dsp_filters/LowPassCPS.dsp
================================================
filters = 1
filter0 = eq

eq_frequencies = "8000 10000 12500 16000 20000"
eq_gains = "0 -30 -30 -30 -30"

# Low pass filter for the QSound chip from CPS-1/2.
# Some games have aliasing due low quality samples, so you can hear some annoying noisy near 11 kHz

# Defaults

# Beta factor for Kaiser window.
# Lower values will allow better frequency resolution, but more ripple.
# eq_window_beta = 4.0

# The block size on which FFT is done.
# Too high value requires more processing as well as longer latency but
# allows finer-grained control over the spectrum.
# eq_block_size_log2 = 8

# An array of which frequencies to control.
# You can create an arbitrary amount of these sampling points.
# The EQ will try to create a frequency response which fits well to these points.
# The filter response is linearly interpolated between sampling points here.
#
# It is implied that 0 Hz (DC) and Nyquist have predefined gains of 0 dB which are interpolated against.
# If you want a "peak" in the spectrum or similar, you have to define close points to say, 0 dB.
#
# E.g.: A boost of 3 dB at 1 kHz can be expressed as.
# eq_frequencies = "500 1000 2000"
# eq_gains = "0 3 0"
# Due to frequency domain smearing, you will not get exactly +3 dB at 1 kHz.

# By default, this filter has a low pass response with cuttof frequency at ~8600 Hz.

# Dumps the impulse response generated by the EQ as a plain-text file
# with one coefficient per line.
# eq_impulse_response_output = "eq_impulse.txt"
#
# Using GNU Octave or Matlab, you can plot the response with:
#
# f = fopen('/path/to/eq_impulse.txt');
# l = textscan(f, '%f');
# res = l{1};
# freqz(res, 1, 4096, 48000);
#
# It will give the response in Hz; 48000 is the default Output Rate of RetroArch


================================================
FILE: audio/dsp_filters/Makefile
================================================
compiler    := gcc
extra_flags :=
use_neon    := 0
build       = release
DYLIB	      := so
PREFIX      := /usr
INSTALLDIR  := $(PREFIX)/lib/retroarch/filters/audio

ifeq ($(platform),)
   platform = unix
   ifeq ($(shell uname -s),)
      platform = win
   else ifneq ($(findstring Darwin,$(shell uname -s)),)
      platform = osx
      arch     = intel
      ifeq ($(shell uname -p),powerpc)
         arch = ppc
      endif
   else ifneq ($(findstring MINGW,$(shell uname -s)),)
      platform = win
   endif
endif

ifeq ($(platform),gcc)
   extra_rules_gcc := $(shell $(compiler) -dumpmachine)
endif

ifneq (,$(findstring armv7,$(extra_rules_gcc)))
   extra_flags += -mcpu=cortex-a9 -mtune=cortex-a9 -mfpu=neon
   use_neon := 1
endif

ifneq (,$(findstring hardfloat,$(extra_rules_gcc)))
   extra_flags += -mfloat-abi=hard
endif

ifeq (release,$(build))
   extra_flags += -O2
endif

ifeq (debug,$(build))
   extra_flags += -O0 -g
endif

ldflags := $(LDFLAGS) -shared -lm -Wl,--version-script=link.T

ifeq ($(platform), unix)
   DYLIB = so
else ifeq ($(platform), osx)
   compiler := $(CC)
   DYLIB = dylib
   ldflags := -dynamiclib
   ARCHFLAGS=
   MINVERFLAGS=
   ifeq ($(shell uname -p),arm)
      MINVERFLAGS = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS  (Metal, ARM 64bit)
   else ifeq ($(HAVE_METAL),1)
      MINVERFLAGS = -mmacosx-version-min=10.13 -stdlib=libc++  # macOS  (Metal, x86 64bit)
   else ifeq ($(shell uname -p),powerpc)
      MINVERFLAGS = -mmacosx-version-min=10.5  # macOSX (PowerPC 32-bit)
   else ifeq ($(shell uname -m),i386)
      MINVERFLAGS = -mmacosx-version-min=10.6  # macOSX (OpenGL, x86 32bit)
   else
      MINVERFLAGS = -mmacosx-version-min=10.7 -stdlib=libc++ # macOSX (OpenGL, x86 64bit)
   endif

	# Build for a specific architecture when ARCH is defined as a switch
   ifeq ($(ARCH),arm64)
      MINVERFLAGS  = -mmacosx-version-min=10.15 -stdlib=libc++ # macOS  (Metal, ARM 64bit)
      ARCHFLAGS    = -arch arm64
   else ifeq ($(ARCH),x86_64)
      ifeq ($(HAVE_METAL),1)
         MINVERFLAGS  = -mmacosx-version-min=10.13 -stdlib=libc++
      else
         MINVERFLAGS  = -mmacosx-version-min=10.7  -stdlib=libc++
      endif
      ARCHFLAGS       = -arch x86_64
   else ifeq ($(ARCH),x86)
      MINVERFLAGS     = -mmacosx-version-min=10.6
      ARCHFLAGS       = -arch x86
   else ifeq ($(ARCH),ppc)
      MINVERFLAGS     = -mmacosx-version-min=10.5
      ARCHFLAGS       = -arch ppc
   endif
   ifeq ($(BUILDBOT),1)
      ARCHFLAGS       = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)
   endif
	extraflags += $(MINVERFLAGS) $(ARCHFLAGS)
	ldflags += $(MINVERFLAGS) $(ARCHFLAGS)
else
   extra_flags += -static-libgcc -static-libstdc++
   DYLIB = dll
endif

CC      := $(compiler) -Wall
CXX     := $(subst CC,++,$(compiler)) -std=gnu++0x -Wall
flags   := $(CPPFLAGS) $(CFLAGS) -fPIC $(extra_flags) -I../../include
asflags := $(ASFLAGS) -fPIC  $(extra_flags)
objects :=

ifeq (1,$(use_neon))
   ASMFLAGS := -INEON/asm
   asflags += -mfpu=neon
endif

plugs := $(wildcard *.c)
objects := $(plugs:.c=.o)
targets := $(objects:.o=.$(DYLIB))

all: build;

%.o: %.S
	$(CC) -c -o $@ $(asflags)  $(ASMFLAGS)  $<

%.o: %.c
	$(CC) -c -o $@ $(flags) $<

%.$(DYLIB): %.o
	$(CC) -o $@ $(ldflags) $(flags) $^

build: $(targets)

clean:
	rm -f *.o
	rm -f *.$(DYLIB)

strip:
	strip -s *.$(DYLIB)

install:
	mkdir -p $(DESTDIR)$(INSTALLDIR)
	cp -t $(DESTDIR)$(INSTALLDIR) $(targets) *.dsp

test-install:
	DESTDIR=/tmp/build $(MAKE) install


================================================
FILE: audio/dsp_filters/Mono.dsp
================================================
filters = 1
filter0 = panning

# Gains are linear.

# Stereo Mono:
 panning_left_mix = "0.5 0.5"
 panning_right_mix = "0.5 0.5"

# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"


================================================
FILE: audio/dsp_filters/Panning.dsp
================================================
filters = 1
filter0 = panning

# Gains are linear.

# The default. Left and right channels map to each other.
panning_left_mix = "1.0 0.0"
panning_right_mix = "0.0 1.0"

# Some examples:
#
# Mono:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.5 0.5"

# Swap left and right channels:
# panning_left_mix = "0.0 1.0"
# panning_right_mix = "1.0 0.0"
#
# Mono on one speaker:
# panning_left_mix = "0.5 0.5"
# panning_right_mix = "0.0 0.0"


================================================
FILE: audio/dsp_filters/Phaser.dsp
================================================
filters = 1
filter0 = phaser

# Defaults.
# phaser_lfo_freq = 0.4
# phaser_lfo_start_phase = 0.0
# phaser_feedback = 0.0
# phaser_depth = 0.4
# phaser_dry_wet = 0.5
# phaser_stages = 2


================================================
FILE: audio/dsp_filters/Reverb.dsp
================================================
filters = 1
filter0 = reverb

# Defaults.
# reverb_drytime = 0.43
# reverb_wettime = 0.4
# reverb_damping = 0.8
# reverb_roomwidth = 0.56
# reverb_roomsize = 0.56


================================================
FILE: audio/dsp_filters/Tremolo.dsp
================================================
filters = 1
filter0 = tremolo

# Defaults.
#tremolo_frequency = 4.0
#tremolo_depth = 0.9


================================================
FILE: audio/dsp_filters/Vibrato.dsp
================================================
filters = 1
filter0 = vibrato

# Defaults.
#vibrato_frequency = 5.0
#vibrato_depth = 0.5


================================================
FILE: audio/dsp_filters/WahWah.dsp
================================================
filters = 1
filter0 = wahwah

# Defaults.
# wahwah_lfo_freq = 1.5
# wahwah_lfo_start_phase = 0.0
# wahwah_freq_offset = 0.3
# wahwah_depth = 0.7
# wahwah_resonance = 2.5


================================================
FILE: audio/dsp_filters/chorus.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (chorus.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define CHORUS_MAX_DELAY 4096
#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1)

struct chorus_data
{
   float old[2][CHORUS_MAX_DELAY];
   float delay;
   float depth;
   float input_rate;
   float mix_dry;
   float mix_wet;
   unsigned old_ptr;
   unsigned lfo_ptr;
   unsigned lfo_period;
};

static void chorus_free(void *data)
{
   if (data)
      free(data);
}

static void chorus_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   float *out             = NULL;
   struct chorus_data *ch = (struct chorus_data*)data;

   output->samples        = input->samples;
   output->frames         = input->frames;
   out                    = output->samples;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      unsigned delay_int;
      float delay_frac, l_a, l_b, r_a, r_b;
      float chorus_l, chorus_r;
      float in[2]             = { out[0], out[1] };
      float delay             = ch->delay + ch->depth * sin((2.0 * M_PI * ch->lfo_ptr++) / ch->lfo_period);

      delay                  *= ch->input_rate;
      if (ch->lfo_ptr >= ch->lfo_period)
         ch->lfo_ptr          = 0;

      delay_int               = (unsigned)delay;

      if (delay_int >= CHORUS_MAX_DELAY - 1)
         delay_int            = CHORUS_MAX_DELAY - 2;

      delay_frac              = delay - delay_int;

      ch->old[0][ch->old_ptr] = in[0];
      ch->old[1][ch->old_ptr] = in[1];

      l_a                     = ch->old[0][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
      l_b                     = ch->old[0][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];
      r_a                     = ch->old[1][(ch->old_ptr - delay_int - 0) & CHORUS_DELAY_MASK];
      r_b                     = ch->old[1][(ch->old_ptr - delay_int - 1) & CHORUS_DELAY_MASK];

      /* Lerp introduces aliasing of the chorus component,
       * but doing full polyphase here is probably overkill. */
      chorus_l                = l_a * (1.0f - delay_frac) + l_b * delay_frac;
      chorus_r                = r_a * (1.0f - delay_frac) + r_b * delay_frac;

      out[0]                  = ch->mix_dry * in[0] + ch->mix_wet * chorus_l;
      out[1]                  = ch->mix_dry * in[1] + ch->mix_wet * chorus_r;

      ch->old_ptr             = (ch->old_ptr + 1) & CHORUS_DELAY_MASK;
   }
}

static void *chorus_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float delay, depth, lfo_freq, drywet;
   struct chorus_data *ch = (struct chorus_data*)calloc(1, sizeof(*ch));
   if (!ch)
      return NULL;

   config->get_float(userdata, "delay_ms", &delay, 25.0f);
   config->get_float(userdata, "depth_ms", &depth, 1.0f);
   config->get_float(userdata, "lfo_freq", &lfo_freq, 0.5f);
   config->get_float(userdata, "drywet", &drywet, 0.8f);

   delay            /= 1000.0f;
   depth            /= 1000.0f;

   if (depth > delay)
      depth          = delay;

   if (drywet < 0.0f)
      drywet         = 0.0f;
   else if (drywet > 1.0f)
      drywet         = 1.0f;

   ch->mix_dry       = 1.0f - 0.5f * drywet;
   ch->mix_wet       = 0.5f * drywet;

   ch->delay         = delay;
   ch->depth         = depth;
   ch->lfo_period    = (1.0f / lfo_freq) * info->input_rate;
   ch->input_rate    = info->input_rate;
   if (!ch->lfo_period)
      ch->lfo_period = 1;
   return ch;
}

static const struct dspfilter_implementation chorus_plug = {
   chorus_init,
   chorus_process,
   chorus_free,

   DSPFILTER_API_VERSION,
   "Chorus",
   "chorus",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation chorus_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask) { return &chorus_plug; }

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/configure
================================================
#!/bin/sh

PACKAGE_NAME=retroarch-filters-audio

================================================
FILE: audio/dsp_filters/crystalizer.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (echo.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

struct delta_data
{
   float intensity;
   float old[2];
};

static void delta_free(void *data)
{
   free(data);
}

static void delta_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i, c;
   struct delta_data *d   = (struct delta_data*)data;
   float *out             = output->samples;
   output->samples        = input->samples;
   output->frames         = input->frames;

   for (i = 0; i < input->frames; i++)
   {
      for (c = 0; c < 2; c++)
      {
           float current  = *out;
           *out++         = current + (current - d->old[c]) * d->intensity;
           d->old[c]      = current;
      }
   }
}

static void *delta_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   struct delta_data *d = (struct delta_data*)calloc(1, sizeof(*d));
   if (!d)
      return NULL;
   config->get_float(userdata, "intensity", &d->intensity, 5.0f);
   return d;
}

static const struct dspfilter_implementation delta_plug = {
   delta_init,
   delta_process,
   delta_free,
   DSPFILTER_API_VERSION,
   "Delta Sharpening",
   "crystalizer",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation delta_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &delta_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/echo.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (echo.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdlib.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

struct echo_channel
{
   float *buffer;
   unsigned ptr;
   unsigned frames;
   float feedback;
};

struct echo_data
{
   struct echo_channel *channels;
   unsigned num_channels;
   float amp;
};

static void echo_free(void *data)
{
   unsigned i;
   struct echo_data *echo = (struct echo_data*)data;

   for (i = 0; i < echo->num_channels; i++)
      free(echo->channels[i].buffer);
   free(echo->channels);
   free(echo);
}

static void echo_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i, c;
   float *out             = NULL;
   struct echo_data *echo = (struct echo_data*)data;

   output->samples        = input->samples;
   output->frames         = input->frames;

   out                    = output->samples;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float left, right;
      float echo_left  = 0.0f;
      float echo_right = 0.0f;

      for (c = 0; c < echo->num_channels; c++)
      {
         echo_left  += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0];
         echo_right += echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1];
      }

      echo_left     *= echo->amp;
      echo_right    *= echo->amp;

      left           = out[0] + echo_left;
      right          = out[1] + echo_right;

      for (c = 0; c < echo->num_channels; c++)
      {
         float feedback_left  = out[0] + echo->channels[c].feedback * echo_left;
         float feedback_right = out[1] + echo->channels[c].feedback * echo_right;

         echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 0] = feedback_left;
         echo->channels[c].buffer[(echo->channels[c].ptr << 1) + 1] = feedback_right;

         echo->channels[c].ptr = (echo->channels[c].ptr + 1) % echo->channels[c].frames;
      }

      out[0] = left;
      out[1] = right;
   }
}

static void *echo_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   unsigned i, channels;
   struct echo_channel *echo_channels    = NULL;
   float *delay                          = NULL;
   float *feedback                       = NULL;
   unsigned num_delay                    = 0;
   unsigned num_feedback                 = 0;

   static const float default_delay[]    = { 200.0f };
   static const float default_feedback[] = { 0.5f };
   struct echo_data *echo                = (struct echo_data*)
      calloc(1, sizeof(*echo));

   if (!echo)
      return NULL;

   config->get_float_array(userdata, "delay", &delay,
         &num_delay, default_delay, 1);
   config->get_float_array(userdata, "feedback", &feedback,
         &num_feedback, default_feedback, 1);
   config->get_float(userdata, "amp", &echo->amp, 0.2f);

   channels            = num_feedback = num_delay = MIN(num_delay, num_feedback);

   if (!(echo_channels = (struct echo_channel*)calloc(channels,
         sizeof(*echo_channels))))
      goto error;

   echo->channels      = echo_channels;
   echo->num_channels  = channels;

   for (i = 0; i < channels; i++)
   {
      unsigned frames  = (unsigned)(delay[i] * info->input_rate / 1000.0f + 0.5f);
      if (!frames)
         goto error;

      if (!(echo->channels[i].buffer = (float*)calloc(frames, 2 * sizeof(float))))
         goto error;

      echo->channels[i].frames   = frames;
      echo->channels[i].feedback = feedback[i];
   }

   config->free(delay);
   config->free(feedback);
   return echo;

error:
   config->free(delay);
   config->free(feedback);
   echo_free(echo);
   return NULL;
}

static const struct dspfilter_implementation echo_plug = {
   echo_init,
   echo_process,
   echo_free,

   DSPFILTER_API_VERSION,
   "Multi-Echo",
   "echo",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation echo_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &echo_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/eq.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (eq.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include <filters.h>
#include <libretro_dspfilter.h>

#include "fft/fft.c"

struct eq_data
{
   fft_t *fft;
   float *save;
   float *block;
   fft_complex_t *filter;
   fft_complex_t *fftblock;
   float buffer[8 * 1024];
   unsigned block_size;
   unsigned block_ptr;
};

struct eq_gain
{
   float freq;
   float gain; /* Linear. */
};

static void eq_free(void *data)
{
   struct eq_data *eq = (struct eq_data*)data;
   if (!eq)
      return;

   fft_free(eq->fft);
   free(eq->save);
   free(eq->block);
   free(eq->fftblock);
   free(eq->filter);
   free(eq);
}

static void eq_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   float *out;
   const float *in;
   unsigned input_frames;
   struct eq_data *eq = (struct eq_data*)data;

   output->samples    = eq->buffer;
   output->frames     = 0;

   out                = eq->buffer;
   in                 = input->samples;
   input_frames       = input->frames;

   while (input_frames)
   {
      unsigned write_avail = eq->block_size - eq->block_ptr;

      if (input_frames < write_avail)
         write_avail = input_frames;

      memcpy(eq->block + eq->block_ptr * 2, in, write_avail * 2 * sizeof(float));

      in            += write_avail * 2;
      input_frames  -= write_avail;
      eq->block_ptr += write_avail;

      /* Convolve a new block. */
      if (eq->block_ptr == eq->block_size)
      {
         unsigned i, c;

         for (c = 0; c < 2; c++)
         {
            fft_process_forward(eq->fft, eq->fftblock, eq->block + c, 2);
            for (i = 0; i < 2 * eq->block_size; i++)
               eq->fftblock[i] = fft_complex_mul(eq->fftblock[i], eq->filter[i]);
            fft_process_inverse(eq->fft, out + c, eq->fftblock, 2);
         }

         /* Overlap add method, so add in saved block now. */
         for (i = 0; i < 2 * eq->block_size; i++)
            out[i]      += eq->save[i];

         /* Save block for later. */
         memcpy(eq->save, out + 2 * eq->block_size, 2 * eq->block_size * sizeof(float));

         out            += eq->block_size * 2;
         output->frames += eq->block_size;
         eq->block_ptr   = 0;
      }
   }
}

static int gains_cmp(const void *a_, const void *b_)
{
   const struct eq_gain *a = (const struct eq_gain*)a_;
   const struct eq_gain *b = (const struct eq_gain*)b_;
   if (a->freq < b->freq)
      return -1;
   if (a->freq > b->freq)
      return 1;
   return 0;
}

static void generate_response(fft_complex_t *response,
      const struct eq_gain *gains, unsigned num_gains, unsigned samples)
{
   unsigned i;

   float start_freq = 0.0f;
   float start_gain = 1.0f;

   float end_freq   = 1.0f;
   float end_gain   = 1.0f;

   if (num_gains)
   {
      end_freq = gains->freq;
      end_gain = gains->gain;
      num_gains--;
      gains++;
   }

   /* Create a response by linear interpolation between
    * known frequency sample points. */
   for (i = 0; i <= samples; i++)
   {
      float gain;
      float lerp = 0.5f;
      float freq = (float)i / samples;

      while (freq >= end_freq)
      {
         if (num_gains)
         {
            start_freq = end_freq;
            start_gain = end_gain;
            end_freq = gains->freq;
            end_gain = gains->gain;

            gains++;
            num_gains--;
         }
         else
         {
            start_freq = end_freq;
            start_gain = end_gain;
            end_freq = 1.0f;
            end_gain = 1.0f;
            break;
         }
      }

      /* Edge case where i == samples. */
      if (end_freq > start_freq)
         lerp = (freq - start_freq) / (end_freq - start_freq);
      gain = (1.0f - lerp) * start_gain + lerp * end_gain;

      response[i].real               = gain;
      response[i].imag               = 0.0f;
      response[2 * samples - i].real = gain;
      response[2 * samples - i].imag = 0.0f;
   }
}

static void create_filter(struct eq_data *eq, unsigned size_log2,
      struct eq_gain *gains, unsigned num_gains, double beta, const char *filter_path)
{
   int i;
   int half_block_size = eq->block_size >> 1;
   double window_mod   = 1.0 / kaiser_window_function(0.0, beta);
   fft_t *fft          = fft_new(size_log2);
   float *time_filter  = (float*)calloc(eq->block_size * 2 + 1, sizeof(*time_filter));
   if (!fft || !time_filter)
      goto end;

   /* Make sure bands are in correct order. */
   qsort(gains, num_gains, sizeof(*gains), gains_cmp);

   /* Compute desired filter response. */
   generate_response(eq->filter, gains, num_gains, half_block_size);

   /* Get equivalent time-domain filter. */
   fft_process_inverse(fft, time_filter, eq->filter, 1);

   /* ifftshift() to create the correct linear phase filter.
    * The filter response was designed with zero phase, which
    * won't work unless we compensate
    * for the repeating property of the FFT here
    * by flipping left and right blocks. */
   for (i = 0; i < half_block_size; i++)
   {
      float tmp = time_filter[i + half_block_size];
      time_filter[i + half_block_size] = time_filter[i];
      time_filter[i] = tmp;
   }

   /* Apply a window to smooth out the frequency response. */
   for (i = 0; i < (int)eq->block_size; i++)
   {
      /* Kaiser window. */
      double phase    = (double)i / eq->block_size;
      phase           = 2.0 * (phase - 0.5);
      time_filter[i] *= window_mod * kaiser_window_function(phase, beta);
   }

#ifdef DEBUG
   /* Debugging. */
   if (filter_path)
   {
      FILE *file = fopen(filter_path, "w");
      if (file)
      {
         for (i = 0; i < (int)eq->block_size - 1; i++)
            fprintf(file, "%.8f\n", time_filter[i + 1]);
         fclose(file);
      }
   }
#endif

   /* Padded FFT to create our FFT filter.
    * Make our even-length filter odd by discarding the first coefficient.
    * For some interesting reason, this allows us to design an odd-length linear phase filter.
    */
   fft_process_forward(eq->fft, eq->filter, time_filter + 1, 1);

end:
   fft_free(fft);
   free(time_filter);
}

static void *eq_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   int size_log2;
   float beta;
   float *frequencies, *gain;
   unsigned num_freq, num_gain, i, size;
   struct eq_gain *gains      = NULL;
   char *filter_path          = NULL;
   const float default_freq[] = { 0.0f, info->input_rate };
   const float default_gain[] = { 0.0f, 0.0f };
   struct eq_data *eq         = (struct eq_data*)calloc(1, sizeof(*eq));
   if (!eq)
      return NULL;

   config->get_float(userdata, "window_beta", &beta, 4.0f);

   config->get_int(userdata, "block_size_log2", &size_log2, 8);
   size = 1 << size_log2;

   config->get_float_array(userdata, "frequencies", &frequencies, &num_freq, default_freq, 2);
   config->get_float_array(userdata, "gains", &gain, &num_gain, default_gain, 2);

   if (!config->get_string(userdata, "impulse_response_output", &filter_path, ""))
   {
      config->free(filter_path);
      filter_path = NULL;
   }

   num_gain = num_freq = MIN(num_gain, num_freq);

   if (!(gains = (struct eq_gain*)calloc(num_gain, sizeof(*gains))))
      goto error;

   for (i = 0; i < num_gain; i++)
   {
      gains[i].freq = frequencies[i] / (0.5f * info->input_rate);
      gains[i].gain = pow(10.0, gain[i] / 20.0);
   }
   config->free(frequencies);
   config->free(gain);

   eq->block_size = size;

   eq->save       = (float*)calloc(    size, 2 * sizeof(*eq->save));
   eq->block      = (float*)calloc(2 * size, 2 * sizeof(*eq->block));
   eq->fftblock   = (fft_complex_t*)calloc(2 * size, sizeof(*eq->fftblock));
   eq->filter     = (fft_complex_t*)calloc(2 * size, sizeof(*eq->filter));

   /* Use an FFT which is twice the block size with zero-padding
    * to make circular convolution => proper convolution.
    */
   eq->fft        = fft_new(size_log2 + 1);

   if (!eq->fft || !eq->fftblock || !eq->save || !eq->block || !eq->filter)
      goto error;

   create_filter(eq, size_log2, gains, num_gain, beta, filter_path);
   config->free(filter_path);
   filter_path = NULL;

   free(gains);
   return eq;

error:
   free(gains);
   eq_free(eq);
   return NULL;
}

static const struct dspfilter_implementation eq_plug = {
   eq_init,
   eq_process,
   eq_free,

   DSPFILTER_API_VERSION,
   "Linear-Phase FFT Equalizer",
   "eq",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation eq_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &eq_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/fft/fft.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (fft.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>

#include "fft.h"

#include <retro_miscellaneous.h>

struct fft
{
   fft_complex_t *interleave_buffer;
   fft_complex_t *phase_lut;
   unsigned *bitinverse_buffer;
   unsigned size;
};

static unsigned bitswap(unsigned x, unsigned size_log2)
{
   unsigned i;
   unsigned ret = 0;
   for (i = 0; i < size_log2; i++)
      ret |= ((x >> i) & 1) << (size_log2 - i - 1);
   return ret;
}

static void build_bitinverse(unsigned *bitinverse, unsigned size_log2)
{
   unsigned i;
   unsigned size = 1 << size_log2;
   for (i = 0; i < size; i++)
      bitinverse[i] = bitswap(i, size_log2);
}

static fft_complex_t exp_imag(double phase)
{
   fft_complex_t out = { cos(phase), sin(phase) };
   return out;
}

static void build_phase_lut(fft_complex_t *out, int size)
{
   int i;
   out += size;
   for (i = -size; i <= size; i++)
      out[i] = exp_imag((M_PI * i) / size);
}

static void interleave_complex(const unsigned *bitinverse,
      fft_complex_t *out, const fft_complex_t *in,
      unsigned samples, unsigned step)
{
   unsigned i;
   for (i = 0; i < samples; i++, in += step)
      out[bitinverse[i]] = *in;
}

static void interleave_float(const unsigned *bitinverse,
      fft_complex_t *out, const float *in,
      unsigned samples, unsigned step)
{
   unsigned i;
   for (i = 0; i < samples; i++, in += step)
   {
      unsigned inv_i = bitinverse[i];
      out[inv_i].real = *in;
      out[inv_i].imag = 0.0f;
   }
}

static void resolve_float(float *out, const fft_complex_t *in, unsigned samples,
      float gain, unsigned step)
{
   unsigned i;
   for (i = 0; i < samples; i++, in++, out += step)
      *out = gain * in->real;
}

fft_t *fft_new(unsigned block_size_log2)
{
   unsigned size;
   fft_t *fft = (fft_t*)calloc(1, sizeof(*fft));
   if (!fft)
      return NULL;

   size                   = 1 << block_size_log2;
   fft->interleave_buffer = (fft_complex_t*)calloc(size, sizeof(*fft->interleave_buffer));
   fft->bitinverse_buffer = (unsigned*)calloc(size, sizeof(*fft->bitinverse_buffer));
   fft->phase_lut         = (fft_complex_t*)calloc(2 * size + 1, sizeof(*fft->phase_lut));

   if (!fft->interleave_buffer || !fft->bitinverse_buffer || !fft->phase_lut)
      goto error;

   fft->size = size;

   build_bitinverse(fft->bitinverse_buffer, block_size_log2);
   build_phase_lut(fft->phase_lut, size);
   return fft;

error:
   fft_free(fft);
   return NULL;
}

void fft_free(fft_t *fft)
{
   if (!fft)
      return;

   free(fft->interleave_buffer);
   free(fft->bitinverse_buffer);
   free(fft->phase_lut);
   free(fft);
}

static void butterfly(fft_complex_t *a, fft_complex_t *b, fft_complex_t mod)
{
   mod = fft_complex_mul(mod, *b);
   *b  = fft_complex_sub(*a, mod);
   *a  = fft_complex_add(*a, mod);
}

static void butterflies(fft_complex_t *butterfly_buf,
      const fft_complex_t *phase_lut,
      int phase_dir, unsigned step_size, unsigned samples)
{
   unsigned i, j;
   for (i = 0; i < samples; i += step_size << 1)
   {
      int phase_step = (int)samples * phase_dir / (int)step_size;
      for (j = i; j < i + step_size; j++)
         butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size],
               phase_lut[phase_step * (int)(j - i)]);
   }
}

void fft_process_forward_complex(fft_t *fft,
      fft_complex_t *out, const fft_complex_t *in, unsigned step)
{
   unsigned step_size;
   unsigned samples = fft->size;
   interleave_complex(fft->bitinverse_buffer, out, in, samples, step);

   for (step_size = 1; step_size < samples; step_size <<= 1)
   {
      butterflies(out,
            fft->phase_lut + samples,
            -1, step_size, samples);
   }
}

void fft_process_forward(fft_t *fft,
      fft_complex_t *out, const float *in, unsigned step)
{
   unsigned step_size;
   unsigned samples = fft->size;
   interleave_float(fft->bitinverse_buffer, out, in, samples, step);

   for (step_size = 1; step_size < fft->size; step_size <<= 1)
   {
      butterflies(out,
            fft->phase_lut + samples,
            -1, step_size, samples);
   }
}

void fft_process_inverse(fft_t *fft,
      float *out, const fft_complex_t *in, unsigned step)
{
   unsigned step_size;
   unsigned samples = fft->size;

   interleave_complex(fft->bitinverse_buffer, fft->interleave_buffer,
         in, samples, 1);

   for (step_size = 1; step_size < samples; step_size <<= 1)
   {
      butterflies(fft->interleave_buffer,
            fft->phase_lut + samples,
            1, step_size, samples);
   }

   resolve_float(out, fft->interleave_buffer, samples, 1.0f / samples, step);
}


================================================
FILE: audio/dsp_filters/fft/fft.h
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (fft.h).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef RARCH_FFT_H__
#define RARCH_FFT_H__

#include <retro_inline.h>
#include <math/complex.h>

typedef struct fft fft_t;

fft_t *fft_new(unsigned block_size_log2);

void fft_free(fft_t *fft);

void fft_process_forward_complex(fft_t *fft,
      fft_complex_t *out, const fft_complex_t *in, unsigned step);

void fft_process_forward(fft_t *fft,
      fft_complex_t *out, const float *in, unsigned step);

void fft_process_inverse(fft_t *fft,
      float *out, const fft_complex_t *in, unsigned step);

#endif


================================================
FILE: audio/dsp_filters/iir.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (iir.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define sqr(a) ((a) * (a))

/* filter types */
enum IIRFilter
{
   LPF,        /* low pass filter */
   HPF,        /* High pass filter */
   BPCSGF,     /* band pass filter 1 */
   BPZPGF,     /* band pass filter 2 */
   APF,        /* Allpass filter*/
   NOTCH,      /* Notch Filter */
   RIAA_phono, /* RIAA record/tape deemphasis */
   PEQ,        /* Peaking band EQ filter */
   BBOOST,     /* Bassboost filter */
   LSH,        /* Low shelf filter */
   HSH,        /* High shelf filter */
   RIAA_CD     /* CD de-emphasis */
};

struct iir_data
{
   float b0, b1, b2;
   float a0, a1, a2;

   struct
   {
      float xn1, xn2;
      float yn1, yn2;
   } l, r;
};

static void iir_free(void *data)
{
   free(data);
}

static void iir_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   struct iir_data *iir = (struct iir_data*)data;
   float *out           = output->samples;

   float b0             = iir->b0;
   float b1             = iir->b1;
   float b2             = iir->b2;
   float a0             = iir->a0;
   float a1             = iir->a1;
   float a2             = iir->a2;

   float xn1_l          = iir->l.xn1;
   float xn2_l          = iir->l.xn2;
   float yn1_l          = iir->l.yn1;
   float yn2_l          = iir->l.yn2;

   float xn1_r          = iir->r.xn1;
   float xn2_r          = iir->r.xn2;
   float yn1_r          = iir->r.yn1;
   float yn2_r          = iir->r.yn2;

   output->samples      = input->samples;
   output->frames       = input->frames;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float in_l = out[0];
      float in_r = out[1];

      float l    = (b0 * in_l + b1 * xn1_l + b2 * xn2_l - a1 * yn1_l - a2 * yn2_l) / a0;
      float r    = (b0 * in_r + b1 * xn1_r + b2 * xn2_r - a1 * yn1_r - a2 * yn2_r) / a0;

      xn2_l      = xn1_l;
      xn1_l      = in_l;
      yn2_l      = yn1_l;
      yn1_l      = l;

      xn2_r      = xn1_r;
      xn1_r      = in_r;
      yn2_r      = yn1_r;
      yn1_r      = r;

      out[0]     = l;
      out[1]     = r;
   }

   iir->l.xn1 = xn1_l;
   iir->l.xn2 = xn2_l;
   iir->l.yn1 = yn1_l;
   iir->l.yn2 = yn2_l;

   iir->r.xn1 = xn1_r;
   iir->r.xn2 = xn2_r;
   iir->r.yn1 = yn1_r;
   iir->r.yn2 = yn2_r;
}

#define CHECK(x) if (strcmp(str, #x) == 0) return x
static enum IIRFilter str_to_type(const char *str)
{
   CHECK(LPF);
   CHECK(HPF);
   CHECK(BPCSGF);
   CHECK(BPZPGF);
   CHECK(APF);
   CHECK(NOTCH);
   CHECK(RIAA_phono);
   CHECK(PEQ);
   CHECK(BBOOST);
   CHECK(LSH);
   CHECK(HSH);
   CHECK(RIAA_CD);

   return LPF; /* Fallback. */
}
#undef CHECK

static void make_poly_from_roots(
      const double *roots, unsigned num_roots, float *poly)
{
   unsigned i, j;

   poly[0] = 1;
   poly[1] = -roots[0];
   memset(poly + 2, 0, (num_roots + 1 - 2) * sizeof(*poly));

   for (i = 1; i < num_roots; i++)
      for (j = num_roots; j > 0; j--)
         poly[j] -= poly[j - 1] * roots[i];
}

static void iir_filter_init(struct iir_data *iir,
      float sample_rate, float freq, float qual, float gain, enum IIRFilter filter_type)
{
	double omega = 2.0 * M_PI * freq / sample_rate;
   double cs    = cos(omega);
   double sn    = sin(omega);
   double a1pha = sn / (2.0 * qual);
   double A     = exp(log(10.0) * gain / 40.0);
   double beta  = sqrt(A + A);

   float b0     = 0.0, b1 = 0.0, b2 = 0.0, a0 = 0.0, a1 = 0.0, a2 = 0.0;

   /* Set up filter coefficients according to type */
   switch (filter_type)
   {
      case LPF:
         b0 =  (1.0 - cs) / 2.0;
         b1 =   1.0 - cs ;
         b2 =  (1.0 - cs) / 2.0;
         a0 =   1.0 + a1pha;
         a1 =  -2.0 * cs;
         a2 =   1.0 - a1pha;
         break;
      case HPF:
         b0 =  (1.0 + cs) / 2.0;
         b1 = -(1.0 + cs);
         b2 =  (1.0 + cs) / 2.0;
         a0 =   1.0 + a1pha;
         a1 =  -2.0 * cs;
         a2 =   1.0 - a1pha;
         break;
      case APF:
         b0 =  1.0 - a1pha;
         b1 = -2.0 * cs;
         b2 =  1.0 + a1pha;
         a0 =  1.0 + a1pha;
         a1 = -2.0 * cs;
         a2 =  1.0 - a1pha;
         break;
      case BPZPGF:
         b0 =  a1pha;
         b1 =  0.0;
         b2 = -a1pha;
         a0 =  1.0 + a1pha;
         a1 = -2.0 * cs;
         a2 =  1.0 - a1pha;
         break;
      case BPCSGF:
         b0 =  sn / 2.0;
         b1 =  0.0;
         b2 = -sn / 2.0;
         a0 =  1.0 + a1pha;
         a1 = -2.0 * cs;
         a2 =  1.0 - a1pha;
         break;
      case NOTCH:
         b0 =  1.0;
         b1 = -2.0 * cs;
         b2 =  1.0;
         a0 =  1.0 + a1pha;
         a1 = -2.0 * cs;
         a2 =  1.0 - a1pha;
         break;
      case RIAA_phono: /* http://www.dsprelated.com/showmessage/73300/3.php */
      {
         double y, b_re, a_re, b_im, a_im, g;
         float b[3] = {0.0f};
         float a[3] = {0.0f};

         if ((int)sample_rate == 44100)
         {
            static const double zeros[] = {-0.2014898, 0.9233820};
            static const double poles[] = {0.7083149, 0.9924091};
            make_poly_from_roots(zeros, 2, b);
            make_poly_from_roots(poles, 2, a);
         }
         else if ((int)sample_rate == 48000)
         {
            static const double zeros[] = {-0.1766069, 0.9321590};
            static const double poles[] = {0.7396325, 0.9931330};
            make_poly_from_roots(zeros, 2, b);
            make_poly_from_roots(poles, 2, a);
         }
         else if ((int)sample_rate == 88200)
         {
            static const double zeros[] = {-0.1168735, 0.9648312};
            static const double poles[] = {0.8590646, 0.9964002};
            make_poly_from_roots(zeros, 2, b);
            make_poly_from_roots(poles, 2, a);
         }
         else if ((int)sample_rate == 96000)
         {
            static const double zeros[] = {-0.1141486, 0.9676817};
            static const double poles[] = {0.8699137, 0.9966946};
            make_poly_from_roots(zeros, 2, b);
            make_poly_from_roots(poles, 2, a);
         }

         b0    = b[0];
         b1    = b[1];
         b2    = b[2];
         a0    = a[0];
         a1    = a[1];
         a2    = a[2];

         /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
         y     = 2.0 * M_PI * 1000.0 / sample_rate;
         b_re  = b0 + b1 * cos(-y) + b2 * cos(-2.0 * y);
         a_re  = a0 + a1 * cos(-y) + a2 * cos(-2.0 * y);
         b_im  = b1 * sin(-y) + b2 * sin(-2.0 * y);
         a_im  = a1 * sin(-y) + a2 * sin(-2.0 * y);
         g     = 1.0 / sqrt((sqr(b_re) + sqr(b_im)) / (sqr(a_re) + sqr(a_im)));
         b0   *= g; b1 *= g; b2 *= g;
         break;
      }
      case PEQ:
         b0 =  1.0 + a1pha * A;
         b1 = -2.0 * cs;
         b2 =  1.0 - a1pha * A;
         a0 =  1.0 + a1pha / A;
         a1 = -2.0 * cs;
         a2 =  1.0 - a1pha / A;
         break;
      case BBOOST:
         beta = sqrt((A * A + 1) / 1.0 - (pow((A - 1), 2)));
         b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
         b1 = 2 * A * ((A - 1) - (A + 1) * cs);
         b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
         a0 = ((A + 1) + (A - 1) * cs + beta * sn);
         a1 = -2 * ((A - 1) + (A + 1) * cs);
         a2 = (A + 1) + (A - 1) * cs - beta * sn;
         break;
      case LSH:
         b0 = A * ((A + 1) - (A - 1) * cs + beta * sn);
         b1 = 2 * A * ((A - 1) - (A + 1) * cs);
         b2 = A * ((A + 1) - (A - 1) * cs - beta * sn);
         a0 = (A + 1) + (A - 1) * cs + beta * sn;
         a1 = -2 * ((A - 1) + (A + 1) * cs);
         a2 = (A + 1) + (A - 1) * cs - beta * sn;
         break;
      case RIAA_CD:
         omega = 2.0 * M_PI * 5283.0 / sample_rate;
         cs = cos(omega);
         sn = sin(omega);
         a1pha = sn / (2.0 * 0.4845);
         A = exp(log(10.0) * -9.477 / 40.0);
         beta = sqrt(A + A);
         (void)a1pha;
      case HSH:
         b0 = A * ((A + 1.0) + (A - 1.0) * cs + beta * sn);
         b1 = -2.0 * A * ((A - 1.0) + (A + 1.0) * cs);
         b2 = A * ((A + 1.0) + (A - 1.0) * cs - beta * sn);
         a0 = (A + 1.0) - (A - 1.0) * cs + beta * sn;
         a1 = 2.0 * ((A - 1.0) - (A + 1.0) * cs);
         a2 = (A + 1.0) - (A - 1.0) * cs - beta * sn;
         break;
      default:
         break;
   }

   iir->b0 = b0;
   iir->b1 = b1;
   iir->b2 = b2;
   iir->a0 = a0;
   iir->a1 = a1;
   iir->a2 = a2;
}

static void *iir_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float freq, qual, gain;
   enum IIRFilter filter  = LPF;
   char           *type   = NULL;
   struct iir_data *iir   = (struct iir_data*)calloc(1, sizeof(*iir));
   if (!iir)
      return NULL;

   config->get_float(userdata, "frequency", &freq, 1024.0f);
   config->get_float(userdata, "quality", &qual, 0.707f);
   config->get_float(userdata, "gain", &gain, 0.0f);

   config->get_string(userdata, "type", &type, "LPF");

   filter = str_to_type(type);
   config->free(type);

   iir_filter_init(iir, info->input_rate, freq, qual, gain, filter);
   return iir;
}

static const struct dspfilter_implementation iir_plug = {
   iir_init,
   iir_process,
   iir_free,

   DSPFILTER_API_VERSION,
   "IIR",
   "iir",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation iir_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &iir_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/link.T
================================================
{
  global: dspfilter_get_implementation;
  local: *;
};


================================================
FILE: audio/dsp_filters/panning.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (panning.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <libretro_dspfilter.h>

struct panning_data
{
   float left[2];
   float right[2];
};

static void panning_free(void *data)
{
   free(data);
}

static void panning_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   struct panning_data *pan = (struct panning_data*)data;
   float *out               = output->samples;

   output->samples          = input->samples;
   output->frames           = input->frames;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float left  = out[0];
      float right = out[1];
      out[0]      = left * pan->left[0]  + right * pan->left[1];
      out[1]      = left * pan->right[0] + right * pan->right[1];
   }
}

static void *panning_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   static const float default_left[]  = { 1.0f, 0.0f };
   static const float default_right[] = { 0.0f, 1.0f };
   float *left                        = NULL;
   float *right                       = NULL;
   unsigned num_left                  = 0;
   unsigned num_right                 = 0;
   struct panning_data *pan           = (struct panning_data*)
      calloc(1, sizeof(*pan));

   if (!pan)
      return NULL;

   config->get_float_array(userdata, "left_mix",
         &left, &num_left, default_left, 2);
   config->get_float_array(userdata, "right_mix",
         &right, &num_right, default_right, 2);

   memcpy(pan->left,  (num_left  == 2) ?
         left :  default_left,  sizeof(pan->left));
   memcpy(pan->right, (num_right == 2) ?
         right : default_right, sizeof(pan->right));

   config->free(left);
   config->free(right);

   return pan;
}

static const struct dspfilter_implementation panning = {
   panning_init,
   panning_process,
   panning_free,

   DSPFILTER_API_VERSION,
   "Panning",
   "panning",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation panning_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &panning;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/phaser.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (phaser.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define PHASER_LFO_SHAPE 4.0
#define PHASER_LFO_SKIP_SAMPLES 20

struct phaser_data
{
   float freq;
   float startphase;
   float fb;
   float depth;
   float drywet;
   float old[2][24];
   float gain;
   float fbout[2];
   float lfoskip;
   float phase;

   int stages;
   unsigned long skipcount;
};

static void phaser_free(void *data)
{
   free(data);
}

static void phaser_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i, c;
   int s;
   float m[2], tmp[2];
   struct phaser_data *ph = (struct phaser_data*)data;
   float *out             = output->samples;

   output->samples        = input->samples;
   output->frames         = input->frames;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float in[2] = { out[0], out[1] };

      for (c = 0; c < 2; c++)
         m[c] = in[c] + ph->fbout[c] * ph->fb * 0.01f;

      if ((ph->skipcount++ % PHASER_LFO_SKIP_SAMPLES) == 0)
      {
         ph->gain = 0.5 * (1.0 + cos(ph->skipcount * ph->lfoskip + ph->phase));
         ph->gain = (exp(ph->gain * PHASER_LFO_SHAPE) - 1.0) / (exp(PHASER_LFO_SHAPE) - 1);
         ph->gain = 1.0 - ph->gain * ph->depth;
      }

      for (s = 0; s < ph->stages; s++)
      {
         for (c = 0; c < 2; c++)
         {
            tmp[c] = ph->old[c][s];
            ph->old[c][s] = ph->gain * tmp[c] + m[c];
            m[c] = tmp[c] - ph->gain * ph->old[c][s];
         }
      }

      for (c = 0; c < 2; c++)
      {
         ph->fbout[c] = m[c];
         out[c] = m[c] * ph->drywet + in[c] * (1.0f - ph->drywet);
      }
   }
}

static void *phaser_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float lfo_freq, lfo_start_phase;
   struct phaser_data *ph = (struct phaser_data*)calloc(1, sizeof(*ph));
   if (!ph)
      return NULL;

   config->get_float(userdata, "lfo_freq", &lfo_freq, 0.4f);
   config->get_float(userdata, "lfo_start_phase", &lfo_start_phase, 0.0f);
   config->get_float(userdata, "feedback", &ph->fb, 0.0f);
   config->get_float(userdata, "depth", &ph->depth, 0.4f);
   config->get_float(userdata, "dry_wet", &ph->drywet, 0.5f);
   config->get_int(userdata, "stages", &ph->stages, 2);

   if (ph->stages < 1)
      ph->stages = 1;
   else if (ph->stages > 24)
      ph->stages = 24;

   ph->lfoskip = lfo_freq * 2.0 * M_PI / info->input_rate;
   ph->phase   = lfo_start_phase * M_PI / 180.0;

   return ph;
}

static const struct dspfilter_implementation phaser_plug = {
   phaser_init,
   phaser_process,
   phaser_free,

   DSPFILTER_API_VERSION,
   "Phaser",
   "phaser",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation phaser_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &phaser_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/reverb.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (reverb.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_inline.h>
#include <libretro_dspfilter.h>

struct comb
{
   float *buffer;
   unsigned bufsize;
   unsigned bufidx;

   float feedback;
   float filterstore;
   float damp1, damp2;
};

struct allpass
{
   float *buffer;
   float feedback;
   unsigned bufsize;
   unsigned bufidx;
};

static INLINE float comb_process(struct comb *c, float input)
{
   float output         = c->buffer[c->bufidx];
   c->filterstore       = (output * c->damp2) + (c->filterstore * c->damp1);

   c->buffer[c->bufidx] = input + (c->filterstore * c->feedback);

   c->bufidx++;
   if (c->bufidx >= c->bufsize)
      c->bufidx = 0;

   return output;
}

static INLINE float allpass_process(struct allpass *a, float input)
{
   float bufout         = a->buffer[a->bufidx];
   float output         = -input + bufout;
   a->buffer[a->bufidx] = input + bufout * a->feedback;

   a->bufidx++;
   if (a->bufidx >= a->bufsize)
      a->bufidx = 0;

   return output;
}

#define numcombs 8
#define numallpasses 4
static const float muted = 0;
static const float fixedgain = 0.015f;
static const float scalewet = 3;
static const float scaledry = 2;
static const float scaledamp = 0.4f;
static const float scaleroom = 0.28f;
static const float offsetroom = 0.7f;
static const float initialroom = 0.5f;
static const float initialdamp = 0.5f;
static const float initialwet = 1.0f / 3.0f;
static const float initialdry = 0;
static const float initialwidth = 1;
static const float initialmode = 0;
static const float freezemode = 0.5f;

struct revmodel
{
   struct comb combL[numcombs];
   struct allpass allpassL[numallpasses];

   float *bufcomb[numcombs];
   float *bufallpass[numallpasses];

   float gain;
   float roomsize, roomsize1;
   float damp, damp1;
   float wet, wet1, wet2;
   float dry;
   float width;
   float mode;
};

static float revmodel_process(struct revmodel *rev, float in)
{
   unsigned i;
   float mono_out = 0.0f;
   float mono_in  = in;
   float input    = mono_in * rev->gain;

   for (i = 0; i < numcombs; i++)
      mono_out += comb_process(&rev->combL[i], input);

   for (i = 0; i < numallpasses; i++)
      mono_out = allpass_process(&rev->allpassL[i], mono_out);

   return mono_in * rev->dry + mono_out * rev->wet1;
}

static void revmodel_update(struct revmodel *rev)
{
   unsigned i;
   rev->wet1 = rev->wet * (rev->width / 2.0f + 0.5f);

   if (rev->mode >= freezemode)
   {
      rev->roomsize1 = 1.0f;
      rev->damp1 = 0.0f;
      rev->gain = muted;
   }
   else
   {
      rev->roomsize1 = rev->roomsize;
      rev->damp1 = rev->damp;
      rev->gain = fixedgain;
   }

   for (i = 0; i < numcombs; i++)
   {
      rev->combL[i].feedback = rev->roomsize1;
      rev->combL[i].damp1 = rev->damp1;
      rev->combL[i].damp2 = 1.0f - rev->damp1;
   }
}

static void revmodel_setroomsize(struct revmodel *rev, float value)
{
   rev->roomsize = value * scaleroom + offsetroom;
   revmodel_update(rev);
}

static void revmodel_setdamp(struct revmodel *rev, float value)
{
   rev->damp = value * scaledamp;
   revmodel_update(rev);
}

static void revmodel_setwet(struct revmodel *rev, float value)
{
   rev->wet = value * scalewet;
   revmodel_update(rev);
}

static void revmodel_setdry(struct revmodel *rev, float value)
{
   rev->dry = value * scaledry;
   revmodel_update(rev);
}

static void revmodel_setwidth(struct revmodel *rev, float value)
{
   rev->width = value;
   revmodel_update(rev);
}

static void revmodel_setmode(struct revmodel *rev, float value)
{
   rev->mode = value;
   revmodel_update(rev);
}

static void revmodel_init(struct revmodel *rev,int srate)
{
   unsigned c;
   static const int comb_lengths[8]    = { 1116,1188,1277,1356,1422,1491,1557,1617 };
   static const int allpass_lengths[4] = { 225,341,441,556 };
   double r = srate * (1 / 44100.0);

   for (c = 0; c < numcombs; ++c)
   {
      unsigned bufsize         = (unsigned)(r * comb_lengths[c]);
      rev->bufcomb[c]          = (float*)calloc(bufsize, sizeof(float));
      rev->combL[c].buffer     = rev->bufcomb[c];
      rev->combL[c].bufsize    = bufsize;
   }

   for (c = 0; c < numallpasses; ++c)
   {
      unsigned bufsize          = (unsigned)(r * allpass_lengths[c]);
      rev->bufallpass[c]        = (float*)calloc(bufsize, sizeof(float));
      rev->allpassL[c].buffer   = rev->bufallpass[c];
      rev->allpassL[c].bufsize  = bufsize;
      rev->allpassL[c].feedback = 0.5f;
   }

   revmodel_setwet(rev, initialwet);
   revmodel_setroomsize(rev, initialroom);
   revmodel_setdry(rev, initialdry);
   revmodel_setdamp(rev, initialdamp);
   revmodel_setwidth(rev, initialwidth);
   revmodel_setmode(rev, initialmode);
}

struct reverb_data
{
   struct revmodel left, right;
};

static void reverb_free(void *data)
{
   unsigned i;
   struct reverb_data *rev = (struct reverb_data*)data;

   for (i = 0; i < numcombs; i++)
   {
      free(rev->left.bufcomb[i]);
      free(rev->right.bufcomb[i]);
   }

   for (i = 0; i < numallpasses; i++)
   {
      free(rev->left.bufallpass[i]);
      free(rev->right.bufallpass[i]);
   }
   free(data);
}

static void reverb_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   float *out;
   struct reverb_data *rev = (struct reverb_data*)data;

   output->samples         = input->samples;
   output->frames          = input->frames;
   out                     = output->samples;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float in[2] = { out[0], out[1] };

      out[0] = revmodel_process(&rev->left, in[0]);
      out[1] = revmodel_process(&rev->right, in[1]);
   }
}

static void *reverb_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float drytime, wettime, damping, roomwidth, roomsize;
   struct reverb_data *rev = (struct reverb_data*)
      calloc(1, sizeof(*rev));
   if (!rev)
      return NULL;

   config->get_float(userdata, "drytime", &drytime, 0.43f);
   config->get_float(userdata, "wettime", &wettime, 0.4f);
   config->get_float(userdata, "damping", &damping, 0.8f);
   config->get_float(userdata, "roomwidth", &roomwidth, 0.56f);
   config->get_float(userdata, "roomsize", &roomsize, 0.56f);

   revmodel_init(&rev->left,info->input_rate);
   revmodel_init(&rev->right,info->input_rate);

   revmodel_setdamp(&rev->left, damping);
   revmodel_setdry(&rev->left, drytime);
   revmodel_setwet(&rev->left, wettime);
   revmodel_setwidth(&rev->left, roomwidth);
   revmodel_setroomsize(&rev->left, roomsize);

   revmodel_setdamp(&rev->right, damping);
   revmodel_setdry(&rev->right, drytime);
   revmodel_setwet(&rev->right, wettime);
   revmodel_setwidth(&rev->right, roomwidth);
   revmodel_setroomsize(&rev->right, roomsize);

   return rev;
}

static const struct dspfilter_implementation reverb_plug = {
   reverb_init,
   reverb_process,
   reverb_free,

   DSPFILTER_API_VERSION,
   "Reverb",
   "reverb",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation reverb_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &reverb_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/tremolo.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (tremolo.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define sqr(a) ((a) * (a))

struct tremolo_core
{
   float *wavetable;
   float freq;
   float depth;
   unsigned index;
   unsigned maxindex;
};

struct tremolo
{
   struct tremolo_core left, right;
};

static void tremolo_free(void *data)
{
   struct tremolo *tre = (struct tremolo*)data;
   free(tre->left.wavetable);
   free(tre->right.wavetable);
   free(data);
}

static void tremolocore_init(struct tremolo_core *core,float depth,int samplerate,float freq)
{
   double env;
   unsigned i;
   const double offset = 1. - depth / 2.;
   core->index     = 0;
   core->maxindex  = samplerate / freq;
   core->wavetable = (float*)malloc(core->maxindex   * sizeof(float));
   memset(core->wavetable, 0, core->maxindex * sizeof(float));
   for (i = 0; i < core->maxindex; i++)
   {
      env                = freq * i / samplerate;
      env                = sin((M_PI*2) * fmod(env + 0.25, 1.0));
      core->wavetable[i] = env * (1 - fabs(offset)) + offset;
   }
}

float tremolocore_core(struct tremolo_core *core,float in)
{
   core->index = core->index % core->maxindex;
   return in * core->wavetable[core->index++];
}

static void tremolo_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   float *out;
   struct tremolo *tre = (struct tremolo*)data;

   output->samples     = input->samples;
   output->frames      = input->frames;
   out                 = output->samples;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float in[2]      = { out[0], out[1] };
      out[0]           = tremolocore_core(&tre->left, in[0]);
      out[1]           = tremolocore_core(&tre->right, in[1]);
   }
}

static void *tremolo_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float freq, depth;
   struct tremolo *tre = (struct tremolo*)calloc(1, sizeof(*tre));
   if (!tre)
      return NULL;

   config->get_float(userdata, "freq", &freq,4.0f);
   config->get_float(userdata, "depth", &depth, 0.9f);
   tremolocore_init(&tre->left,depth,info->input_rate,freq);
   tremolocore_init(&tre->right,depth,info->input_rate,freq);
   return tre;
}

static const struct dspfilter_implementation tremolo_plug = {
   tremolo_init,
   tremolo_process,
   tremolo_free,

   DSPFILTER_API_VERSION,
   "Tremolo",
   "tremolo",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation tremolo_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &tremolo_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/vibrato.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (vibrato.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define sqr(a) ((a) * (a))

#define VIBRATO_BASE_DELAY_SEC 0.002f /* 2 ms */
#define VIBRATO_FREQUENCY_DEFAULT_HZ 2.0f
#define VIBRATO_FREQUENCY_MAX_HZ 14.0f
#define VIBRATO_DEPTH_DEFAULT_PERCENT 50.0f
#define VIBRATO_ADD_DELAY 3

static float hermite_interp(float x, float *y)
{
   float c0 = y[1];
   float c1 = (1.0f / 2.0f) * (y[2] - y[0]);
   float c2 = (y[0] - (5.0f / 2.0f) * y[1]) + (2.0f * y[2] - (1.0f / 2.0f) * y[3]);
   float c3 = (1.0f / 2.0f) * (y[3] - y[0]) + (3.0f / 2.0f) * (y[1] - y[2]);
   return ((c3 * x + c2) * x + c1) * x + c0;
}

struct vibrato_core
{
   float* buffer;
   float freq;
   float samplerate;
   float depth;
   int phase;
   int writeindex;
   int size;
};

struct vibrato
{
   struct vibrato_core left, right;
};

static void vibrato_free(void *data)
{
   struct vibrato *vib = (struct vibrato*)data;
   free(vib->left.buffer);
   free(vib->right.buffer);
   free(data);
}

static void vibratocore_init(struct vibrato_core *core,float depth,int samplerate,float freq)
{
	core->size       = VIBRATO_BASE_DELAY_SEC * samplerate * 2;
	core->buffer     = (float*)malloc((core->size + VIBRATO_ADD_DELAY) * sizeof(float));
	memset(core->buffer, 0, (core->size   + VIBRATO_ADD_DELAY) * sizeof(float));
	core->samplerate = samplerate;
	core->freq       = freq;
	core->depth      = depth;
	core->phase      = 0;
	core->writeindex = 0;
}

float vibratocore_core(struct vibrato_core *core,float in)
{
   int ipart;
   float delay, readindex, fpart, value;
   float M                        = core->freq / core->samplerate;
   int maxphase                   = core->samplerate / core->freq;
   float lfo                      = sin(M * 2. * M_PI * core->phase++);
   int maxdelay                   = VIBRATO_BASE_DELAY_SEC * core->samplerate;
   core->phase                    = core->phase % maxphase;
   lfo                            = (lfo + 1) * 1.; /* Transform from [-1; 1] to [0; 1] */
   delay                          =  lfo * core->depth * maxdelay;
   delay                         += VIBRATO_ADD_DELAY;
   readindex                      = core->writeindex - 1 - delay;
   while (readindex < 0)
      readindex                  += core->size;
   while (readindex >= core->size)
      readindex                  -= core->size;
   ipart                          = (int)readindex;    /* Integer part of the delay */
   fpart                          = readindex - ipart; /* fractional part of the delay */
   value                          = hermite_interp(fpart, &(core->buffer[ipart]));
   core->buffer[core->writeindex] = in;
   if (core->writeindex < VIBRATO_ADD_DELAY)
      core->buffer[core->size + core->writeindex] = in;
   core->writeindex++;
   if (core->writeindex == core->size)
      core->writeindex = 0;
   return value;
}

static void vibrato_process(void *data,
      struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   float *out;
   struct vibrato *vib = (struct vibrato*)data;

   output->samples     = input->samples;
   output->frames      = input->frames;
   out                 = output->samples;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float in[2] = { out[0], out[1] };
      out[0]      = vibratocore_core(&vib->left, in[0]);
      out[1]      = vibratocore_core(&vib->right, in[1]);
   }
}

static void *vibrato_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   float freq, depth;
   struct vibrato *vib = (struct vibrato*)calloc(1, sizeof(*vib));
   if (!vib)
      return NULL;

   config->get_float(userdata, "freq", &freq,5.0f);
   config->get_float(userdata, "depth", &depth, 0.5f);
   vibratocore_init(&vib->left,depth,info->input_rate,freq);
   vibratocore_init(&vib->right,depth,info->input_rate,freq);
   return vib;
}

static const struct dspfilter_implementation vibrato_plug = {
   vibrato_init,
   vibrato_process,
   vibrato_free,

   DSPFILTER_API_VERSION,
   "Vibrato",
   "vibrato",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation vibrato_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   return &vibrato_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/dsp_filters/wahwah.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (wahwah.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>

#define WAHWAH_LFO_SKIP_SAMPLES 30

struct wahwah_data
{
   float phase;
   float lfoskip;
   float b0, b1, b2, a0, a1, a2;
   float freq, startphase;
   float depth, freqofs, res;
   unsigned long skipcount;

   struct
   {
      float xn1, xn2, yn1, yn2;
   } l, r;
};

static void wahwah_free(void *data)
{
   if (data)
      free(data);
}

static void wahwah_process(void *data, struct dspfilter_output *output,
      const struct dspfilter_input *input)
{
   unsigned i;
   struct wahwah_data *wah = (struct wahwah_data*)data;
   float *out              = output->samples;

   output->samples         = input->samples;
   output->frames          = input->frames;

   for (i = 0; i < input->frames; i++, out += 2)
   {
      float out_l, out_r;
      float in[2] = { out[0], out[1] };

      if ((wah->skipcount++ % WAHWAH_LFO_SKIP_SAMPLES) == 0)
      {
         float omega, sn, cs, alpha;
         float frequency = (1.0f + cos(wah->skipcount * wah->lfoskip + wah->phase)) / 2.0f;

         frequency       = frequency * wah->depth * (1.0f - wah->freqofs) + wah->freqofs;
         frequency       = exp((frequency - 1.0f) * 6.0f);

         omega           = M_PI * frequency;
         sn              = sin(omega);
         cs              = cos(omega);
         alpha           = sn / (2.0f * wah->res);

         wah->b0         = (1.0f - cs) / 2.0f;
         wah->b1         = 1.0f  - cs;
         wah->b2         = (1.0f - cs) / 2.0f;
         wah->a0         = 1.0f + alpha;
         wah->a1         = -2.0f * cs;
         wah->a2         = 1.0f - alpha;
      }

      out_l              = (wah->b0 * in[0] + wah->b1 * wah->l.xn1 + wah->b2 * wah->l.xn2 - wah->a1 * wah->l.yn1 - wah->a2 * wah->l.yn2) / wah->a0;
      out_r              = (wah->b0 * in[1] + wah->b1 * wah->r.xn1 + wah->b2 * wah->r.xn2 - wah->a1 * wah->r.yn1 - wah->a2 * wah->r.yn2) / wah->a0;

      wah->l.xn2         = wah->l.xn1;
      wah->l.xn1         = in[0];
      wah->l.yn2         = wah->l.yn1;
      wah->l.yn1         = out_l;

      wah->r.xn2         = wah->r.xn1;
      wah->r.xn1         = in[1];
      wah->r.yn2         = wah->r.yn1;
      wah->r.yn1         = out_r;

      out[0]             = out_l;
      out[1]             = out_r;
   }
}

static void *wahwah_init(const struct dspfilter_info *info,
      const struct dspfilter_config *config, void *userdata)
{
   struct wahwah_data *wah = (struct wahwah_data*)calloc(1, sizeof(*wah));
   if (!wah)
      return NULL;

   config->get_float(userdata, "lfo_freq", &wah->freq, 1.5f);
   config->get_float(userdata, "lfo_start_phase", &wah->startphase, 0.0f);
   config->get_float(userdata, "freq_offset", &wah->freqofs, 0.3f);
   config->get_float(userdata, "depth", &wah->depth, 0.7f);
   config->get_float(userdata, "resonance", &wah->res, 2.5f);

   wah->lfoskip = wah->freq * 2.0f * M_PI / info->input_rate;
   wah->phase   = wah->startphase * M_PI / 180.0f;

   return wah;
}

static const struct dspfilter_implementation wahwah_plug = {
   wahwah_init,
   wahwah_process,
   wahwah_free,

   DSPFILTER_API_VERSION,
   "Wah-Wah",
   "wahwah",
};

#ifdef HAVE_FILTERS_BUILTIN
#define dspfilter_get_implementation wahwah_dspfilter_get_implementation
#endif

const struct dspfilter_implementation *
dspfilter_get_implementation(dspfilter_simd_mask_t mask)
{
   (void)mask;
   return &wahwah_plug;
}

#undef dspfilter_get_implementation


================================================
FILE: audio/resampler/audio_resampler.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (audio_resampler.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <string.h>

#include <string/stdstring.h>
#include <features/features_cpu.h>
#include <file/config_file_userdata.h>

#include <audio/audio_resampler.h>

static void resampler_null_process(void *a, struct resampler_data *b) { }
static void resampler_null_free(void *a) { }
static void *resampler_null_init(const struct resampler_config *a, double b,
      enum resampler_quality c, resampler_simd_mask_t d) { return (void*)0; }

retro_resampler_t null_resampler = {
   resampler_null_init,
   resampler_null_process,
   resampler_null_free,
   RESAMPLER_API_VERSION,
   "null",
   "null"
};

static const retro_resampler_t *resampler_drivers[] = {
   &sinc_resampler,
#ifdef HAVE_CC_RESAMPLER
   &CC_resampler,
#endif
#ifdef HAVE_NEAREST_RESAMPLER
   &nearest_resampler,
#endif
   &null_resampler,
   NULL,
};

static const struct resampler_config resampler_config = {
   config_userdata_get_float,
   config_userdata_get_int,
   config_userdata_get_float_array,
   config_userdata_get_int_array,
   config_userdata_get_string,
   config_userdata_free,
};

/**
 * find_resampler_driver_index:
 * @ident                      : Identifier of resampler driver to find.
 *
 * Finds resampler driver index by @ident name.
 *
 * Returns: resampler driver index if resampler driver was found, otherwise
 * -1.
 **/
static int find_resampler_driver_index(const char *ident)
{
   unsigned i;

   for (i = 0; resampler_drivers[i]; i++)
      if (string_is_equal_noncase(ident, resampler_drivers[i]->ident))
         return i;
   return -1;
}

/**
 * find_resampler_driver:
 * @ident                      : Identifier of resampler driver to find.
 *
 * Finds resampler by @ident name.
 *
 * Returns: resampler driver if resampler driver was found, otherwise
 * NULL.
 **/
static const retro_resampler_t *find_resampler_driver(const char *ident)
{
   int i = find_resampler_driver_index(ident);

   if (i >= 0)
      return resampler_drivers[i];

   return resampler_drivers[0];
}

/**
 * resampler_append_plugs:
 * @re                         : Resampler handle
 * @backend                    : Resampler backend that is about to be set.
 * @bw_ratio                   : Bandwidth ratio.
 *
 * Initializes resampler driver based on queried CPU features.
 *
 * Returns: true (1) if successfully initialized, otherwise false (0).
 **/
static bool resampler_append_plugs(void **re,
      const retro_resampler_t **backend,
      enum resampler_quality quality,
      double bw_ratio)
{
   resampler_simd_mask_t mask = (resampler_simd_mask_t)cpu_features_get();

   if (*backend)
      *re = (*backend)->init(&resampler_config, bw_ratio, quality, mask);

   if (!*re)
      return false;
   return true;
}


/**
 * audio_resampler_driver_find_handle:
 * @idx                : index of driver to get handle to.
 *
 * Returns: handle to audio resampler driver at index. Can be NULL
 * if nothing found.
 **/
const void *audio_resampler_driver_find_handle(int idx)
{
   const void *drv = resampler_drivers[idx];
   if (!drv)
      return NULL;
   return drv;
}

/**
 * audio_resampler_driver_find_ident:
 * @idx                : index of driver to get handle to.
 *
 * Returns: Human-readable identifier of audio resampler driver at index.
 * Can be NULL if nothing found.
 **/
const char *audio_resampler_driver_find_ident(int idx)
{
   const retro_resampler_t *drv = resampler_drivers[idx];
   if (!drv)
      return NULL;
   return drv->ident;
}

/**
 * retro_resampler_realloc:
 * @re                         : Resampler handle
 * @backend                    : Resampler backend that is about to be set.
 * @ident                      : Identifier name for resampler we want.
 * @bw_ratio                   : Bandwidth ratio.
 *
 * Reallocates resampler. Will free previous handle before
 * allocating a new one. If ident is NULL, first resampler will be used.
 *
 * Returns: true (1) if successful, otherwise false (0).
 **/
bool retro_resampler_realloc(void **re, const retro_resampler_t **backend,
      const char *ident, enum resampler_quality quality, double bw_ratio)
{
   if (*re && *backend)
      (*backend)->free(*re);

   *re      = NULL;
   *backend = find_resampler_driver(ident);

   if (!resampler_append_plugs(re, backend, quality, bw_ratio))
   {
      if (!*re)
         *backend = NULL;
      return false;
   }

   return true;
}


================================================
FILE: audio/resampler/drivers/nearest_resampler.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (nearest_resampler.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <stdint.h>
#include <stdlib.h>
#include <math.h>

#include <audio/audio_resampler.h>

typedef struct rarch_nearest_resampler
{
   float fraction;
} rarch_nearest_resampler_t;

static void resampler_nearest_process(
      void *re_, struct resampler_data *data)
{
   rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
   audio_frame_float_t  *inp     = (audio_frame_float_t*)data->data_in;
   audio_frame_float_t  *inp_max = (audio_frame_float_t*)inp + data->input_frames;
   audio_frame_float_t  *outp    = (audio_frame_float_t*)data->data_out;
   float                   ratio = 1.0 / data->ratio;

   while (inp != inp_max)
   {
      while (re->fraction > 1)
      {
         *outp++       = *inp;
         re->fraction -= ratio;
      }
      re->fraction++;
      inp++;
   }

   data->output_frames = (outp - (audio_frame_float_t*)data->data_out);
}

static void resampler_nearest_free(void *re_)
{
   rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)re_;
   if (re)
      free(re);
}

static void *resampler_nearest_init(const struct resampler_config *config,
      double bandwidth_mod,
      enum resampler_quality quality,
      resampler_simd_mask_t mask)
{
   rarch_nearest_resampler_t *re = (rarch_nearest_resampler_t*)
      calloc(1, sizeof(rarch_nearest_resampler_t));
   if (!re)
      return NULL;
   re->fraction = 0;
   return re;
}

retro_resampler_t nearest_resampler = {
   resampler_nearest_init,
   resampler_nearest_process,
   resampler_nearest_free,
   RESAMPLER_API_VERSION,
   "nearest",
   "nearest"
};


================================================
FILE: audio/resampler/drivers/sinc_resampler.c
================================================
/* Copyright  (C) 2010-2020 The RetroArch team
 *
 * ---------------------------------------------------------------------------------------
 * The following license statement only applies to this file (sinc_resampler.c).
 * ---------------------------------------------------------------------------------------
 *
 * Permission is hereby granted, free of charge,
 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* Bog-standard windowed SINC implementation. */

#if defined(__GNUC__) && defined(__OPTIMIZE__) && !defined(__clang__)
#pragma GCC push_options
#pragma GCC optimize ("fast-math")
#endif

#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include <retro_environment.h>
#include <retro_inline.h>
#include <filters.h>
#include <memalign.h>

#include <audio/audio_resampler.h>

#ifdef __SSE__
#include <xmmintrin.h>
#endif

#if defined(__AVX__)
#include <immintrin.h>
#endif

/* Rough SNR values for upsampling:
 * LOWEST: 40 dB
 * LOWER: 55 dB
 * NORMAL: 70 dB
 * HIGHER: 110 dB
 * HIGHEST: 140 dB
 */

/* TODO, make all this more configurable. */

enum sinc_window
{
   SINC_WINDOW_NONE   = 0,
   SINC_WINDOW_KAISER,
   SINC_WINDOW_LANCZOS
};

/* For the little amount of taps we're using,
 * SSE1 is faster than AVX for some reason.
 * AVX code is kept here though as by increasing number
 * of sinc taps, the AVX code is clearly faster than SSE1.
 */

typedef struct rarch_sinc_resampler
{
   /* A buffer for phase_table, buffer_l and buffer_r
    * are created in a single calloc().
    * Ensure that we get as good cache locality as we can hope for. */
   float *main_buffer;
   float *phase_table;
   float *buffer_l;
   float *buffer_r;
   unsigned phase_bits;
   unsigned subphase_bits;
   unsigned subphase_mask;
   unsigned taps;
   unsigned ptr;
   uint32_t time;
   float subphase_mod;
   float kaiser_beta;
} rarch_sinc_resampler_t;

/*
 * Macro to handle the common "push input samples into ring buffer"
 * logic shared by every process function. Reduces code duplication and
 * ensures the input-feeding logic stays consistent across all paths.
 */
#define SINC_PUSH_INPUT_SAMPLES(resamp, input, taps, phases, frames) \
   do {                                                              \
      while ((frames) && (resamp)->time >= (phases))                 \
      {                                                              \
         if (!(resamp)->ptr)                                         \
            (resamp)->ptr = (taps);                                  \
         (resamp)->ptr--;                                            \
         (resamp)->buffer_l[(resamp)->ptr + (taps)] =                \
            (resamp)->buffer_l[(resamp)->ptr]       = *(input)++;    \
         (resamp)->buffer_r[(resamp)->ptr + (taps)] =                \
            (resamp)->buffer_r[(resamp)->ptr]       = *(input)++;    \
         (resamp)->time -= (phases);                                 \
         (frames)--;                                                 \
      }                                                              \
   } while (0)


#if (defined(__ARM_NEON__) || defined(HAVE_NEON))

#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
void process_sinc_neon_asm(float *out, const float *left,
      const float *right, const float *coeff, unsigned taps);
#else
#include <arm_neon.h>

/* Assumes that taps >= 8, and that taps is a multiple of 8.
 * Not bothering to reimplement this one for the external .S
 */
static void resampler_sinc_process_neon_kaiser(void *re_, struct resampler_data *data)
{
   rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
   unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits);
   uint32_t ratio                 = phases / data->ratio;
   const float *input             = data->data_in;
   float *output                  = data->data_out;
   size_t frames                  = data->input_frames;
   size_t out_frames              = 0;
   unsigned taps                  = resamp->taps;
   unsigned taps2                 = taps * 2;

   while (frames)
   {
      SINC_PUSH_INPUT_SAMPLES(resamp, input, taps, phases, frames);

      {
         const float *buffer_l    = resamp->buffer_l + resamp->ptr;
         const float *buffer_r    = resamp->buffer_r + resamp->ptr;
         while (resamp->time < phases)
         {
            /* C89: all declarations at top of block */
            int i;
            float32x2_t p3, p4;
            unsigned phase           = resamp->time >> resamp->subphase_bits;
            const float *phase_table = resamp->phase_table + phase * taps2;
            const float *delta_table = phase_table + taps;
            float32x4_t delta        = vdupq_n_f32((resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
            float32x4_t p1           = vdupq_n_f32(0.0f);
            float32x4_t p2           = vdupq_n_f32(0.0f);

            for (i = 0; i < (int)taps; i += 8)
            {
               float32x4x2_t coeff8  = vld2q_f32(&phase_table[i]);
               float32x4x2_t delta8  = vld2q_f32(&delta_table[i]);
               float32x4x2_t left8   = vld2q_f32(&buffer_l[i]);
               float32x4x2_t right8  = vld2q_f32(&buffer_r[i]);

               coeff8.val[0] = vmlaq_f32(coeff8.val[0], delta8.val[0], delta);
               coeff8.val[1] = vmlaq_f32(coeff8.val[1], delta8.val[1], delta);

               p1 = vmlaq_f32(p1,  left8.val[0], coeff8.val[0]);
               p2 = vmlaq_f32(p2, right8.val[0], coeff8.val[0]);
               p1 = vmlaq_f32(p1,  left8.val[1], coeff8.val[1]);
               p2 = vmlaq_f32(p2, right8.val[1], coeff8.val[1]);
            }

            p3 = vadd_f32(vget_low_f32(p1), vget_high_f32(p1));
            p4 = vadd_f32(vget_low_f32(p2), vget_high_f32(p2));
            vst1_f32(output, vpadd_f32(p3, p4));
            output                 += 2;
            out_frames++;
            resamp->time           += ratio;
         }
      }
   }

   data->output_frames = out_frames;
}
#endif

/* Assumes that taps >= 8, and that taps is a multiple of 8. */
static void resampler_sinc_process_neon(void *re_, struct resampler_data *data)
{
   rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
   unsigned phases                = 1 << (resamp->phase_bits + resamp->subphase_bits);
   uint32_t ratio                 = phases / data->ratio;
   const float *input             = data->data_in;
   float *output                  = data->data_out;
   size_t frames                  = data->input_frames;
   size_t out_frames              = 0;
   unsigned taps                  = resamp->taps;

   while (frames)
   {
      SINC_PUSH_INPUT_SAMPLES(resamp, input, taps, phases, frames);

      {
         const float *buffer_l    = resamp->buffer_l + resamp->ptr;
         const float *buffer_r    = resamp->buffer_r + resamp->ptr;
         while (resamp->time < phases)
         {
            unsigned phase           = resamp->time >> resamp->subphase_bits;
            const float *phase_table = resamp->phase_table + phase * taps;
#ifdef HAVE_ARM_NEON_ASM_OPTIMIZATIONS
            process_sinc_neon_asm(output, buffer_l, buffer_r, phase_table, taps);
#else
            {
               /* C89: declarations at top of block */
               int i;
               float32x2_t p3, p4;
               float32x4_t p1 = vdupq_n_f32(0.0f);
               float32x4_t p2 = vdupq_n_f32(0.0f);

               for (i = 0; i < (int)taps; i += 8)
               {
                  float32x4x2_t coeff8  = vld2q_f32(&phase_table[i]);
                  float32x4x2_t left8   = vld2q_f32(&buffer_l[i]);
                  float32x4x2_t right8  = vld2q_f32(&buffer_r[i]);

                  p1 = vmlaq_f32(p1,  left8.val[0], coeff8.val[0]);
                  p2 = vmlaq_f32(p2, right8.val[0], coeff8.val[0]);
                  p1 = vmlaq_f32(p1,  left8.val[1], coeff8.val[1]);
                  p2 = vmlaq_f32(p2, right8.val[1], coeff8.val[1]);
               }

               p3 = vadd_f32(vget_low_f32(p1), vget_high_f32(p1));
               p4 = vadd_f32(vget_low_f32(p2), vget_hig
Download .txt
gitextract_6fnfu5rl/

├── .gitignore
├── Makefile.test
├── audio/
│   ├── audio_mix.c
│   ├── audio_mixer.c
│   ├── conversion/
│   │   ├── float_to_s16.c
│   │   ├── float_to_s16_neon.S
│   │   ├── float_to_s16_neon.c
│   │   ├── mono_to_stereo_float.c
│   │   ├── s16_to_float.c
│   │   ├── s16_to_float_neon.S
│   │   ├── s16_to_float_neon.c
│   │   └── stereo_to_mono_float.c
│   ├── dsp_filter.c
│   ├── dsp_filters/
│   │   ├── BassBoost.dsp
│   │   ├── ChipTune-Lowpass.dsp
│   │   ├── ChipTuneEnhance.dsp
│   │   ├── Chorus.dsp
│   │   ├── Crystalizer.dsp
│   │   ├── EQ.dsp
│   │   ├── Echo.dsp
│   │   ├── EchoReverb.dsp
│   │   ├── HighShelfDampen.dsp
│   │   ├── IIR.dsp
│   │   ├── LowPassCPS.dsp
│   │   ├── Makefile
│   │   ├── Mono.dsp
│   │   ├── Panning.dsp
│   │   ├── Phaser.dsp
│   │   ├── Reverb.dsp
│   │   ├── Tremolo.dsp
│   │   ├── Vibrato.dsp
│   │   ├── WahWah.dsp
│   │   ├── chorus.c
│   │   ├── configure
│   │   ├── crystalizer.c
│   │   ├── echo.c
│   │   ├── eq.c
│   │   ├── fft/
│   │   │   ├── fft.c
│   │   │   └── fft.h
│   │   ├── iir.c
│   │   ├── link.T
│   │   ├── panning.c
│   │   ├── phaser.c
│   │   ├── reverb.c
│   │   ├── tremolo.c
│   │   ├── vibrato.c
│   │   └── wahwah.c
│   └── resampler/
│       ├── audio_resampler.c
│       └── drivers/
│           ├── nearest_resampler.c
│           ├── sinc_resampler.c
│           └── sinc_resampler_neon.S
├── cdrom/
│   └── cdrom.c
├── compat/
│   ├── compat_fnmatch.c
│   ├── compat_getopt.c
│   ├── compat_ifaddrs.c
│   ├── compat_posix_string.c
│   ├── compat_snprintf.c
│   ├── compat_strcasestr.c
│   ├── compat_strl.c
│   ├── compat_strldup.c
│   ├── compat_vscprintf.c
│   └── fopen_utf8.c
├── crt/
│   ├── include/
│   │   └── string.h
│   └── string.c
├── dynamic/
│   └── dylib.c
├── encodings/
│   ├── encoding_base64.c
│   ├── encoding_crc32.c
│   └── encoding_utf.c
├── features/
│   └── features_cpu.c
├── file/
│   ├── archive_file.c
│   ├── archive_file_7z.c
│   ├── archive_file_zlib.c
│   ├── archive_file_zstd.c
│   ├── config_file.c
│   ├── config_file_userdata.c
│   ├── file_path.c
│   ├── file_path_io.c
│   ├── nbio/
│   │   ├── nbio_intf.c
│   │   ├── nbio_linux.c
│   │   ├── nbio_stdio.c
│   │   ├── nbio_unixmmap.c
│   │   └── nbio_windowsmmap.c
│   └── retro_dirent.c
├── formats/
│   ├── bmp/
│   │   ├── rbmp.c
│   │   └── rbmp_encode.c
│   ├── cdfs/
│   │   └── cdfs.c
│   ├── image_texture.c
│   ├── image_transfer.c
│   ├── jpeg/
│   │   └── rjpeg.c
│   ├── json/
│   │   └── rjson.c
│   ├── libchdr/
│   │   ├── libchdr_bitstream.c
│   │   ├── libchdr_cdrom.c
│   │   ├── libchdr_chd.c
│   │   ├── libchdr_flac.c
│   │   ├── libchdr_flac_codec.c
│   │   ├── libchdr_huffman.c
│   │   ├── libchdr_lzma.c
│   │   ├── libchdr_zlib.c
│   │   └── libchdr_zstd.c
│   ├── logiqx_dat/
│   │   └── logiqx_dat.c
│   ├── m3u/
│   │   └── m3u_file.c
│   ├── png/
│   │   ├── rpng.c
│   │   ├── rpng_encode.c
│   │   └── rpng_internal.h
│   ├── tga/
│   │   └── rtga.c
│   ├── wav/
│   │   └── rwav.c
│   ├── webp/
│   │   └── rwebp.c
│   └── xml/
│       └── rxml.c
├── gfx/
│   ├── gl_capabilities.c
│   └── scaler/
│       ├── pixconv.c
│       ├── scaler.c
│       ├── scaler_filter.c
│       └── scaler_int.c
├── glsym/
│   ├── README.md
│   ├── glgen.py
│   ├── glsym_es2.c
│   ├── glsym_es3.c
│   ├── glsym_gl.c
│   ├── rglgen.c
│   ├── rglgen.py
│   └── xglgen.py
├── hash/
│   └── lrc_hash.c
├── include/
│   ├── array/
│   │   ├── rbuf.h
│   │   └── rhmap.h
│   ├── audio/
│   │   ├── audio_mix.h
│   │   ├── audio_mixer.h
│   │   ├── audio_resampler.h
│   │   ├── conversion/
│   │   │   ├── dual_mono.h
│   │   │   ├── float_to_s16.h
│   │   │   └── s16_to_float.h
│   │   └── dsp_filter.h
│   ├── boolean.h
│   ├── cdrom/
│   │   └── cdrom.h
│   ├── clamping.h
│   ├── compat/
│   │   ├── apple_compat.h
│   │   ├── fnmatch.h
│   │   ├── fopen_utf8.h
│   │   ├── getopt.h
│   │   ├── ifaddrs.h
│   │   ├── intrinsics.h
│   │   ├── msvc/
│   │   │   └── stdint.h
│   │   ├── msvc.h
│   │   ├── posix_string.h
│   │   ├── strcasestr.h
│   │   ├── strl.h
│   │   └── zlib/
│   │       ├── zconf.h
│   │       └── zlib.h
│   ├── defines/
│   │   ├── cocoa_defines.h
│   │   ├── d3d_defines.h
│   │   ├── gx_defines.h
│   │   ├── ps3_defines.h
│   │   ├── ps4_defines.h
│   │   └── psp_defines.h
│   ├── dynamic/
│   │   └── dylib.h
│   ├── encodings/
│   │   ├── base64.h
│   │   ├── crc32.h
│   │   ├── utf.h
│   │   └── win32.h
│   ├── fastcpy.h
│   ├── features/
│   │   └── features_cpu.h
│   ├── file/
│   │   ├── archive_file.h
│   │   ├── config_file.h
│   │   ├── config_file_userdata.h
│   │   ├── file_path.h
│   │   └── nbio.h
│   ├── filters.h
│   ├── formats/
│   │   ├── cdfs.h
│   │   ├── image.h
│   │   ├── logiqx_dat.h
│   │   ├── m3u_file.h
│   │   ├── rbmp.h
│   │   ├── rjpeg.h
│   │   ├── rjson.h
│   │   ├── rjson_helpers.h
│   │   ├── rpng.h
│   │   ├── rtga.h
│   │   ├── rwav.h
│   │   ├── rwebp.h
│   │   └── rxml.h
│   ├── gfx/
│   │   ├── gl_capabilities.h
│   │   ├── math/
│   │   │   ├── matrix_3x3.h
│   │   │   ├── matrix_4x4.h
│   │   │   ├── vector_2.h
│   │   │   ├── vector_3.h
│   │   │   └── vector_4.h
│   │   ├── scaler/
│   │   │   ├── filter.h
│   │   │   ├── pixconv.h
│   │   │   ├── scaler.h
│   │   │   └── scaler_int.h
│   │   └── video_frame.h
│   ├── glsym/
│   │   ├── glsym.h
│   │   ├── glsym_es2.h
│   │   ├── glsym_es3.h
│   │   ├── glsym_gl.h
│   │   ├── rglgen.h
│   │   ├── rglgen_headers.h
│   │   ├── rglgen_private_headers.h
│   │   └── switch/
│   │       ├── nx_gl.h
│   │       └── nx_glsym.h
│   ├── libchdr/
│   │   ├── bitstream.h
│   │   ├── cdrom.h
│   │   ├── chd.h
│   │   ├── chdconfig.h
│   │   ├── coretypes.h
│   │   ├── flac.h
│   │   ├── huffman.h
│   │   ├── libchdr_zlib.h
│   │   ├── libchdr_zstd.h
│   │   ├── lzma.h
│   │   └── minmax.h
│   ├── libco.h
│   ├── libretro.h
│   ├── libretro_d3d.h
│   ├── libretro_d3d11.h
│   ├── libretro_d3d12.h
│   ├── libretro_dspfilter.h
│   ├── libretro_gskit_ps2.h
│   ├── libretro_vulkan.h
│   ├── lists/
│   │   ├── dir_list.h
│   │   ├── file_list.h
│   │   ├── linked_list.h
│   │   ├── nested_list.h
│   │   └── string_list.h
│   ├── lrc_hash.h
│   ├── math/
│   │   ├── complex.h
│   │   ├── float_minmax.h
│   │   └── fxp.h
│   ├── media/
│   │   └── media_detect_cd.h
│   ├── memalign.h
│   ├── memmap.h
│   ├── net/
│   │   ├── net_compat.h
│   │   ├── net_http.h
│   │   ├── net_http_parse.h
│   │   ├── net_ifinfo.h
│   │   ├── net_socket.h
│   │   └── net_socket_ssl.h
│   ├── playlists/
│   │   └── label_sanitization.h
│   ├── queues/
│   │   ├── fifo_queue.h
│   │   ├── generic_queue.h
│   │   ├── message_queue.h
│   │   └── task_queue.h
│   ├── retro_assert.h
│   ├── retro_atomic.h
│   ├── retro_common.h
│   ├── retro_common_api.h
│   ├── retro_dirent.h
│   ├── retro_endianness.h
│   ├── retro_environment.h
│   ├── retro_inline.h
│   ├── retro_math.h
│   ├── retro_miscellaneous.h
│   ├── retro_spsc.h
│   ├── retro_timers.h
│   ├── rthreads/
│   │   ├── async_job.h
│   │   ├── rthreads.h
│   │   └── tpool.h
│   ├── streams/
│   │   ├── chd_stream.h
│   │   ├── file_stream.h
│   │   ├── file_stream_transforms.h
│   │   ├── interface_stream.h
│   │   ├── memory_stream.h
│   │   ├── network_stream.h
│   │   ├── rzip_stream.h
│   │   ├── stdin_stream.h
│   │   └── trans_stream.h
│   ├── string/
│   │   └── stdstring.h
│   ├── time/
│   │   └── rtime.h
│   ├── utils/
│   │   └── md5.h
│   ├── vfs/
│   │   ├── vfs.h
│   │   ├── vfs_implementation.h
│   │   ├── vfs_implementation_cdrom.h
│   │   └── vfs_implementation_saf.h
│   └── vulkan/
│       └── vulkan_symbol_wrapper.h
├── libco/
│   ├── aarch64.c
│   ├── amd64.c
│   ├── armeabi.c
│   ├── fiber.c
│   ├── genode.cpp
│   ├── libco.c
│   ├── ppc.c
│   ├── ps2.c
│   ├── ps3.S
│   ├── psp1.c
│   ├── psp2.c
│   ├── scefiber.c
│   ├── sjlj.c
│   ├── ucontext.c
│   └── x86.c
├── lists/
│   ├── dir_list.c
│   ├── file_list.c
│   ├── linked_list.c
│   ├── nested_list.c
│   ├── string_list.c
│   └── vector_list.c
├── media/
│   └── media_detect_cd.c
├── memmap/
│   ├── memalign.c
│   └── memmap.c
├── net/
│   ├── cacert.h
│   ├── net_compat.c
│   ├── net_http.c
│   ├── net_http_parse.c
│   ├── net_ifinfo.c
│   ├── net_socket.c
│   ├── net_socket_ssl_bear.c
│   └── net_socket_ssl_mbed.c
├── playlists/
│   └── label_sanitization.c
├── queues/
│   ├── fifo_queue.c
│   ├── generic_queue.c
│   ├── message_queue.c
│   ├── retro_spsc.c
│   └── task_queue.c
├── rthreads/
│   ├── ctr_pthread.h
│   ├── gx_pthread.h
│   ├── psp_pthread.h
│   ├── rthreads.c
│   ├── tpool.c
│   └── xenon_sdl_threads.c
├── samples/
│   ├── atomic/
│   │   ├── retro_atomic_extern_c_linkage/
│   │   │   ├── Makefile
│   │   │   └── retro_atomic_extern_c_linkage_test.cpp
│   │   └── retro_atomic_test/
│   │       ├── Makefile
│   │       └── retro_atomic_test.c
│   ├── compat/
│   │   ├── fnmatch/
│   │   │   ├── Makefile
│   │   │   └── compat_fnmatch_test.c
│   │   └── snprintf/
│   │       ├── Makefile
│   │       └── snprintf_test.c
│   ├── core_options/
│   │   ├── README.md
│   │   ├── example_categories/
│   │   │   ├── conversion_scripts/
│   │   │   │   ├── core_option_regex.py
│   │   │   │   └── v1_to_v2_converter.py
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   ├── example_default/
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   ├── example_hide_option/
│   │   │   ├── libretro_core_options.h
│   │   │   └── libretro_core_options_intl.h
│   │   └── example_translation/
│   │       ├── .github/
│   │       │   └── workflows/
│   │       │       ├── crowdin_initial_setup.yml
│   │       │       ├── crowdin_source_upload.yml
│   │       │       └── crowdin_translation_sync.yml
│   │       ├── instructions.txt
│   │       ├── intl/
│   │       │   ├── .gitignore
│   │       │   ├── activate.py
│   │       │   ├── core_option_regex.py
│   │       │   ├── core_option_translation.py
│   │       │   ├── crowdin.yaml
│   │       │   ├── crowdin_prep.py
│   │       │   ├── crowdin_source_upload.py
│   │       │   ├── crowdin_translate.py
│   │       │   ├── crowdin_translation_download.py
│   │       │   ├── download_workflow.py
│   │       │   ├── initial_sync.py
│   │       │   ├── remove_initial_cycle.py
│   │       │   ├── upload_workflow.py
│   │       │   └── v1_to_v2_converter.py
│   │       ├── libretro_core_options.h
│   │       └── libretro_core_options_intl.h
│   ├── encodings/
│   │   └── base64/
│   │       ├── Makefile
│   │       └── unbase64_test.c
│   ├── file/
│   │   ├── archive_file/
│   │   │   ├── Makefile
│   │   │   └── archive_zip_test.c
│   │   ├── archive_zstd/
│   │   │   ├── Makefile
│   │   │   └── archive_zstd_test.c
│   │   ├── config_file/
│   │   │   ├── Makefile
│   │   │   └── config_file_test.c
│   │   ├── file_path/
│   │   │   ├── Makefile
│   │   │   ├── fill_pathname_test.c
│   │   │   └── path_resolve_realpath_test.c
│   │   ├── nbio/
│   │   │   ├── Makefile
│   │   │   └── nbio_test.c
│   │   ├── vfs/
│   │   │   ├── Makefile
│   │   │   └── vfs_read_overflow_test.c
│   │   └── vfs_cdrom/
│   │       ├── Makefile
│   │       └── cdrom_cuesheet_overflow_test.c
│   ├── formats/
│   │   ├── bmp/
│   │   │   ├── Makefile
│   │   │   └── rbmp_test.c
│   │   ├── cdfs/
│   │   │   ├── Makefile
│   │   │   └── cdfs_dir_record_test.c
│   │   ├── json/
│   │   │   ├── Makefile
│   │   │   └── rjson_test.c
│   │   ├── png/
│   │   │   ├── Makefile
│   │   │   ├── rpng_chunk_overflow_test.c
│   │   │   ├── rpng_roundtrip_test.c
│   │   │   └── rpng_test.c
│   │   ├── tga/
│   │   │   ├── Makefile
│   │   │   └── rtga_test.c
│   │   └── xml/
│   │       ├── Makefile
│   │       └── rxml_test.c
│   ├── net/
│   │   ├── Makefile
│   │   ├── net_http_parse_test.c
│   │   ├── net_http_test.c
│   │   ├── net_ifinfo_test.c
│   │   └── udp-test.c
│   ├── queues/
│   │   ├── retro_spsc_test/
│   │   │   ├── Makefile
│   │   │   └── retro_spsc_test.c
│   │   └── task_queue_title_error_test/
│   │       ├── Makefile
│   │       └── task_queue_title_error_test.c
│   ├── rthreads/
│   │   └── tpool_wait_test/
│   │       ├── Makefile
│   │       └── tpool_wait_test.c
│   ├── streams/
│   │   ├── chd/
│   │   │   ├── Makefile
│   │   │   └── chd_meta_overflow_test.c
│   │   └── rzip/
│   │       ├── Makefile
│   │       ├── rzip.c
│   │       └── rzip_chunk_size_test.c
│   └── string/
│       ├── strlcpy_append/
│       │   ├── Makefile
│       │   └── strlcpy_append_test.c
│       └── word_wrap_overflow_test/
│           ├── Makefile
│           └── word_wrap_overflow_test.c
├── streams/
│   ├── chd_stream.c
│   ├── file_stream.c
│   ├── file_stream_transforms.c
│   ├── interface_stream.c
│   ├── memory_stream.c
│   ├── network_stream.c
│   ├── rzip_stream.c
│   ├── stdin_stream.c
│   ├── trans_stream.c
│   ├── trans_stream_pipe.c
│   └── trans_stream_zlib.c
├── string/
│   └── stdstring.c
├── test/
│   ├── formats/
│   │   └── test_rpng.c
│   ├── hash/
│   │   └── test_hash.c
│   ├── lists/
│   │   └── test_linked_list.c
│   ├── queues/
│   │   └── test_generic_queue.c
│   ├── string/
│   │   └── test_stdstring.c
│   └── utils/
│       └── test_utils.c
├── time/
│   └── rtime.c
├── utils/
│   ├── md5.c
│   └── sha1.c
├── vfs/
│   ├── saf/
│   │   └── src/
│   │       └── com/
│   │           └── libretro/
│   │               └── common/
│   │                   └── vfs/
│   │                       └── VfsImplementationSaf.java
│   ├── vfs_implementation.c
│   ├── vfs_implementation_cdrom.c
│   ├── vfs_implementation_saf.c
│   ├── vfs_implementation_smb.c
│   ├── vfs_implementation_smb.h
│   └── vfs_implementation_uwp.cpp
└── vulkan/
    └── vulkan_symbol_wrapper.c
Condensed preview — 434 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,844K chars).
[
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "glsm/\n*.[od]\n*.dll\n*.so\n*.dylib\n*.exe\n"
  },
  {
    "path": "Makefile.test",
    "chars": 2639,
    "preview": "\nOBJDIR = ../obj-unix\n\nTEST_UNIT_CFLAGS = $(CFLAGS) -Iinclude $(LDFLAGS) -lcheck $(LIBCHECK_CFLAGS) -Werror -Wdeclaratio"
  },
  {
    "path": "audio/audio_mix.c",
    "chars": 10759,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/audio_mixer.c",
    "chars": 40100,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/float_to_s16.c",
    "chars": 6397,
    "preview": "/* Copyright  (C) 2010-2021 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/float_to_s16_neon.S",
    "chars": 2297,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/float_to_s16_neon.c",
    "chars": 2598,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/mono_to_stereo_float.c",
    "chars": 1913,
    "preview": "/* Copyright  (C) 2010-2023 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/s16_to_float.c",
    "chars": 6818,
    "preview": "/* Copyright  (C) 2010-2021 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/s16_to_float_neon.S",
    "chars": 2491,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/s16_to_float_neon.c",
    "chars": 2848,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/conversion/stereo_to_mono_float.c",
    "chars": 2000,
    "preview": "/* Copyright  (C) 2010-2023 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filter.c",
    "chars": 9748,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/BassBoost.dsp",
    "chars": 180,
    "preview": "filters = 2\nfilter0 = iir\nfilter1 = panning\n\niir_gain = 10.0\niir_type = BBOOST\niir_frequency = 200.0\n\n# Avoids clipping."
  },
  {
    "path": "audio/dsp_filters/ChipTune-Lowpass.dsp",
    "chars": 100,
    "preview": "filters = 1\nfilter0 = iir\n\niir_frequency = 8600.0\niir_quality = 0.707\niir_gain = 6.0\niir_type = LPF\n"
  },
  {
    "path": "audio/dsp_filters/ChipTuneEnhance.dsp",
    "chars": 493,
    "preview": "filters = 4\nfilter0 = eq\nfilter1 = reverb\nfilter2 = iir\nfilter3 = panning\n\neq_frequencies = \"32 64 125 250 500 1000 2000"
  },
  {
    "path": "audio/dsp_filters/Chorus.dsp",
    "chars": 385,
    "preview": "filters = 1\nfilter0 = chorus\n\n# Controls the base delay of the chorus (milliseconds).\n# chorus_delay_ms = 25.0\n#\n# Contr"
  },
  {
    "path": "audio/dsp_filters/Crystalizer.dsp",
    "chars": 121,
    "preview": "filters = 1\nfilter0 = crystalizer\n# Controls dry/wet-ness of effect. 0.0 = none, 10.0 = max.\ncrystalizer_intensity = 5.0"
  },
  {
    "path": "audio/dsp_filters/EQ.dsp",
    "chars": 1481,
    "preview": "filters = 1\nfilter0 = eq\n\n# Defaults\n\n# Beta factor for Kaiser window.\n# Lower values will allow better frequency resolu"
  },
  {
    "path": "audio/dsp_filters/Echo.dsp",
    "chars": 672,
    "preview": "filters = 1\nfilter0 = echo\n\n# Somewhat fancy Echo filter. Can take any number of echo channels with varying delays (ms) "
  },
  {
    "path": "audio/dsp_filters/EchoReverb.dsp",
    "chars": 194,
    "preview": "filters = 2\nfilter0 = echo\nfilter1 = reverb\n\necho_delay = \"200\"\necho_feedback = \"0.6\"\necho_amp = \"0.25\"\n\nreverb_roomwidt"
  },
  {
    "path": "audio/dsp_filters/HighShelfDampen.dsp",
    "chars": 82,
    "preview": "filters = 1\nfilter0 = iir\n\niir_gain = -12.0\niir_type = HSH\niir_frequency = 8000.0\n"
  },
  {
    "path": "audio/dsp_filters/IIR.dsp",
    "chars": 395,
    "preview": "filters = 1\nfilter0 = iir\n\n# Defaults.\n#iir_frequency = 1024.0\n#iir_quality = 0.707\n#iir_gain = 0.0\n#iir_type = LPF\n\n# F"
  },
  {
    "path": "audio/dsp_filters/LowPassCPS.dsp",
    "chars": 1742,
    "preview": "filters = 1\nfilter0 = eq\n\neq_frequencies = \"8000 10000 12500 16000 20000\"\neq_gains = \"0 -30 -30 -30 -30\"\n\n# Low pass fil"
  },
  {
    "path": "audio/dsp_filters/Makefile",
    "chars": 3496,
    "preview": "compiler    := gcc\nextra_flags :=\nuse_neon    := 0\nbuild       = release\nDYLIB\t      := so\nPREFIX      := /usr\nINSTALLDI"
  },
  {
    "path": "audio/dsp_filters/Mono.dsp",
    "chars": 215,
    "preview": "filters = 1\nfilter0 = panning\n\n# Gains are linear.\n\n# Stereo Mono:\n panning_left_mix = \"0.5 0.5\"\n panning_right_mix = \"0"
  },
  {
    "path": "audio/dsp_filters/Panning.dsp",
    "chars": 444,
    "preview": "filters = 1\nfilter0 = panning\n\n# Gains are linear.\n\n# The default. Left and right channels map to each other.\npanning_le"
  },
  {
    "path": "audio/dsp_filters/Phaser.dsp",
    "chars": 185,
    "preview": "filters = 1\nfilter0 = phaser\n\n# Defaults.\n# phaser_lfo_freq = 0.4\n# phaser_lfo_start_phase = 0.0\n# phaser_feedback = 0.0"
  },
  {
    "path": "audio/dsp_filters/Reverb.dsp",
    "chars": 163,
    "preview": "filters = 1\nfilter0 = reverb\n\n# Defaults.\n# reverb_drytime = 0.43\n# reverb_wettime = 0.4\n# reverb_damping = 0.8\n# reverb"
  },
  {
    "path": "audio/dsp_filters/Tremolo.dsp",
    "chars": 89,
    "preview": "filters = 1\nfilter0 = tremolo\n\n# Defaults.\n#tremolo_frequency = 4.0\n#tremolo_depth = 0.9\n"
  },
  {
    "path": "audio/dsp_filters/Vibrato.dsp",
    "chars": 89,
    "preview": "filters = 1\nfilter0 = vibrato\n\n# Defaults.\n#vibrato_frequency = 5.0\n#vibrato_depth = 0.5\n"
  },
  {
    "path": "audio/dsp_filters/WahWah.dsp",
    "chars": 170,
    "preview": "filters = 1\nfilter0 = wahwah\n\n# Defaults.\n# wahwah_lfo_freq = 1.5\n# wahwah_lfo_start_phase = 0.0\n# wahwah_freq_offset = "
  },
  {
    "path": "audio/dsp_filters/chorus.c",
    "chars": 5324,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/configure",
    "chars": 47,
    "preview": "#!/bin/sh\n\nPACKAGE_NAME=retroarch-filters-audio"
  },
  {
    "path": "audio/dsp_filters/crystalizer.c",
    "chars": 2931,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/echo.c",
    "chars": 5430,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/eq.c",
    "chars": 10129,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/fft/fft.c",
    "chars": 5942,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/fft/fft.h",
    "chars": 1883,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/iir.c",
    "chars": 10946,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/link.T",
    "chars": 57,
    "preview": "{\n  global: dspfilter_get_implementation;\n  local: *;\n};\n"
  },
  {
    "path": "audio/dsp_filters/panning.c",
    "chars": 3596,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/phaser.c",
    "chars": 4403,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/reverb.c",
    "chars": 8641,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/tremolo.c",
    "chars": 4121,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/vibrato.c",
    "chars": 5762,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/dsp_filters/wahwah.c",
    "chars": 4892,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/resampler/audio_resampler.c",
    "chars": 5730,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/resampler/drivers/nearest_resampler.c",
    "chars": 2955,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/resampler/drivers/sinc_resampler.c",
    "chars": 35131,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "audio/resampler/drivers/sinc_resampler_neon.S",
    "chars": 2442,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "cdrom/cdrom.c",
    "chars": 46628,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n*\n* ---------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_fnmatch.c",
    "chars": 4096,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_getopt.c",
    "chars": 5664,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_ifaddrs.c",
    "chars": 18861,
    "preview": "/*\nCopyright (c) 2013, Kenneth MacKay\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or w"
  },
  {
    "path": "compat/compat_posix_string.c",
    "chars": 2702,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_snprintf.c",
    "chars": 2602,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_strcasestr.c",
    "chars": 2101,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_strl.c",
    "chars": 2009,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_strldup.c",
    "chars": 1582,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/compat_vscprintf.c",
    "chars": 1841,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "compat/fopen_utf8.c",
    "chars": 4193,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "crt/include/string.h",
    "chars": 200,
    "preview": "#ifndef __LIBRETRO_SDK_CRT_STRING_H_\n#define __LIBRETRO_SDK_CRT_STRING_H_\n\n#include <stdio.h>\n\nvoid *memcpy(void *dst, c"
  },
  {
    "path": "crt/string.c",
    "chars": 640,
    "preview": "#ifdef _MSC_VER\n#include <cruntime.h>\n#endif\n#include <stdio.h>\n#include <string.h>\n\nvoid *memset(void *dst, int val, si"
  },
  {
    "path": "dynamic/dylib.c",
    "chars": 6635,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "encodings/encoding_base64.c",
    "chars": 5715,
    "preview": "/*\n  https://github.com/superwills/NibbleAndAHalf\n  base64.h -- Fast base64 encoding and decoding.\n  version 1.0.0, Apri"
  },
  {
    "path": "encodings/encoding_crc32.c",
    "chars": 8247,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "encodings/encoding_utf.c",
    "chars": 17310,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "features/features_cpu.c",
    "chars": 27811,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/archive_file.c",
    "chars": 21065,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/archive_file_7z.c",
    "chars": 16558,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/archive_file_zlib.c",
    "chars": 22483,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/archive_file_zstd.c",
    "chars": 10209,
    "preview": "/* Copyright  (C) 2010-2026 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/config_file.c",
    "chars": 39644,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/config_file_userdata.c",
    "chars": 7489,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/file_path.c",
    "chars": 38836,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/file_path_io.c",
    "chars": 4963,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/nbio/nbio_intf.c",
    "chars": 4085,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/nbio/nbio_linux.c",
    "chars": 8973,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/nbio/nbio_stdio.c",
    "chars": 9820,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/nbio/nbio_unixmmap.c",
    "chars": 6882,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/nbio/nbio_windowsmmap.c",
    "chars": 7782,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "file/retro_dirent.c",
    "chars": 4380,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/bmp/rbmp.c",
    "chars": 16841,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/bmp/rbmp_encode.c",
    "chars": 7013,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/cdfs/cdfs.c",
    "chars": 21774,
    "preview": "#include <formats/cdfs.h>\n\n#include <retro_miscellaneous.h>\n#include <compat/strl.h>\n#include <file/file_path.h>\n#includ"
  },
  {
    "path": "formats/image_texture.c",
    "chars": 6141,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/image_transfer.c",
    "chars": 9747,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/jpeg/rjpeg.c",
    "chars": 163756,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/json/rjson.c",
    "chars": 52393,
    "preview": "/* Copyright  (C) 2010-2021 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/libchdr/libchdr_bitstream.c",
    "chars": 3209,
    "preview": "/* license:BSD-3-Clause\n * copyright-holders:Aaron Giles\n***************************************************************"
  },
  {
    "path": "formats/libchdr/libchdr_cdrom.c",
    "chars": 36709,
    "preview": "/* license:BSD-3-Clause\n * copyright-holders:Aaron Giles\n***************************************************************"
  },
  {
    "path": "formats/libchdr/libchdr_chd.c",
    "chars": 71579,
    "preview": "/***************************************************************************\n\n    chd.c\n\n    MAME Compressed Hunks of Da"
  },
  {
    "path": "formats/libchdr/libchdr_flac.c",
    "chars": 11009,
    "preview": "/* license:BSD-3-Clause\n * copyright-holders:Aaron Giles\n***************************************************************"
  },
  {
    "path": "formats/libchdr/libchdr_flac_codec.c",
    "chars": 7003,
    "preview": "/***************************************************************************\n\n    libchdr_flac_codec.c\n\n    MAME Compres"
  },
  {
    "path": "formats/libchdr/libchdr_huffman.c",
    "chars": 16700,
    "preview": "/* license:BSD-3-Clause\n * copyright-holders:Aaron Giles\n***************************************************************"
  },
  {
    "path": "formats/libchdr/libchdr_lzma.c",
    "chars": 11551,
    "preview": "/***************************************************************************\n\n    libchdr_lzma_codec.c\n\n    MAME Compres"
  },
  {
    "path": "formats/libchdr/libchdr_zlib.c",
    "chars": 9628,
    "preview": "/***************************************************************************\n\n    libchdr_zlib.c\n\n    MAME Compressed Hu"
  },
  {
    "path": "formats/libchdr/libchdr_zstd.c",
    "chars": 7042,
    "preview": "/***************************************************************************\n\n    libchdr_zstd.c\n\n    MAME Compressed Hu"
  },
  {
    "path": "formats/logiqx_dat/logiqx_dat.c",
    "chars": 12892,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/m3u/m3u_file.c",
    "chars": 16381,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/png/rpng.c",
    "chars": 62366,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/png/rpng_encode.c",
    "chars": 17705,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/png/rpng_internal.h",
    "chars": 1840,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/tga/rtga.c",
    "chars": 14875,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/wav/rwav.c",
    "chars": 5837,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "formats/webp/rwebp.c",
    "chars": 69013,
    "preview": "/* Copyright  (C) 2010-2024 The RetroArch team\n *\n * Permission is hereby granted, free of charge,\n * to any person obta"
  },
  {
    "path": "formats/xml/rxml.c",
    "chars": 9506,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "gfx/gl_capabilities.c",
    "chars": 10355,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "gfx/scaler/pixconv.c",
    "chars": 39132,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "gfx/scaler/scaler.c",
    "chars": 11230,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "gfx/scaler/scaler_filter.c",
    "chars": 7919,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "gfx/scaler/scaler_int.c",
    "chars": 9313,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "glsym/README.md",
    "chars": 267,
    "preview": "# Autogenerate GL extension loaders\n\n## OpenGL desktop\n\nUse Khronos' recent [header](www.opengl.org/registry/api/glext.h"
  },
  {
    "path": "glsym/glgen.py",
    "chars": 5751,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\n   License statement applies to this file (glgen.py) only.\n\n   Permission is hereby granted,"
  },
  {
    "path": "glsym/glsym_es2.c",
    "chars": 20063,
    "preview": "#include \"glsym/glsym.h\"\n#include <stddef.h>\n#define SYM(x) { \"gl\" #x, &(gl##x) }\nconst struct rglgen_sym_map rglgen_sym"
  },
  {
    "path": "glsym/glsym_es3.c",
    "chars": 20266,
    "preview": "#include \"glsym/glsym.h\"\n#include <stddef.h>\n#define SYM(x) { \"gl\" #x, &(gl##x) }\nconst struct rglgen_sym_map rglgen_sym"
  },
  {
    "path": "glsym/glsym_gl.c",
    "chars": 101408,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "glsym/rglgen.c",
    "chars": 1864,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "glsym/rglgen.py",
    "chars": 5294,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\n   License statement applies to this file (glgen.py) only.\n\n   Permission is hereby granted,"
  },
  {
    "path": "glsym/xglgen.py",
    "chars": 5821,
    "preview": "#!/usr/bin/env python3\n\n\"\"\"\n   License statement applies to this file (xglgen.py) only.\n\n   Permission is hereby granted"
  },
  {
    "path": "hash/lrc_hash.c",
    "chars": 21424,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/array/rbuf.h",
    "chars": 6414,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/array/rhmap.h",
    "chars": 11702,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/audio_mix.h",
    "chars": 3153,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/audio_mixer.h",
    "chars": 3277,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/audio_resampler.h",
    "chars": 7544,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/conversion/dual_mono.h",
    "chars": 3048,
    "preview": "/* Copyright  (C) 2010-2023 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/conversion/float_to_s16.h",
    "chars": 2456,
    "preview": "/* Copyright  (C) 2010-2021 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/conversion/s16_to_float.h",
    "chars": 2568,
    "preview": "/* Copyright  (C) 2010-2021 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/audio/dsp_filter.h",
    "chars": 2083,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/boolean.h",
    "chars": 1723,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/cdrom/cdrom.h",
    "chars": 4657,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/clamping.h",
    "chars": 2217,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/apple_compat.h",
    "chars": 2517,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/fnmatch.h",
    "chars": 1714,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/fopen_utf8.h",
    "chars": 1781,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/getopt.h",
    "chars": 2889,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/ifaddrs.h",
    "chars": 2205,
    "preview": "/*\n * Copyright (c) 1995, 1999\n *\tBerkeley Software Design, Inc.  All rights reserved.\n *\n * Redistribution and use in s"
  },
  {
    "path": "include/compat/intrinsics.h",
    "chars": 3069,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/msvc/stdint.h",
    "chars": 7823,
    "preview": "/* ISO C9x  compliant stdint.h for Microsoft Visual Studio\n * Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG"
  },
  {
    "path": "include/compat/msvc.h",
    "chars": 4028,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/posix_string.h",
    "chars": 3053,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/strcasestr.h",
    "chars": 2164,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/strl.h",
    "chars": 2939,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/compat/zlib/zconf.h",
    "chars": 14954,
    "preview": "/* zconf.h -- configuration of the zlib compression library\n * Copyright (C) 1995-2013 Jean-loup Gailly.\n * For conditio"
  },
  {
    "path": "include/compat/zlib/zlib.h",
    "chars": 86051,
    "preview": "#ifndef _COMPAT_ZLIB_H\n#define _COMPAT_ZLIB_H\n\n/* zlib.h -- interface of the 'zlib' general purpose compression library\n"
  },
  {
    "path": "include/defines/cocoa_defines.h",
    "chars": 5626,
    "preview": "/* Copyright (C) 2010-2021 The RetroArch team\r\n *\r\n * ------------------------------------------------------------------"
  },
  {
    "path": "include/defines/d3d_defines.h",
    "chars": 1790,
    "preview": "/* Copyright (C) 2010-2021 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/defines/gx_defines.h",
    "chars": 3656,
    "preview": "/* Copyright (C) 2010-2021 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/defines/ps3_defines.h",
    "chars": 32530,
    "preview": "/* Copyright (C) 2010-2021 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/defines/ps4_defines.h",
    "chars": 1628,
    "preview": "#ifndef _PS4_DEFINES_H\n#define _PS4_DEFINES_H\n\n#define PS4_MAX_ORBISPADS 16\n#define PS4_MAX_PAD_PORT_TYPES 3\n\n#define\tOR"
  },
  {
    "path": "include/defines/psp_defines.h",
    "chars": 5319,
    "preview": "/* Copyright (C) 2010-2021 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/dynamic/dylib.h",
    "chars": 4129,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/encodings/base64.h",
    "chars": 1136,
    "preview": "#ifndef _LIBRETRO_ENCODINGS_BASE64_H\n#define _LIBRETRO_ENCODINGS_BASE64_H\n\n#include <stdint.h>\n#include <stddef.h>\n\n#inc"
  },
  {
    "path": "include/encodings/crc32.h",
    "chars": 1884,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/encodings/utf.h",
    "chars": 3592,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/encodings/win32.h",
    "chars": 2378,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/fastcpy.h",
    "chars": 2981,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/features/features_cpu.h",
    "chars": 3421,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/file/archive_file.h",
    "chars": 7733,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/file/config_file.h",
    "chars": 9651,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/file/config_file_userdata.h",
    "chars": 2545,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/file/file_path.h",
    "chars": 19318,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/file/nbio.h",
    "chars": 6363,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/filters.h",
    "chars": 2944,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/cdfs.h",
    "chars": 4174,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/image.h",
    "chars": 2959,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/logiqx_dat.h",
    "chars": 3989,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/m3u_file.h",
    "chars": 3876,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rbmp.h",
    "chars": 2266,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rjpeg.h",
    "chars": 2002,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rjson.h",
    "chars": 11920,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rjson_helpers.h",
    "chars": 3390,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rpng.h",
    "chars": 2381,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rtga.h",
    "chars": 1858,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rwav.h",
    "chars": 2842,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rwebp.h",
    "chars": 1886,
    "preview": "/* Copyright  (C) 2010-2024 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/formats/rxml.h",
    "chars": 2575,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/gl_capabilities.h",
    "chars": 2265,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/math/matrix_3x3.h",
    "chars": 9728,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/math/matrix_4x4.h",
    "chars": 15538,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/math/vector_2.h",
    "chars": 3285,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/math/vector_3.h",
    "chars": 2318,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/math/vector_4.h",
    "chars": 2086,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/scaler/filter.h",
    "chars": 1635,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/scaler/pixconv.h",
    "chars": 3961,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/scaler/scaler.h",
    "chars": 3451,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/scaler/scaler_int.h",
    "chars": 1964,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/gfx/video_frame.h",
    "chars": 6807,
    "preview": "/* Copyright  (C) 2010-2020 The RetroArch team\n *\n * -------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/glsym.h",
    "chars": 1765,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/glsym_es2.h",
    "chars": 54826,
    "preview": "#ifndef RGLGEN_DECL_H__\n#define RGLGEN_DECL_H__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#ifdef GL_APIENTRY\ntypedef void ("
  },
  {
    "path": "include/glsym/glsym_es3.h",
    "chars": 55593,
    "preview": "#ifndef RGLGEN_DECL_H__\n#define RGLGEN_DECL_H__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#ifdef GL_APIENTRY\ntypedef void ("
  },
  {
    "path": "include/glsym/glsym_gl.h",
    "chars": 236760,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/rglgen.h",
    "chars": 1865,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/rglgen_headers.h",
    "chars": 1898,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/rglgen_private_headers.h",
    "chars": 2627,
    "preview": "/* Copyright (C) 2010-2020 The RetroArch team\r\n *\r\n * ------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/switch/nx_gl.h",
    "chars": 45598,
    "preview": "/* Copyright (C) 2018-2020 The RetroArch team\n *\n * --------------------------------------------------------------------"
  },
  {
    "path": "include/glsym/switch/nx_glsym.h",
    "chars": 60673,
    "preview": "#ifndef __NX_GLSYM_H__\n#define __NX_GLSYM_H__\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void *GLeglImageOES;\ntype"
  },
  {
    "path": "include/libchdr/bitstream.h",
    "chars": 1407,
    "preview": "/* license:BSD-3-Clause\n * copyright-holders:Aaron Giles\n***************************************************************"
  }
]

// ... and 234 more files (download for full content)

About this extraction

This page contains the full source code of the libretro/libretro-common GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 434 files (4.4 MB), approximately 1.2M tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!